use 5.014;
package SudokuTools;
use strict;
use warnings;
use List::Util 'shuffle';
use HTML::Template::Compiled;
use GD;
require Exporter;

our @ISA = qw(Exporter);
our @EXPORT = qw(html_out_2 random_permute random_row_shuffle 
		random_col_shuffle random_row_block_shuffle 
		random_col_block_shuffle transpose linearize linear_solve 
        todays_sudoku
		mkdir_p svg_export eps_export png_export);
our %EXPORT_TAGS = (":all" => \@EXPORT);

sub todays_sudoku {
    my $time = $_[0] // time;
	my $line;
	{
		my $s_file = "sudoku17";
		my $sl;
		open ($sl, $s_file) or die "$0: Can't read $s_file: $!";
		my $days = int( 0.5 + ($time - 1169249681) / (3600*24));
		$days++;
		$line = <$sl> while($days--);
	}
    die "No sudoku found" if length($line) < 80;

	my @line = split //, $line;


	# convert from linear to list-of-list representation
	my @s = ([], [], [], [], [], [], [], [], []);
	foreach (0 .. 80){
		$s[int($_/9)][$_ % 9] = $line[$_];
	}

	# our Sudokus are too uniform, let's randomize
    srand $time;
	random_permute(\@s);
	random_col_shuffle(\@s);
	random_col_block_shuffle(\@s);
	random_row_shuffle(\@s);
	random_row_block_shuffle(\@s);
	transpose(\@s) if (rand() < 0.5);
	return \@s;
}

sub random_permute {
	my $s = shift;
	my @perm = shuffle(1 .. 9);
	unshift @perm, 0;
	foreach my $x (0 .. 8){
		foreach my $y (0 .. 8){
			$s->[$x][$y] = $perm[$s->[$x][$y]];
		}
	}

	return $s;
}

sub random_tripple {
	my @a = ([0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], 
			[2, 0, 1], [2, 1, 0]);
	my $b = $a[rand @a];
	return wantarray ? @$b : $b;
}

sub random_col_shuffle {
	my $s = shift;
	foreach (0, 3, 6){
		my @p = random_tripple();
		($s->[$p[0] + $_], $s->[$p[1] + $_], $s->[$p[2] + $_]) 
			= @$s[$_ .. $_ + 2];
	}

}

sub random_row_shuffle {
	my $s = shift;
	transpose($s);
	random_col_shuffle($s);
	transpose($s);
}


sub random_col_block_shuffle {
	my $s = shift;
	my @p = random_tripple();
	($s->[3*$p[0]], $s->[3*$p[0] + 1], $s->[3*$p[0] + 2],
		$s->[3*$p[1]], $s->[3*$p[1] + 1], $s->[3*$p[1] + 2],
		$s->[3*$p[2]], $s->[3*$p[2] + 1], $s->[3*$p[2] + 2]) 
			= @$s;

}
sub random_row_block_shuffle {
	my $s = shift;
	transpose($s);
	random_col_block_shuffle($s);
	transpose($s);
}

sub transpose {
	my $s = shift;
	foreach my $x (0 .. 8){
		foreach my $y ($x+1 .. 8) {
			($s->[$x][$y], $s->[$y][$x]) = 
				($s->[$y][$x], $s->[$x][$y]);
		}
	}
}

sub javascript_out {
	# expects a linear represented sudoku
	my $l = shift;
	my @l = @$l;
	my $js = "[\n";
	for my $x (0 .. 8){
		$js .= "\t[";
		$js .= join ", ", @l[9*$x .. 9*$x+8];
		$js .= "],\n";
	}
	$js .= "]";
}


sub linear_solve {
	# expects a linearized sudoku and returns a solved linear one
	my $s = shift;
	my $s_string = join '', @$s;
	my $res = `echo $s_string |/home/moritz/yasss `;
	die "$0: Execution of sudoku_solver failed :$!" unless ($res);
	chomp $res;
	my @res = split //, $res;
	return \@res;
}


sub linearize {
	my $s = shift;
	my @res = ();
	foreach (@$s){
		push @res, @$_;
	}
	return \@res;
}


sub html_out_2 {
	my ($s, $short_date, $lang, $solved, $cgi) = @_;
	my $js = javascript_out($solved);

	my $t = HTML::Template::Compiled->new(
			filename 			=> "templates/html_out.shtml.$lang",
			die_on_bad_params 	=> 0,
			open_mode			=> '<:utf8',
			);
	$t->param(DATE_SHORT => $short_date,
			JS_TABLE => $js);
	my @row;
	for(my $y = 0; $y < 9; $y++){
		my @col = ();
		for (my $x = 0; $x < 9; $x++){
			my $val = $s->[$y][$x];
			my %item = (NAME => "s$y$x");
			if ($val){
				$item{VALUE} = $val;
				$item{GIVEN} = 1;
			} else {
				$item{VALUE} = "";
			}
			if (($y+1) % 3 == 0 && $y < 7){
				$item{B} = 1;
			}
			if (($x+1) % 3 == 0 && $x < 7){
				$item{R} = 1;
			}
			push @col, \%item;
		}
		push @row, {COL => \@col};
	}
	$t->param(ROW => \@row, TIMESTAMP => time);

	return $t->output;

}

sub mkdir_p($) {
	my $path = shift;
	return if (-d $path);
	my @dir = split '/', $path;
	foreach (0 .. $#dir){
		my $p =  join '/', @dir[0 .. $_];
		if ($p && ! -d $p){
			mkdir $p or die "$0: mkdir_p: Can't create directory $p: $!";
		}
	}
}

sub svg_export {
	my $s = shift;
	my $tmpl = HTML::Template::Compiled->new(filename => "templates/print.svg");
	my @numbers;
	for (my $x = 0; $x < 9; $x++){
		for (my $y = 0; $y < 9; $y++){
			my $n = $s->[$x + 9 * $y];
			if ($n){
				push @numbers, {
					ID 		=> $x . $y, 
					X 		=> 100 / 3 * $x + 10.4,
					Y 		=> 100 / 3 * $y + 24.5,
					NUMBER 	=> $n,
				};
			}
		}
	}
	$tmpl->param(NUMBERS => \@numbers);
	return $tmpl->output;
}
sub eps_export ($) {
	my $s = shift;
	my $tmpl = HTML::Template::Compiled->new(filename => "templates/print.eps");
	my @numbers;
	for (my $x = 0; $x < 9; $x++){
		for (my $y = 0; $y < 9; $y++){
			my $n = $s->[$x + 9 * $y];
			if ($n){
				push @numbers, {ID => $x . $y, 
					X => 100 / 3 * $x + 12,
					Y => 300 - 100 / 3 * ($y + 1) + 10,
					NUMBER => $n};
			}
		}
	}
	$tmpl->param(NUMBERS => \@numbers);
	return $tmpl->output;
}

sub png_export {
	my $s = shift;
	my $gd = GD::Image->newFromPng("templates/print.png") or die "Can't create GD image!\n";
	my @numbers;
	for (my $x = 0; $x < 9; $x++){
		for (my $y = 0; $y < 9; $y++){
			my $n = $s->[$x + 9 * $y];
			if ($n){
				$gd->string(gdGiantFont, 100 / 3 * $x + 16, 
						100 / 3 * $y + 13, $n, 0);
			}
		}
	}
	return $gd->png;
}
