#!/usr/bin/perl

require "../cgi-lib.pl";
ReadParse(*params);
srand( time() ^ ($$ + ($$ << 15)) );

@cookies = split /; /,$ENV{HTTP_COOKIE};

#derived "best letters":
#EATIROSNLCDUHMPGFBYWVKXJZQ

if($params{game} ne "reset") {
    foreach (@cookies) {
	if(/HANGSTRING=([0-9A-Z]+)/) {
	    $cstring = $1;
	} elsif(/HANGGUESS=([0-9A-Z]+)/) {
	    $cguess = $1;
	}
    }
} else {
    print "Set-Cookie: HANGGUESS=; path=\/;\n";
}

if(!$cstring || decode($cstring) eq "INVALID!") {
    $string = get_rand_string();
    $estring = encode($string);
    print "Set-Cookie: HANGSTRING=$estring; path=\/;\n";
} else {
    $string = decode($cstring);
    $estring = encode($string);
}
($diff,$level) = string_level($string);
if(!$cguess || decode($cguess) eq "INVALID!") {
    $guessed = '';
} else {
    $guessed = decode($cguess);
}
if($params{guess}) {
    $guessed .= uc $params{guess};
}
$eguess = encode($guessed);
print "Set-Cookie: HANGGUESS=$eguess; path=\/;\n";

print "Content-Type: text/html\n\n";
$known = '';
foreach $char (split //,$string) {
    if($char =~ /[A-Za-z]/) {
	if($guessed =~ /$char/i) {
	    $known .= $char;
	} else {
	    $known .= "_";
	}
    }
    else { $known .= $char; }
}

$body = qq`    ,---,  
    |   |
    |   h
    |  lbr
    |   b
    |  L R
  __|__`;

