package Web::Canvas;

use strict;
use warnings;

use constant {
	DEFAULT_TITLE => 'Untitled',
	DEFAULT_HEADING => 'Untitled',
};

use URI::Escape;
use Web::Canvas::Form;
use Web::Canvas::Section;

sub new {
	my ($class, $app) = @_;
	my $self = bless {
		app => $app,
		language => undef,
		title => undef,
		heading => undef,
		styles => [],
		content => [],
		class => undef,
		level => 1,
	}, $class;
	
	return $self;
}

sub class { $_[0]->{class} }
sub app { $_[0]->{app} }
sub content { wantarray ? @{$_[0]->{content}} : $_[0]->{content} }
sub styles { wantarray ? @{$_[0]->{styles}} : $_[0]->{styles} }
sub language { $_[0]->{language} }
sub title { $_[0]->{title} }
sub heading { $_[0]->{heading} }
sub level { $_[0]->{level} }

sub set_language { $_[0]->{language} = $_[1]; $_[0] }
sub set_title { $_[0]->{title} = $_[1]; $_[0] }
sub set_heading { $_[0]->{heading} = $_[1]; $_[0] }
sub set_class { $_[0]->{class} = $_[1]; undef }

sub use_class { 
	my ($self) = @_;
	my $class = $self->class;
	
	return unless defined $class;
	
	$self->set_class(undef);
	
	return wantarray ? (class => $class) : $class;
}

sub add_style {
	my ($self, $style) = @_;
	
	push @{$self->{styles}}, $style;
	
	return $self;
}

sub push_content {
	my ($self, @stuff) = @_;
	
	push @{$self->content}, @stuff;
	
	return $self;
}

sub add_text {
	my ($self, @text) = @_;
	
	$self->push_content([p => {$self->use_class}, @text]);
	
	return $self;
}

sub add_free_text {
	my ($self, @text) = @_;
	
	$self->push_content([div => {$self->use_class}, @text]);
	
	return $self;
}

sub add_list {
	my ($self, $items) = @_;
	my $list = [ul => {$self->use_class}, map {
		[li => $_],
	} @$items];
	
	$self->push_content($list);
	
	return $self;
}

sub add_form {
	my ($self, $action) = @_;
	my $form = Web::Canvas::Form->new($self, $action, $self->use_class);
	
	$self->push_content($form);
	
	return $form;
}

sub add_section {
	my ($self, $heading) = @_;
	my $section = Web::Canvas::Section->new($self, $heading, $self->level + 1, 
		$self->use_class);
	
	$self->push_content($section);
	
	return $section;
}

sub add_linked_text {
	my ($self, $text, @actions) = @_;
	my @stuff;
	
	while ($text =~ /^(.*?)\[(.*?)\](.*)$/) {
		$text = $3;
		push @stuff, $1;
		push @stuff, $self->create_link($2 => shift @actions);
	}
	
	push @stuff, $text;
	$self->push_content([p => {$self->use_class}, @stuff]);
	
	return $self;
}

sub add_error_text {
	my ($self, %messages) = @_;
	
	for my $name (keys %messages) {
		next unless $self->has_error($name);
		
		my ($error, @message) = $messages{$name}->();
		
		$self->push_content(
			[p => {class => 'error'},
				[strong => $error],
				[span => ': '],
				@message,
			],
		);
	}
	
	return $self;
}

sub add_table {
	my ($self, $headings, @data) = @_;
	my $table_class = {$self->use_class};
	my $class;
	my $last;
	my %counts;
	my @heading_row;
	my @data_rows;
	
	for (@$headings) {
		$counts{$_}++;
		
		if ($counts{$_} == 1) {
			push @heading_row, $_;
		}
	}
	
	@heading_row = map {
		$class = undef;
		$class = 'number' if /^\s*[-+.,_\d]+\s*$/;
		
		[th => {
			($counts{$_} > 1 ? (colspan => $counts{$_}) : ()),
			(defined $class ? (class => $class) : ()),
		}, $_]
	} @heading_row;
	
	@data_rows = map {
		my $row = $_;
		
		[tr => map {
			$class = undef;
			$class = 'number' if /^\s*[-+.,_\d]+\s*$/;
			$class = 'seperator' if @$row == 1 && scalar @$headings > 1;
			
			[td => {
				(@$row == 1 ? (colspan => scalar @$headings) : ()),
				(defined $class ? (class => $class) : ()),
			}, $_],
		} @$_],
	} @data;
	
	$self->push_content(
		[table => $table_class,
			@$headings ? [thead => 
				[tr => @heading_row],
			] : (),
			[tbody =>
				@data_rows,
			],
		],
	);
	
	return $self;
}

sub create_section {
	my ($self, $heading) = @_;
	my $section = Web::Canvas::Section->new($self, $heading, $self->level + 1, 
		$self->use_class);
	
	return $section;
}

sub create_link {
	my ($self, $text, @parts) = @_;
	my $action = join '/', map { uri_escape($_) } @parts;
	my $href = $self->app->cgi->script_name.
		(length($action) ? '/'.$action : '');
	
	return [a => {href => $href}, $text];
}

sub create_action {
	my ($self, $value, $action, $entity) = @_;
	my $form = Web::Canvas::Form->new($self, $action);
	
	if (defined $entity) {
		$form->add_hidden(id => $entity->id);
	}
	
	$form->add_button($value);
	
	return $form->structure;
}

sub create_image_action {
	my ($self, $image_data, $action, $entity) = @_;
	my $form = Web::Canvas::Form->new($self, $action);
	
	if (defined $entity) {
		$form->add_hidden(id => $entity->id);
	}
	
	$form->add_image(@$image_data);
	
	return $form->structure;
}

sub create_language {
	my ($self) = @_;
	
	return unless defined $self->language;
	return {lang => $self->language};
}

sub create_title {
	my ($self) = @_;
	
	return [title => $self->title || DEFAULT_TITLE];
}

sub create_heading {
	my ($self) = @_;
	my $gi = 'h'.$self->level;
	
	return [$gi => $self->heading || DEFAULT_HEADING];
}

sub content_structure {
	my ($self) = @_;
	my @content = map { 
		ref =~ /^Web::Canvas/ ? $_->structure : $_ 
	} $self->content;
	
	return wantarray ? @content : \@content;
}
	
sub structure {
	my ($self) = @_;
		
	return (
		[html => $self->create_language,
			[head =>
				$self->create_title,
				map {
					[link => {
						rel => 'stylesheet',
						type => 'text/css',
						href => $_,
					}],
				} $self->styles,
			],
			[body =>
				$self->create_heading,
				[div =>
					$self->content_structure,
				],
			],
		],
	);
}

sub elementify {
	my ($self) = @_;
	
	return HTML::Element->new_from_lol($self->structure);
}

1;
