package Web::Dialog;

use strict;
use warnings;

use constant {
	RENDER_SUB => 'render',
};

sub new {
	my ($class, $controller, $app, $package, @params) = @_;
	my $self = bless {
		controller => $controller,
		app => $app,
		package => $package,
		params => [@params],
		content => [],
	}, $class;
	
	return $self;
}

sub controller { $_[0]->{controller} }
sub package { $_[0]->{package} }
sub params { wantarray ? @{$_[0]->{params}} : $_[0]->{params} }
sub app { $_[0]->{app} }
sub cgi { $_[0]->app->cgi }
sub db { $_[0]->app->db }

sub call {
	my ($self, $function, $params) = @_;
	my $name = $self->package;
	
	# Black Magic: Get a reference to the function in the package via direct
	# access to the symbol table and call it; could call the function name on 
	# the package, but that would leave the package name as first parameter,
	# complicating the interface.
	no strict 'refs';
	my $coderef = *{"$name\::"}->{$function};
	
	die 'Unknown page'
		unless $coderef;

	return $coderef->($self->controller, @$params);
}

sub show {
	my ($self) = @_;
	my $method = $self->cgi->request_method || 'GET';

	$self->controller->set_params({$self->cgi->Vars});
	
	if ($method eq 'POST') {
		my $action = $self->cgi->param('action');
		
		# Action needs to be a lower-case alphanum sequence starting with a
		# letter; this ensures that only normal functions can be called if the
		# standard perl coding conventions are used. Functions starting with
		# an underscore are assumed to be private.
		die "Malformed action '$action'"
			unless $action =~ /^[a-z][a-z0-9_]*$/;
			
		eval {
			$self->call($action, $self->params);
			$self->db->commit
				if defined $self->db;
		};
		
		if ($@) {
			$self->db->rollback
				if defined $self->db;
				
			die $@;
		}
		
		$self->controller->reload;
		
		return;
	}

	return $self->call(RENDER_SUB, $self->params);
}

1;