print "<table border=1><tr valign=top><td valign=top>\n";
foreach $char (split //,$guessed) {
    if($string !~ /$char/i) {
	$bodyparts++;
    }
}
if($bodyparts >= 1) { $body =~ s/h/O/g; }
if($bodyparts >= 2) { $body =~ s/b/\|/g; }
if($bodyparts >= 3) { $body =~ s/r/\\/g; }
if($bodyparts >= 4) { $body =~ s/l/\//g; }
if($bodyparts >= 5) { $body =~ s/R/\\/g; }
if($bodyparts >= 6) { $body =~ s/L/\//g; }
$body =~ s/[hbrlRL]/ /g;
print "<pre>$body</pre>\n";
print "</td><td valign=top nowrap>\n";
print "Difficulty: $level (1=easy, 10=hard)<br>\n";
if($bodyparts >= 6) {
    $print = $string;
} else {
    $print = $known;
}
print "<table border=1>\n";
print "<tr>\n";
foreach $char (split //,$print) {
    print "<td".(($char =~ /\S/) ? ">$char" : " bgcolor=#CCCCCC>&nbsp;")."</td>\n";
}
print "</tr>\n";
print "</table>\n";

$letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
print "<br>\nAlready Guessed:\n";
print uc $guessed;
print "<br>\n";

if($known !~ /_/) {
    print "<br>\nYou won in ".length($guessed)." guesses!\n";
    print "<a href=\"$ENV{SCRIPT_NAME}?game=reset\">Play Again?</a><br>\n";
} elsif($bodyparts >= 6) {
    print "<br>\nYou LOST!\n";
    print "<a href=\"$ENV{SCRIPT_NAME}?game=reset\">Play Again?</a><br>\n";
} else {
    print "Guess:\n";
    foreach $char (split //,$letters) {
	next if($guessed =~ /$char/i);
	print "<a href=\"$ENV{SCRIPT_NAME}?guess=$char\">$char</a>\n";
    }
    print "<br>\n";
    print "<br><a href=\"$ENV{SCRIPT_NAME}?game=reset\">Restart Game</a>\n";
}
print "</td></tr>\n";

#suggestions
my(@suggestions) = get_suggestions();
if(@suggestions < 100) {
    print "<tr><td colspan=2 align=center><br>\n";
    $params{suggestion} =~ s/\"//g;
    print "<form>Suggest a Lego-related word or phrase for hangman: <input type=text name=suggestion value=\"$params{suggestion}\" maxlength=50>\n";
    if($params{suggestion}) {
	$sug = $params{suggestion};
	$sug =~ s/^\s+//;
	$sug =~ s/\s+$//;
	$sug = substr($sug,0,50);
	$sug = uc $sug;
	$tmp = $sug;
	$letters = $tmp =~ s/[A-Z]//g;
	$other = length $tmp;
	if($letters < 4) {
	    print "<br>That suggestion doesn't have enough letters!<br>\n";
	} elsif($other * 4 > $letters) {
	    print "<br>There are too many non-letters in that suggestion.<br>\n";
	} else {
	    my($flag) = 1;
	    my(@strings) = get_strings();
	    foreach (@strings) {
		if($sug eq $_) {
		    print "<br>We've already got that one!<br>\n";
		    $flag = 0;
		}
	    }
	    if($flag) {
		foreach (@suggestions) {
		    if($sug eq $_) {
			print "<br>We don't have that yet, but it has been suggested!<br>\n";
			$flag = 0;
		    }
		}
		if($flag) {
		    print "<br>Thanks for the suggestion!<br>\n";
		    my($v,$l) = string_level($sug);
		    print "The suggestion you entered ranks about level $l<br>";
		    add_suggestion($sug);
		}
	    }
	}
    }
    print "</form>\n";
    print "</td></tr>\n";
}

print "</table>\n";

sub get_suggestions {
    my($file) = "lego_word_suggestions.txt";
    my(@strings);
    if(open(FILE,$file)) {
	while(<FILE>) {
	    chomp;
	    push @strings,$_ if(/\w+/);
	}
    }
    return @strings;    
}

sub add_suggestion {
    my($sug) = @_;
    my($file) = "lego_word_suggestions.txt";
    if(open(FILE,">> $file")) {
	print FILE "$sug\n";
	close FILE;
    }
}

sub string_level {
    my($string) = @_;
    #EATIROSNLCDUHMPGFBYWVKXJZQ
    my($hard) = "QZJXKVWYBFGPMHUDCLNSORITAE";
    $string = uc $string;
    $string =~ s/[^A-Z]//g;
    my($char,%chars,$xl,$xd,$ml,$md,$level);
    foreach $char (split //,$string) {
	$chars{$char} = 1;
    }
    my($diff) = 0;
    #for each letter, more points, for each COMMON letter, even more points
    foreach $char (keys %chars) {
	$diff+= index($hard,$char) + 1;
    }
    #can go from 0 to 351 (assuming the empty string is unguessable, as
    #all letters guessed will be wrong)

    #Hmm... Let's add something for string length and maybe even "average
    #guessability".

    my($avg) = $diff / (scalar (keys %chars));
    #could range from 1 to 26, but usually from 11.75 to 22.5
    
    my($len) = length($string);
    #typically between 4 and 24

    my($val) = $len+$avg+($diff/10);
    #could conceivably go from 2.1 to 105.48, in practice, 22.45 to 79.05

    # after quasi close examination, highest diff was 281, and lowest
    # was 47.

    #Let's make 270+ => level 1, and 80- => level 10
    $xl = 10;
    $ml = 1;
    $xd = 35;
    $md = 65;

    $level = (($xl-$ml)/($xd-$md))*$val + ($ml - $md*(($xl-$ml)/($xd-$md)));
    if($level > $xl) { $level = $xl; }
    if($level < $ml) { $level = $ml; }
    $level = int $level;
    return ($val,$level);
}

sub get_strings {
    my($file) = "legowords.txt";
    my(@strings);
    if(open(FILE,$file)) {
	while(<FILE>) {
	    chomp;
	    push @strings,$_ if(/\w+/);
	}
    }
    return @strings;
}

sub get_rand_string {
    my(@strings) = get_strings();
    my($rand) = int(rand scalar(@strings));
    if($params{get_levels}) {
	print "Content-Type: text/html\n\n";
	my(%rat) = ();
	foreach(@strings) {
	    ($d,$l) = string_level($_);
	    push @{$rat{$l}},$_;
	}
	print "Total strings: ".scalar(@strings)."<br>\n";
	foreach $l (sort {$a <=> $b} (keys %rat)) {
	    print "<b>Level $l (".scalar(@{$rat{$l}}).")</b><br>\n";
	    foreach (@{$rat{$l}}) {
		my($tmp) = $_;
		my($cnt) = $tmp =~ s/[A-Z]//g;
		my($len) = length($tmp);
		my(@letters) = grep /[A-Z]/,(split //);
		my(%diff_letters);
		map {$diff_letters{$_}++} @letters;
		my($letter_count) = scalar(keys %diff_letters);
		if($len) {
		    $tmp = $cnt/$len;
		    if($tmp < 5) { $tmp = "<b>$tmp</b>"; }
		} else {
		    $tmp = "NONE";
		}
		print "$_ ~ (Score:".(string_level($_))[0].", Tmp:$tmp Len:".scalar(@letters)." DiffLetters:$letter_count)<br>\n";
	    }
	}
    }
    return $strings[$rand];
}

sub encode {
    my($string) = @_;
    my(@chars) = split //,$string;
    my($hex,$result,$char,$str);
    my($offset) = int(rand 10); #0 thru 9
    $result = "$offset";
    foreach $char (@chars) {
	$hex .= sprintf("%X",(ord($char)-$offset));
    }
    $hex = join("",(reverse(split //,$hex)));
    while($hex =~ /^(........)/) {
	$str = $1;
	$hex =~ s/^\Q$str\E//;
	$result .= add_check_digit($str);
    }
    $result .= $hex;
    return $result;
}

sub decode {
    my($string) = @_;
    my($offset) = substr($string,0,1);
    if($offset !~ /\d/) { return "INVALID!"; }
    my($sub,$check,$res,$hex,$result);
    $string =~ s/^.//;
    while($string =~ /^(........)(.)/) {
	$sub = $1;
	$check = $2;
	return "INVALID!" if(add_check_digit($sub) ne "$sub$check");
	$res .= $sub;
	$string =~ s/^\Q$sub$check\E//;
    }
    $res .= $string;
    $res = join("",(reverse(split //,$res)));
    while($res =~ /^(..)/) {
	$hex = $1;
	$result .= chr(hex($hex)+$offset);
	$res =~ s/^\Q$hex\E//;
    }
    return $result;
}

sub add_check_digit {
    if(length $_[0] == 8) {
	if($_[0] !~ /^[0-9a-zA-Z\*\@\#]{8}$/) { return $_[0]; }
        my(@d,$i,$val,%m);
        foreach(0..9,'a'..'z','*','@','#') { $m{$_} = $i++; }
        @d = split //,(lc $_[0]);
        for($i=0;$i<@d;$i++) {
            $d[$i] = int $m{$d[$i]};
            if($i%2) { $d[$i] *= 2; }
        }
        @d = split //,join("",@d);
        $val = 0;
        foreach (@d) { $val += $_; }
        return $_[0] . ((10 - $val % 10) % 10);
    } else {
	return $_[0];
    }
}
