# Sudoku Show
# 02/22/2006
# Partially based on demo code by Steve Lidie, April 1999, on the PerlTK.org website
# http://www.perltk.org/index.php?option=com_content&task=view&id=15&Itemid=28
#
# USAGE: perl SudokuShow.pl <Answer file> <Incomplete puzzle file>

package SudS;
use Tk;
use Tk::widgets qw/Dialog/;
use Text::ParseWords;
use strict;
use warnings;

#use vars qw/$SudokuShow $VERSION @Sudoku @Answer/;
our ($SudokuShow, $VERSION, @Sudoku, @Answer);
$SudokuShow = MainWindow->new;
# print "@ARGV\nor else not";
build_window(@ARGV); 
MainLoop; 

sub build_window {		# Where the action happens

    $VERSION = '0.1.0 Beta'; 	# What version I'm on

    $SudokuShow->title("Sudoku Show"); 
    $SudokuShow->iconname('S'); 
    my $bmp = $SudokuShow->Photo(-file => "s.bmp", -format => 'bmp');		# A nifty little corner-icon thingy
    $SudokuShow->Icon(-image => $bmp);

	my $completeFile = shift;						# I haven't gotten the hang of passing arguments into the file yet
	my $incompleteFile = shift;
#	my $fileName = "sudoku.txt";			# So I've hard-coded the filename in.
	open (INPUTCF, $completeFile) or die "\nCan't open $completeFile : $!";
	open (INPUTICF, $incompleteFile) or die "\nCan't open $incompleteFile : $!";
	my @slurpCF = <INPUTCF>;					# With Text::ParseWords, I can slurp the whole file and dump it to an array.
	my @slurpICF = <INPUTICF>;
	@Answer = nested_quotewords(" ", 0, @slurpCF);		# When I get a better handle on regular expressions, the input file can be more flexible than single space delimiting.
	@Sudoku = nested_quotewords(" ", 0, @slurpICF);
	close INPUTCF;
	close INPUTICF;
#	check();
#	my $error = 0;
#	for my $i (0..8) { for my $j (0..8) { if ($Sudoku[$i][$j]==$Answer[$i][$j]) { $error = 1; } } }
#	if ($error == 1) { print "That's right!"; }
#	elsif ($error == 0) { print "Nope! Try again!"; }
#	@Sudoku = (	[1,2,3,4,5,6,7,8,9],		# A full Sudoku, for testing without inputs.
#				[4,5,6,7,8,9,1,2,3],
#				[7,8,9,1,2,3,4,5,6],
#				[2,3,4,5,6,7,8,9,1],
#				[5,6,7,8,9,1,2,3,4],
#				[8,9,1,2,3,4,5,6,7],
#				[3,4,5,6,7,8,9,1,2],
#				[6,7,8,9,1,2,3,4,5],
#				[9,1,2,3,4,5,6,7,8]);
#	@Sudoku = (	[11,12,13],		# A small test grid to get the GUI working
#				[14,15,16],
#				[17,18,19]);
	for my $i (0..$#Sudoku) {		# This chunk builds the boxes for each element.
		for my $j (0..$#{$Sudoku[$i]}) {
#			print " $Sudoku[$i][$j]";
			my $val = $Sudoku[$i][$j];
			my $box = build_box($val,$i,$j);
		}
	}
    my $menubar = build_menubar(); 
}

sub build_box {		# Creates a label widget for the numbers, or a button widget for the blanks, and places them in the grid.
	my $num = shift;
	my $yPos = shift;
	my $xPos = shift;
	my $box = $SudokuShow->Label(	-text => $num,
									-background => 'light blue', # The fixed numbers are surrounded by light blue, as opposed to the user-entered white buttons.
									-relief => 'groove',	# The border of the boxes
									-height => 5,
									-width => 10);	# Size of the boxes
	if ($num == 0) {		# If the number is blank, the label is overwritten by a button.
		our $change_dialog = $SudokuShow->Dialog(-title   => 'Edit',		# This dialog lets the user choose the new value.
												-text    => 'Select a new value for this square.',
												-default_button => '0',
												-buttons => [qw/1 2 3 4 5 6 7 8 9/]); 
#		print $Sudoku[$yPos][$xPos];
		$box = $SudokuShow->Button(	-text => "",
									-background => 'white',	
									-relief => 'groove',	# The border of the boxes
									-height => 5,
									-width => 10,
									-command => sub{my $change = $change_dialog -> Show;
													$box->configure(-text => $change);
													alter($change,$yPos,$xPos);});			# The program didn't want to edit the @Sudoku array here, so I made it a seperate subroutine and it was happy.
#													print $Sudoku[$yPos][$xPos];});
	}
	$box->grid(	-column => $xPos,	# Positional Data
				-row => $yPos,
				-ipadx => 1,		# I'm trying to put a border on the boxes here, but groove did it better
				-ipady => 1);
	$box;						# To return something, and because I was trying to map it outside the subroutine for a while.
}

