#!/usr/bin/perl

# pjcs - 2024-07-12 - revised version

use utf8;
use v5.26;
use warnings;
use HTML::Entities;
use Encode 'decode_utf8';
use URI::Escape;

# in content script
our ($analysis, $array1, $array2, $base, %data, $date, $examples, $f9_can_execute, $heading, $hilit, $id, $name, $next,
	$one, $onex, $output, $prev, $PRODUCTION, $root, $task, $task_no, $try_eval, $try_it, $two, $twox, $week);
	
my (@t, $today10, $timestamp, $log);

# standard start
pwc_start();

# more or main
if ($data{more}) { show_more() }
else             { show_main() }

# -----

sub show_main {

	my ($date2, $db, $e, $err, $next_link, $prev_link, $script, $status, $styles, 
		$task1, $task2, $f91, $f92);
	
	# get week and task_no
	($week, $task_no, $status, $prev, $next) = get_week_data();

	# get content
	if ($status) { require qq[$root/content/content_${week}_$task_no.pl] }
	else         { $err = qq[No content file for week $week task $task_no] }

	# get highlit source script
	if (open HILIT, '<utf8:', qq[$root/hilit/source_${week}_$task_no.pl.html]) {
		undef $/;
		$hilit = <HILIT>;
		close HILIT;
		$/ = qq[\n];
		$hilit =~ m|<style.*?>(.*?)</style>|s;
		$styles = qq[<style>$1</style>];
		$styles =~ s|span .*?\}||s;
		$hilit =~ m|<body>.*<div.*?>(.*?)</div>.*</body>|s;
		$script = qq[<pre class='script'>\n\n$1\n</pre>\n];
	} else {
		$script = qq[No highlit source file for week $week task $task_no];
		$styles = '';
	}

	# heading
	$date =~ m|(\d+)\s+(\w+)\s+(\d+)|;   # 1 January 2024
	$date2 = sprintf('%d %s %d', $1, substr($2, 0, 3), $3);
	
	# page heading
	page_begin({title => 'The Weekly Challenge', description => 'The Weekly Challenge', styles => $styles,
		heading => $heading, line1 => qq[Weekly challenge $week &mdash; $date],
		line2 => qq[Week $week: $date2]});
	# for $e (sort keys %data) { say qq[<br>$e: $data{$e} ] };
	if ($err) {
		say $err;
		page_end();
		exit;
	}

	# skip to try_it
	say qq[<script>
		window.addEventListener('load', scrollTo);
		function scrollTo()
		{ location.hash = '#try_it' }
		</script>] if $data{submit};
		
	# prev & next
	prev_and_next();
	
	# tabs
	$task1 = -e qq[$root/content/content_${week}_1.pl] ? 'Task 1' : '';
	$task2 = -e qq[$root/content/content_${week}_2.pl] ? 'Task 2' : '';
	tabs("$task1|$task2|More", "/challenge/$week/1|/challenge/$week/2|/challenge?more=index", $task_no - 1, 1, 0);

	# non-existent task
	unless ($status) {
		say qq[<p style='text-align:center'>Sorry, I did not submit a solution for this task.];
		page_end();
		exit;
	}

	# left column - task name
	$task =~ s|!!!(.*?)!!!|<pre class='script nobord'>$1</pre>|sg;
	$task =~ s|!!(.*?)!!|<code>$1</code>|sg;
	say qq[<div class='column'>
		<h2>Task \&mdash; $name</h2>
		$task];
		
	# examples
	if ($examples) {
		$examples =~ s|\n(Example\s*\d+)|\n\n<u>$1</u>|g unless $examples =~ m|<u>|;
		$examples =~ s|[\n\s]+Example|\nExample|gs;
		$examples =~ s|\n*$|\n\n|s;
		$examples =~ s|^(\s*)Input([\d\s]*?):|<span class='input'>$1Input$2:</span>|gm;
		$examples =~ s|^(\s*)Out?put([\d\s]*?):|<span class='output'>$1Output$2:</span>|gm;
		say qq[<h2>Examples</h2>
			<pre class='script'>$examples</pre>];
	}

	# analysis
	$analysis =~ s|!!!(.*?)!!!|<pre class='script nobord'>$1</pre>|sg;
	$analysis =~ s|!!(.*?)!!|<code>$1</code>|sg;
	say qq[<h2>Analysis</h2>
		$analysis];

	# try_it
	if ($try_it) {
		
		@t = localtime();
		$timestamp = sprintf('%04d-%02d-%02d %02d:%02d:%02d', 
			$t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]);   # 2020-12-25 09:35:20
		
		say qq[<h2>Try it<a id='try_it'>&nbsp;</a></h2>
			$try_it];
			
		if ($data{submit} and defined($try_eval)) {
			unless ($week == 165) {
				$try_eval =~ s|^\n+|\n|s;
				$try_eval =~ s|\n+$|\n|s;
				$try_eval =~ s|^Input([\d\s]*?):|<span class='input'>Input$1:</span>|gm;
				$try_eval =~ s|^Output([\d\s]*?):|<span class='output'>Output$1:</span>|gm;
				
			} else { # special case for SVG challenges
				unless ($try_eval =~ m|^invalid|) {
					$try_eval = `cat /var/www/pwc/public_html/tmp/temp_165_2_$id.txt`;
					$try_eval .= qq[<img style='height:200px; max-width:95%'
						src='http://campbellsmiths.f9.co.uk:8083/tmp/temp_165_${task_no}_$id.svg'>];
				}
			}
			say qq[<br><b>Your results:</b><br><div class='script'><pre>\n$try_eval</pre></div>] if $try_eval;

			# record usage
			if ($PRODUCTION) {
				$two = $two ? qq[ p2=($two)] : '';
				$log = uri_escape(qq[$timestamp ch=$week:$task_no url=$ENV{REMOTE_ADDR} p1=($one)$two]);
				`curl -s http://campbellsmiths.f9.co.uk:8083/perl/log_tryit.pl?z=$log`;
			}
		}
	}
	say qq[<p></div> <!-- # end of left column -->\n];

	# right column - script
	say qq[<div class='column'>	

		<!-- script -->
		<div style='float:left'><h2>Script</h2></div>

		<button class='button' 
		onclick="window.open('$base/challenge/source/$week/$task_no', '_blank',
		'top=100,left=100,width=1000,height=800,titlebar=0,toolbar=0')">
		<b>⤡</b></button>
		
		<div style='clear:both'></div>
		$script];
		
	# output
	$output =~ s|^Input([\d\s]*?):|<span class='input'>Input$1:</span>|gm;
	$output =~ s|^Output([\d\s]*?):|<span class='output'>Output$1:</span>|gm;
	say qq[<h2>Output</h2>$output];

	say qq[<p></div> <!-- column -->];
	page_end();
}

