use strict;
use warnings;
use JSON;
use File::Find qw(finddepth);
use Encode qw(decode);

#### START OF GLOBAL VARS

my $rawDir = "C:/Users/Owner/Perl/myprogs/WalkthruGenerator";
my $fh;  # Output file handle
my $respJsonData;
my $rawData;
my $walkData;	# The extracted part of rawData that defines the walkthrough section.
my @pureData;	# The purified walkData is a list of favoured lines from walkData to examine in more depth.
my @rawFiles = ();
my @searchMissing = ();
my @addSuggestions = ();

#### END OF GLOBAL VARS

#### START OF MAIN PROGRAM ####
# For each .json file, analyze it. Then make your report.

my @jsonFiles = <./json/*.json>;  # Note: It would be cleaner to do this with glob().
initRawFiles();
foreach my $file (@jsonFiles) {
	$respJsonData = loadJson($file);
	if (exists($respJsonData->{'search'})) {	# Don't bother if the search field isn't defined.
		scanRawFiles();
	}
	else {
		push @searchMissing, $file;
	}
}
makeSuggestPage();

#### END OF MAIN PROGRAM ####

sub loadJson {
   my $inname = $_[0];
   my $jsondata = '';
   open(my $jinfh, "<", $inname) || die("can't open $inname: $!");
   print "*** reading $inname ... ";
   while ( <$jinfh> ) {
     $jsondata .= $_; 
   }
   close $jinfh;
   print "DONE!\n";

   print "*** parsing $inname ... ";
   $jsondata =~ s/^\x{ef}\x{bb}\x{bf}//;  # decode_json doesn't like these leading bytes, the "Byte Order Mark" aka BOM.
   print STDERR "$inname\n";
   my $hashref = decode_json $jsondata;
   print "DONE!\n";
   return $hashref;
}

sub initRawFiles {
	# We want to initialize @rawFiles with a list of filenames.
	push @rawFiles, glob("$rawDir/a/*.txt");
	push @rawFiles, glob("$rawDir/b/*.txt");
	push @rawFiles, glob("$rawDir/c/*.txt");
	push @rawFiles, glob("$rawDir/d/*.txt");
	push @rawFiles, glob("$rawDir/e/*.txt");
	push @rawFiles, glob("$rawDir/f/*.txt");
	push @rawFiles, glob("$rawDir/g/*.txt");
	push @rawFiles, glob("$rawDir/h/*.txt");
	push @rawFiles, glob("$rawDir/i/*.txt");
	push @rawFiles, glob("$rawDir/j/*.txt");
	push @rawFiles, glob("$rawDir/k/*.txt");
	push @rawFiles, glob("$rawDir/l/*.txt");
	push @rawFiles, glob("$rawDir/m/*.txt");
	push @rawFiles, glob("$rawDir/n/*.txt");
	push @rawFiles, glob("$rawDir/o/*.txt");
	push @rawFiles, glob("$rawDir/p/*.txt");
	push @rawFiles, glob("$rawDir/q/*.txt");
	push @rawFiles, glob("$rawDir/r/*.txt");
	push @rawFiles, glob("$rawDir/s/*.txt");
	push @rawFiles, glob("$rawDir/t/*.txt");
	push @rawFiles, glob("$rawDir/u/*.txt");
	push @rawFiles, glob("$rawDir/v/*.txt");
	push @rawFiles, glob("$rawDir/w/*.txt");
	push @rawFiles, glob("$rawDir/x/*.txt");
	push @rawFiles, glob("$rawDir/y/*.txt");
	push @rawFiles, glob("$rawDir/z/*.txt");
}

sub scanRawFiles {
	# Note: The $respJsonData is already set.
	my ($wid, $wtitle);
	foreach my $rawFile (@rawFiles) {
		$wid = '';
		if ($rawFile =~ /\/\w\/(.*?)\.txt$/) {
			$wid = $1;
		}
		if ($wid ne '' && !exists($respJsonData->{'works'}->{$wid})) {
			loadRaw($rawFile);	# Read the text of the file and put it into $rawData.
			if ($rawData =~ /\{TITLE=(.*?)\}/s) {
				$wtitle = $1;
			}
			else {
				$wtitle = '';
			}
			# Now extract the walkthrough data only.
			if ($rawData =~ /==Walkthrough==(.*?)==Extras/s) {
				$walkData = $1;
			}
			elsif ($rawData =~ /<h2 id="walk">(.*?)<h2 id="extras">/s) {
				$walkData = $1;
			}
			elsif ($rawData =~ /<h2 id="walk">(.*)/s) {
				# Some really old raw files have no Extras section, so just go to the end of the file.
				$walkData = $1;
			}
			else {
				# I give up; use the entire file then, you weirdly formated raw file.
				$walkData = $rawData;
			}
			# Now extract from walkData only the lines we're interested in:
			my @tmpWalkData = split /\n/, $walkData;
			@pureData = ();
			foreach my $rec (@tmpWalkData) {
				if ($rec =~ /^(@|!!|>|\* >)/) {
					push @pureData, $rec;
				}
			}
			scanPureData($wid, $wtitle);
		}
		else {
			print STDERR "DEBUG: Declining to scanRawFile(\"$wid\").\n";
		}
	}
}

sub scanPureData {
	my ($wid,$wtitle) = @_;
	my $searchStrings = $respJsonData->{'search'};
	
	#### DEBUG
	# print STDERR "================\n";
	# foreach my $searchString (@{$searchStrings}) {
	# 	my $cat = $respJsonData->{'type'};
	# 	print STDERR "Re $cat: scanRawFile($wid) for \"$searchString\".\n";
	# }
	# print STDERR "----------------\n";
	# foreach my $rec (@pureData) {
	# 	print STDERR $rec . "\n";
	# }
	
	# my @stringsMatched = ();
	my @objLocPairs = ();
	foreach my $s (@{$searchStrings}) {
		my $currentRoom = '';
		foreach my $rec (@pureData) {
			if ($rec =~ /^@\s*(.*)/) {
				$currentRoom = $1;
			}
			elsif ($rec =~ /\b$s\b/i) {
				push @objLocPairs, [ $s, $currentRoom ] unless grep {$_[0] eq $s && $_[1] eq $currentRoom} @objLocPairs;
				last;
			}
		}
	}
	
	# Finally, make an add-suggestion if any search terms were found.
	if ((scalar @objLocPairs) > 0) {
		if ($wtitle =~ /^(The |A |An |La |Le )(.*)$/) {
			$wtitle = "$1|$2";
		}
		my $sug = " \"$wid\":{\"title\":\"" . s4json($wtitle) . "\",\n";
		$sug .= "   \"things\":[";
		$sug .= "\n" if scalar(@objLocPairs) > 1;
		my $nPairs = 0;
		foreach my $pair (@objLocPairs) {
			my $obj = $pair->[0];
			my $loc = s4json( $pair->[1] );
			$sug .= ",\n" if $nPairs++ > 0;
			$sug .= "     " if scalar(@objLocPairs) > 1;
			$sug .= "{\"name\":\"TODO $obj\",\"type\":\"" . $respJsonData->{'type'} . "\",\"loc\":\"$loc\",";
			$sug .= "\"desc\":\"\"}"
		}
		$sug .= "\n   " if scalar(@objLocPairs) > 1;
		$sug .= "]\n";
		$sug .= " },";
#		push @addSuggestions, "\"$wid\":{\" to " . $respJsonData->{'type'} . "; matched " . join(', ', @stringsMatched) . ".";
		push @addSuggestions, $sug;
	}
}

sub s4json {
	# Convert a string value so it can be used safely in a JSON file.
	my $s1 = $_[0];
	my $s2 = '';
	my $n = length $s1;
	for (my $i = 0; $i < $n; $i++) {
		my $c = substr($s1,$i,1);
		if ($c eq '"') { $s2 .= '\\"'; }
		elsif ($c eq '\n') { $s2 .= '\\n'; }
		elsif ($c eq '\\') { $s2 .= '\\\\'; }
		else {$s2 .= $c; }
	}
	return $s2;
}

sub loadRaw {
	my $rawFile = $_[0];
	$rawData = '';
	open(my $rawfh, "<:encoding(UTF-8)", $rawFile) || die("can't open $rawFile: $!");
	while ( <$rawfh> ) {
		$rawData .= $_; 
	}
	close $rawfh;
}

sub makeSuggestPage {
   open($fh, ">:encoding(UTF-8)", "suggestions.html");
   print $fh "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n";
   print $fh "<meta charset=\"UTF-8\">\n";
   print $fh "<meta name=\"Author\" content=\"David Welbourn\">\n";
   print $fh "<title>Thinger Suggestions</title>\n";
   print $fh "</head>\n\n";
   print $fh "<body>\n<h1><img src=\"../img/kaclogo.svg\" width=\"50\" height=\"50\" alt=\"\" style=\"vertical-align:middle\"> <span id=\"h1\">Thinger Suggestions</span></h1>\n\n";

   print $fh "<p>";
   print $fh "</p>\n\n";
   
   # reportRawFiles(); # Comment out this DEBUG function when no longer needed.
   reportSearchMissing();
   reportAddSuggestions();

   doFooter();
   print $fh "</body>\n</html>\n";
   close $fh;
}

sub reportRawFiles {
	# This debug function is just a quick and dirty way for me to make sure that @rawFiles is initialized correctly.
	print $fh "<h2>Raw files</h2>\n<ul>\n";
	foreach my $f (@rawFiles) {
		print $fh "<li>$f</li>\n";
	}
	print $fh "</ul>\n\n";
}

sub reportSearchMissing {
	print $fh "<h2>Search field missing errors</h2>\n";
	print $fh "<p>The following files didn't define any search field, so we skipped searching any suggestions for them:</p>\n";
	print $fh "<ul>\n";
	foreach my $s (@searchMissing) {
		print $fh "<li>$s</li>\n";
	}
	print $fh "</ul>\n";
}

sub reportAddSuggestions {
	print $fh "<h2>Search matches</h2>\n";
	print $fh "<p>Consider adding these works to Thinger datafiles, based on the search terms found in their respective walkthrough files:</p>\n";
	print $fh "<ul>\n";
	foreach my $s (@addSuggestions) {
		print $fh "<li><pre>$s</pre></li>\n";
	}
	print $fh "</ul>\n";
}

sub getCurrentYear {
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   return 1900 + $year;
}

sub doFooter {
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   my $dd = substr('0' . $mday,-2);
   my $mmm = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[$mon];
   my $yyyy = 1900 + $year;
   print $fh "<footer>\n";
   print $fh "<hr>\n";
   print $fh "<p style=\"font-size:small;text-align:center;\">\n";
   print $fh " <a href=\"html\\index.html\">IF Things</a>\n";
   print $fh " <span style=\"float:left;\">Last updated: $dd $mmm $yyyy</span>\n";
   print $fh " <span style=\"float:right;\">davidwelbourn(a)hotmail*com</span>\n";
   print $fh "</p>\n";
   print $fh "</footer>\n";
}