sub build_menubar {		# It builds the menu bar.  It will be important later, and it was useful practice.

    my $menubar = $SudokuShow->Menu; 
    $SudokuShow->configure(-menu => $menubar); 
    my $file = $menubar->cascade(-label => '~File', -tearoff => 0);		# I don't care for the tearoff option.  It's ugly.
    my $help = $menubar->cascade(-label => '~Help', -tearoff => 0); 
	
	$file->command(-label => 'Generate', -command => sub{generate()});	# Asks for the new difficulty then makes a new Sudoku.
	$file->command(-label => 'Check', -command => sub{check()});		# Checks the answer and displays the correctness.
	$file->command(-label => 'Quit', -command => \&exit);	# In case somebody doesn't want to use the corner x.

    $help->command(-label => 'Version');	# Version info
    $help->separator; 
    $help->command(-label => 'About');		# What it does

    my $version_dialog =   $SudokuShow->Dialog(-title   => 'SudokuShow Version', 
                                -text    => "SudokuShow\n\nVersion $VERSION",		#Reads the version info and displays it
                                -buttons => ['OK'], 
                                -bitmap  => 'info'); 
    my $about_dialog = $SudokuShow->Dialog(-title   => 'About SudokuShow', 
                                -text    => 'This program displays Sudoku puzzles and allows the user to fill in gaps.',	# It still doesn't generate them.
                                -buttons => ['OK']); 
    my $menu = $help->cget('-menu'); 
    $menu->entryconfigure('Version', -command => [$version_dialog   => 'Show']); 
    $menu->entryconfigure('About',   -command => [$about_dialog => 'Show']); 

    $menubar;
}

sub check {			# This subroutine checks the sudoku as displayed with the answer matrix.
	my $error = 0;
	my $complete = 1;
	for my $i (0..8) { for my $j (0..8) {	if ($Sudoku[$i][$j]!=$Answer[$i][$j]) {	$error = 1; };
											if ($Sudoku[$i][$j]==0) { $complete = 0; } } }
#																					print "$Sudoku[$i][$j] != $Answer[$i][$j]";} } }
#	if ($error == 0) { print "That's right!"; }
#	elsif ($error == 1) { print "Nope! Try again!"; }
	if ($complete == 0) {		# If any cells of the sudoku remain unfiller, this case points it out.
		my $check_dialog = $SudokuShow->Dialog(	-title   => 'Whoops!', 
												-text    => 'The puzzle isn\'t yet complete!',
												-buttons => ['OK']); 
		$check_dialog -> Show;
	}
	elsif ($error == 1) {		# If any of the cells has the wrong value, this case points it out.
		my $check_dialog = $SudokuShow->Dialog(	-title   => 'Answer', 
												-text    => 'This isn\'t the correct answer.',
												-buttons => ['OK']); 
		$check_dialog -> Show;
	}
	elsif ($error == 0) {		# If the Sudoku is complete and correct, this message congratulates the player.
		my $check_dialog = $SudokuShow->Dialog(	-title   => 'Answer', 
												-text    => 'Congratulations!  That\'s the correct answer!',
												-buttons => ['OK']); 
		$check_dialog -> Show;
	}
}	

sub alter {					# Altering the elements of the @Sudoku array seems to work better as part of a subroutine than as part of the button command.
	my $newVal = shift;
	my $y = shift;
	my $x = shift;
	$Sudoku[$y][$x] = $newVal;
}

sub generate {				# This subroutine queries the user as to what difficulty to make a new Sudoku, and will eventually link into the Sudoku generation piece.
	my $diff_dialog = $SudokuShow->Dialog(	-title	=> 'New Sudoku',
											-text	=> 'Select a new difficulty:',
											-default_button	=> 'Cancel',
											-buttons=> [qw/Easy Medium Hard Cancel/]);
	my $diffiText = $diff_dialog -> Show;
	my $diffiNum = 0;
	if ($diffiText eq 'Cancel') {return 0;}		# If the user selects cancel, the routine exits back to the main window without doing anything.
	elsif ($diffiText eq 'Easy')				# The rest of this if-block turns the button pressed into a number.
		$diffiNum = 1;
	elsif ($diffiText eq 'Medium')
		$diffiNum = 2;
	elsif ($diffiText eq 'Hard')
		$diffiNum = 3;
	return 1;									# The routine doesn't hook to anything yet, so this just jumps back to the main loop.
}