sub show_more {
	
	my ($FILE, $date, $line, $result, $task1, $task2, $f91, $f92, $week, $this_week, %found, $sub1, $sub2, $x, 
		$priv, $ln1, $ln2, $tloc);
	
	# get latest week
	open $FILE, '<utf8:', qq[$root/data/index.dat];
	$line = <$FILE>;
	close $FILE;
	$line =~ m|¦(\d\d\d)¦|;
	$this_week = $1;
	$priv = -e './check_all';
		
	# page heading
	page_begin({title => 'The Weekly Challenge', description => 'The Weekly Challenge', styles => '',
		heading => qq[The Weekly Challenge<br>Week $this_week], line1 => 'More information'});
		
	# tabs
	$task1 = -e qq[$root/content/content_${this_week}_1.pl] ? 'Task 1' : '';
	$task2 = -e qq[$root/content/content_${this_week}_2.pl] ? 'Task 2' : '';
	tabs("$task1|$task2|More", "/challenge/$this_week/1|/challenge/$this_week/2|/challenge?more=1", 2, 1, 0);
		
	# top stuff
	say qq[<div style='margin:0 1em 0 1em'>
		<div style='float:left; width:50%; min-width:300px;'>
		<h2>The Weekly Challenge</h2>
		<ul><li><a href='/challenge?more=what'>What&rsquo;s this all about?</a>
		<li><a href='/challenge?more=who'>Who is Peter?</a>
		<li><a href='/challenge?more=index'>Can I see your past solutions?</a>
		<li><a href='/challenge?more=use'>Can I use your code?</a>
		<li><a href='/challenge?more=contact'>Can I contact you?</a>
		<li><a href='/challenge?more=how'>How does this site work?</a>
		</ul>
		</div>
		<div style='float:left; width:50%; min-width:300px;'>
		<h2>Perl</h2>
		<ul><li><a href='https://perl.org/' target='_blank'>Perl home page</a> &ndash; all about the language
		<li><a href='https://perldoc.perl.org/' target='_blank'>Perldoc</a> &ndash; detailed Perl documentation
		<li><a href='https://www.cpan.org/' target='_blank'>CPAN</a> &ndash; library of useful Perl modules
		</ul></div><div style='clear:both'></div>];
		
	say qq[<hr>];
	
	$data{more} = 'index' unless $data{more} =~ m|^[a-z]|;
	if ($data{more} eq 'index') {
		
		# display
		$data{find} = '' unless $data{find};
		say qq[<h2>Peter's solutions</h2>
			<p><form action='?' method='post' style='margin:auto;display:block'>
			<input name='find' value='$data{find}' style='width:30em;max-width:95%'>
			<input type='hidden' name='more' value='1'>
			<input name='submit' type='submit' value='🔎'></form>];

		# search entries
		if ($data{find}) {
			$data{find} =~ s|'||g;
			$result = `grep -l -i '$data{find}' $root/content/*.pl`;
			while ($result =~ m|(\d\d\d)_(\d)|g) {
				$found{$1}{$2} = 1;
			}
		}

		# show results or all
		say qq[<p><table class='bord' style='max-width:700px'>
			<tr><th style='width:15%' >&nbsp;Date</th>		
			<th style='width:37%'>Task 1</th>
			<th style='width:37%'>Task 2</th>
			<th style='width:11%'>Week</th></tr>];

		open $FILE, '<utf8:', qq[$root/data/index.dat] or say qq[could not open $root/data/index.dat];
		$tloc = 0;
		while ($line = <$FILE>) {
			($date, $week, $task1, $task2, $x, $x, $sub1, $sub2, $f91, $f92, $ln1, $ln2) = split('¦', $line);
			$sub1 = $sub2 = 'Y' if $week == $this_week;
			$task1 .= ' *' if ($task1 and $sub1 ne 'Y');
			$task2 .= ' *' if ($task2 and $sub2 ne 'Y');
			$task1 .= ' &rarr;' if ($task1 and $f91 ne 'Y' and $priv);
			$task2 .= ' &rarr;' if ($task2 and $f92 ne 'Y' and $priv);
			if ($data{find}) {
				next unless defined $found{$week};
				$task1 = "<b>$task1</b>" if $found{$week}{1};			
				$task2 = "<b>$task2</b>" if $found{$week}{2};			
			}
			$date =~ m|\d\d(\d\d).(\d+).(\d+)|;
			$date = qq[$3.$2.$1];
			say qq[<tr><td>&nbsp;$date</td>
				<td><p class='tasks'><a class='task-link' href='$base/challenge/$week/1'>
					$task1</a> <span style='color:#096'>[$ln1]</span></td>
				<td><p class='tasks'><a class='task-link' href='$base/challenge/$week/2'>
					$task2</a> <span style='color:#096'>[$ln2]</span></td>
				<td><a href='https://theweeklychallenge.org/blog/perl-weekly-challenge-$week/' 
					target='_blank'>&nbsp;$week</a></td></tr>];
			if ($week == 233) {
				say qq[<tr><td>&nbsp;28.08.23</td>
					<td colspan='3' style='text-align:center'><i>No challenge was set for this week</i></td></tr>];
			}
			$tloc += $ln1 + $ln2;
		}
		say qq[</table>];
		close $FILE;
		
		say qq[<table class='nobord' style='width:400px'>
			<tr><td>*</td><td>solved after the closing date</td></tr>
			<tr><td><span style='color:#096'>[nn]</span></td><td>lines of code for task</td></tr>
			<tr><td>$tloc</td><td>total lines of code</td></tr>];
		say qq[<tr><td>&rarr;</td><td>try-it uses web service on Joppa</td></tr>] if $priv;
		say qq[</table>];
		
		say qq[</div> <!-- page margins --><!-- wide -->];

	} else {
		
		# text page
		open $FILE, '<:utf8:', qq[$root/more/$data{more}.htm];
		undef $/;
		$result = <$FILE>;
		close $FILE;
		$result =~ s|<base>|$base|egs;
		say qq[$result</div>]; <!-- page margins -->
	}
	
	page_end();
}

