#!/usr/local/bin/perl

use strict ;
use warnings ;

use Template::Simple ;
use Benchmark qw( timethese cmpthese ) ;
use Data::Dumper ;

my @dims = @ARGV ? (shift, shift) : ( 3, 5 ) ;

my $data = generate_data( @dims ) ;
#print Dumper $data ;

my $tmpl = Template::Simple->new() ;
my $comp = Template::Simple->new() ;

my $template = <<TMPL ;
Header
Val 1: [%val_1%]
[%START nest%]
	Level: [%level%]
[%END nest%]
[%START opt%]
	Optional Level: [%level%]
[%END opt%]
Val 2: [%val_2%]
Footer
TMPL

$tmpl->add_templates( { bench => $template } ) ;
my $rendered = $tmpl->render( 'bench', $data ) ;

$comp->add_templates( { bench => $template } ) ;
$comp->compile( 'bench' ) ;
my $comp_rendered = $comp->render( 'bench', $data ) ;
#print $comp->get_source( 'bench' ) ;

my $ok = $$rendered eq $$comp_rendered ;
print "OK comp [$ok]\n" ;

my $appended = append( $data ) ;
$ok = $$rendered eq $$appended ;
print "OK append [$ok]\n" ;

#print $$rendered ;
#my $joined = joined( $data ) ;
# print $$rendered ;
# print "----\n" ;
# print $$joined ;
# print $$comp_rendered ;

cmpthese( shift || -2,
	{
		render	=> sub { my $x = $tmpl->render( 'bench', $data ) },
#		joined	=> sub { my $x = joined( $data ) },
#		concat	=> sub { my $x = concat( $data ) },
		append	=> sub { my $x = append( $data ) },
#		compiler => sub { my $x = $comp->compile( 'bench' ) },
		compiled => sub { my $x = $comp->render( 'bench', $data ) },
	}
) ;

exit ;

no warnings ;

sub joined {

no warnings ;

	my( $data ) = @_ ;

	my $out = do {
		my $out ;

		my @data = $data ;
		while( defined( my $data = shift @data ) ) {

			if ( ref $data eq 'ARRAY' ) {
				push @data, @{$data} ;
				next ;
			}

			$out .= ref $data ne 'HASH' ? $data :

			join( '',
			"Header\nVal 1: "
			,
			$data->{val_1}
			,
			"\n"
			,
			do {
				my $data = $data->{nest} ;
				my $out ;

				my @data = $data ;
				while( defined( my $data = shift @data ) ) {

					if ( ref $data eq 'ARRAY' ) {
						push @data, @{$data} ;
						next ;
					}

					$out .= ref $data ne 'HASH' ? $data :
					join( '',
					"\n\tLevel: "
					,
					$data->{level}
					,
					"\n" ) ;
				}
				$out ;
			}
			,
			"\n"
			,
			do {
				my $data = $data->{opt} ;
				my $out ;

				my @data = $data ;
				while( defined( my $data = shift @data ) ) {

					if ( ref $data eq 'ARRAY' ) {
						push @data, @{$data} ;
						next ;
					}

					$out .= ref $data ne 'HASH' ? $data :
					join( '', 
					"\n\tOptional Level: "
					,
					$data->{level}
					,
					"\n" ) ;
				}
				$out ;
			}
			,
			"\nVal 2: "
			,
			$data->{val_2}
			,
			"\nFooter\n" ) ;
		}
		$out ;
	} ;

	return \$out ;
}


sub concat {

no warnings ;

	my( $data ) = @_ ;

	my $out = do {
		my $out ;

		my @data = $data ;
		while( defined( my $data = shift @data ) ) {

			if ( ref $data eq 'ARRAY' ) {
				push @data, @{$data} ;
				next ;
			}

			$out .= ref $data ne 'HASH' ? $data :
			"Header\nVal 1: "
			.
			$data->{val_1}
			.
			"\n"
			.
			do {
				my $data = $data->{nest} ;
				my $out ;

				my @data = $data ;
				while( defined( my $data = shift @data ) ) {

					if ( ref $data eq 'ARRAY' ) {
						push @data, @{$data} ;
						next ;
					}

					$out .= ref $data ne 'HASH' ? $data :
					"\n\tLevel: "
					.
					$data->{level}
					.
					"\n" ;
				}
				$out ;
			}
			.
			"\n"
			.
			do {
				my $data = $data->{opt} ;
				my $out ;

				my @data = $data ;
				while( defined( my $data = shift @data ) ) {

					if ( ref $data eq 'ARRAY' ) {
						push @data, @{$data} ;
						next ;
					}

					$out .= ref $data ne 'HASH' ? $data :
					"\n\tOptional Level: "
					.
					$data->{level}
					.
					"\n" ;
				}
				$out ;
			}
			.
			"\nVal 2: "
			.
			$data->{val_2}
			.
			"\nFooter\n" ;
		}

		$out ;
	} ;

	return \$out ;
}

sub append {
	my( $data ) = @_ ;

	my $out ;

	my @data = $data ;
	while( defined( my $data = shift @data ) ) {

		if ( ref $data eq 'ARRAY' ) {
			push @data, @{$data} ;
			next ;
		}


		$out .= ref $data ne 'HASH' ? $data :
		"Header\nVal 1: "
		.
		$data->{val_1}
		.
		"\n" ;

		my @data = $data->{nest} ;
		while( defined( my $data = shift @data ) ) {

			if ( ref $data eq 'ARRAY' ) {
				push @data, @{$data} ;
				next ;
			}


			$out .= ref $data ne 'HASH' ? $data :
			"\n\tLevel: "
			.
			$data->{level}
			.
			"\n" ;

		}

		$out .= ref $data ne 'HASH' ? $data :
		"\n" ;

		my @data = $data->{opt} ;
		while( defined( my $data = shift @data ) ) {

			if ( ref $data eq 'ARRAY' ) {
				push @data, @{$data} ;
				next ;
			}


			$out .= ref $data ne 'HASH' ? $data :
			"\n\tOptional Level: "
			.
			$data->{level}
			.
			"\n" ;

		}

		$out .= ref $data ne 'HASH' ? $data :
		"\nVal 2: "
		.
		$data->{val_2}
		.
		"\nFooter\n" ;

	}

	return \$out ;
}

sub generate_data {

	my( $outer_size, $inner_size ) = @_ ;

	my @outer_data ;

	for my $outer ( 1 .. $outer_size ) {

		my @inner_data ;

		for my $inner ( 1 .. $inner_size ) {

			push @inner_data, {

				val_1	=> ($outer x 2),
				val_2	=> ($inner x 3),
				nest	=> {
					level => "$outer-$inner",
				},
				$inner % 2 ? () : (
			
					opt	=> {
						level => "$outer-$inner",
					},
				),
			} ;
		}

		push @outer_data, [@inner_data] ;
	}

	return \@outer_data ;
}