sub get_week_data {
	
	my ($FILE, $line, @vals, $task_no, $week, $prev, $next);
	
	# week and task_no
	$week = defined $data{week} ? $data{week} : 0;
	$task_no = defined($data{task_no}) ? $data{task_no} : 1;
	
	# get prev and next
	open $FILE, '<utf8:', qq[$root/data/index.dat];
	while ($line = <$FILE>) {
		@vals = split(/¦/, $line);
		
		# default to latest week
		$week = $vals[1] unless $week;
		if ($week == $vals[1]) {
			$task_no = 2 unless $task_no;
			$prev = $vals[4] + 0;
			$next = $vals[5] + 0;
			last;
		}
	}
	close $FILE;
	
	# return week, task, status
	return ($week, $task_no, -e qq[$root/content/content_${week}_$task_no.pl], $prev, $next);	
}

sub page_begin {   # standard page header

	# print page header
	my $k;
	my %params = %{$_[0]};
	for $k (qw[styles heading line1 line2]) {
		$params{$k} = '' unless $params{$k};
	}
	say qq[Content-type: text/html; charset=UTF-8

<!doctype html>
<html lang='en-GB'>
<head>
	<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>
	<meta name='robots' content='index,follow'>
	<meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=yes'>

	<title>The Weekly Challenge</title>
	<meta name='description' content="Peter's submissions to The Weekly Challenge">

	<link rel="icon" type="image/png" href="$base/favicons/favicon-96x96.png" sizes="96x96">
	<link rel="icon" type="image/svg+xml" href="$base/favicons/favicon.svg">
	<link rel="shortcut icon" href="$base/favicons/favicon.ico">
	<link rel="apple-touch-icon" sizes="180x180" href="$base/favicons/apple-touch-icon.png">
	<link rel="manifest" href="$base/favicons/site.webmanifest">

	<!-- google fonts -->
	<link rel='preconnect' href='https://fonts.gstatic.com'>
	<link href='https://fonts.googleapis.com/css2?family=Open+Sans&family=Roboto+Slab' rel='stylesheet'>

	<!-- my stylesheet -->
	<link rel='stylesheet' href='$base/css/pwc_21.css' type='text/css'>

	<!-- styles from Notepad++ formatted html (used in Script section -->
	$params{styles}
</head>

<body style='width:100%'>
	<div id='frame_outer'>
		<div id='frame_inner' style='background:white'>
			<div id='heading' style='height:auto'>
				<div id='icon-div'>
					<img class='pjcs' src='$base/favicons/favicon.svg' alt='Camel'>
				</div> <!-- icon-div -->
				<div id='img-div'>
					<img class='pjcs' src='$base/images/peter.png' style='border-radius:50%' alt='Peter'>
					<br><span class='caption'><b>Peter Campbell&nbsp;Smith</b></span>
				</div> <!-- img-div -->
				<div id='title'>
					<p>$params{heading}
				</div> <!-- title -->
				<div id='challenge'>
					<p id='big-date'>$params{line1}
					<p id='small-date'>$params{line2}
				</div> <!-- challenge -->
			</div> <!-- heading -->
	<!-- end of page_begin -->
];
}

sub page_end { 

	# footing
	say qq[<!-- start of page_end -->
		<p>&nbsp;
		<div id='footing'>
		<p style='text-align: center;margin-top:1em'>Any content of this website which has been created by 
		<a class='foot-link' href='http://ccgi.campbellsmiths.force9.co.uk/solutions' target='_blank'>Peter Campbell Smith</a> is in the
		<a class='foot-link' href='https://creativecommons.org/publicdomain/zero/1.0/' target='_blank'>public domain</a>
		</div><!-- footing -->

	</div></div> <!-- frame inner and outer -->
	<script src='$base/scripts/mailmurg.js'></script>
</body></html>];
}

sub pwc_start {

	my ($DEBUG, $HOSTNAME, $hostname, @t);
			
	use Encode;          # various encode/decode functions
	use POSIX qw(tzset); # sets to UK time rather than where the server is
	use open qw(:utf8);  # opens all text files in UTF-8 mode
	use Time::Local;
	$ENV{TZ} = 'Europe/London';
	tzset;

	# environment
	$DEBUG = 0;
	
	# hostname on Linux
	$hostname = `hostname`;
	$hostname =~ s|[\r\n]+$||g;
	$HOSTNAME = $hostname;
	$PRODUCTION = $ENV{HTTP_HOST} !~ m|joppa|;

	# file system root and website base
	if ($PRODUCTION) {
		$root = `pwd`;
		$root =~ s|pwc(.*)|pwc|s;
		$base = '/pwc';
	} else {
		$root = '/var/www/pwc/public_html';
		$base = '';
	}
	
	# make STDOUT and STDIN utf8
	binmode(STDOUT, ':utf8');
	binmode(STDIN, ':utf8');

	# get paramter data
	%data = get_data();
}

sub get_data {   # ()

	my ($item, $j, $param, $query, $save, $value, %params, %result, @list);

	# returns get and post parameters as hash, so $result{key} -> value
	use Encode;
	use URI::Escape;
		
	%params = ();
	for $j (1 .. 2) {
		if ($j == 1) {   # parameters in URI (ie method = get)
			$query = $ENV{QUERY_STRING} . '&';

		} else {         # parameters in form (ie method = post)
			last unless $ENV{REQUEST_METHOD} eq 'POST';
			$save = $/;
			undef $/;
			$params{post_body} = <STDIN>;
			$query .= $params{post_body};
			$query =~ s|\+| |g;
			$/ = $save;
		}

		if ($query) {
			@list = split /&/, $query;

			# must split before unescaping in case there's a '=' escaped
			foreach $item (@list) {
				($param, $value) = split(/=/, $item, 2);
				if ($param) {
					$param = uri_unescape($param);
					$value = uri_unescape($value);
					utf8::decode($param);
					utf8::decode($value);
					$params{$param} = $value if $param;
				}
			}
		}
	}
	return %params;
}

sub tabs {  # captions, screens, selected, can_switch, can_select

	my ($c, $can_select, $can_switch, $i, $num_tabs, $selected, $t, $w_spc, $w_tab, @captions, @screens);

# captions = pipe-separated list of tab captions - eg one|two|three
# screens =  pipe-separated links for the captions - eg one.pl?param=1|two.pl?param=1|two.pl?param=3
# selected = 0-based index of selected tab - eg 0 is the first one
# can_switch = true if the unselected tabs can be activated, false if not
# can_select = true if the tab caption is still a link when the tab is selected (default is false)

	@captions = split /\|/, $_[0];
	@screens = split /\|/, $_[1];
	$selected = $_[2];
	$can_switch = $_[3];
	$can_select = $_[4];
	$num_tabs = scalar @captions;
	$w_tab = qq[calc(90% / $num_tabs - 2px)];
	$w_spc = qq[calc(5% - ${num_tabs}px)];
	print qq[<p><div class='tab_spc' style='width:$w_spc'></div>\n];
	
	# loop over tabs
	for $i (0 .. $num_tabs - 1) {
		if ($i == $selected and not $can_select) {   # selected and unclickable
			$c = 'tab_sel';
			$t = $captions[$i];
		} elsif ($i == $selected) {   # selected and clickable
			$c = 'tab_sel';
			$t = qq[<a href='$screens[$i]'>$captions[$i]</a>];
		} elsif ($can_switch) {   # unselected and clickable
			$c = 'tab_unsel';
			$t = qq[<a href='$screens[$i]'>$captions[$i]</a>];
		} else {   # unselected and disabled
			$c = 'tab_dis';
			$t = $captions[$i];
		}
		print qq[<div class='$c' style='width:$w_tab'>$t</div>\n];
	}
	print qq[<div class='tab_spc' style='width:$w_spc'></div><div style='clear:both; margin-bottom:1em'></div>\n];
}

sub prev_and_next {
	
	my $prev2 = $prev ? qq[&nbsp;&nbsp;&larr; Week $prev] : '';
		
	say qq[<div style='height:1em; width:100%'></div>];
	say qq[<div class='prev'><p><a href='/challenge/$prev'>$prev2</a></div>];
	say qq[<div class='this'><p><a href='/challenge'>Current week &rarr;</a></div>] if $next;
	say qq[<div class='next'><p><a href='/challenge/$next'>Week $next &rarr;&nbsp;&nbsp;</a></div>] if $next;
	say qq[<div style='clear:both'></div>];
}

sub encode_string {

	my $string = shift;
	return '' unless $string;
	$string =~ s|'|&#39;|gs;
	$string =~ s|"|&#34;|gs;
	return $string;
}

sub limit_lines {
	
	my ($text, $max_length, $indent, $folded, $line, @chars, $j, $k, $ch);
	
	# breaks lines after $max_length chars
	# indents all but first line
	$text = $_[0];
	$max_length = $_[1];
	$indent = $_[2];
	$folded = '';
	
	# loop over lines
	W: while ($text =~ m|(.*?)\n|g) {
		$line = $1;
		$line =~ s|&quot;|"|g;
		
		# split line into multiple lines
		@chars = split(//, $line);
		while (1) {
			
			# remaining line shorter than max
			if ($#chars <= $max_length) {
				$folded .= join('', @chars) . qq[\n];
				next W;				
			}
			
			# seek comma
			for ($j = $max_length; $j >= -1; $j --) {
				last if ($j == -1 or $chars[$j] eq ',');
			}
			
			# or blank
			if ($j == -1) {
				for ($j = $max_length; $j >= -1; $j --) {
					last if ($j == -1 or $chars[$j] eq ' ');
				}
			}
			
			# no comma found - output rest of line
			if ($j == -1) {
				$folded .= join('', @chars) . qq[\n];
				next W;
			}
			
			# output chars up to comma
			$folded .= shift @chars for 0 .. $j;
			$folded .= qq[\n];
			
			# add indent and repeat
			shift @chars while $chars[0] eq ' ';
			unshift @chars, ' ' for 1 .. $indent;
		}
	}
	return $folded;
}	

