#!/usr/bin/perl

# vtm-cgi.pl -- the WWW interface for the Virtual Turing Machine 2.
# Copyright (C) 2001 Paul R. C. Ming
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

# USER CONFIGURABLE SECTION ###################################################

# Modify this to the path to the script in the URL.
$CGI_URL = "/~prcm/turing/vtm-cgi.pl";

# END OF USER CONFIGURABLE SECTION ############################################

use VTM;

$VTM_HOMEPAGE = "http://www.nmt.edu/~prcm/turing/";

$version = '2.02';

$OUTPUT_RESULT    = 0;
$OUTPUT_1LINE     = 1;
$OUTPUT_2LINES    = 2;

$SHOWSTATE_NONE   = 0;
$SHOWSTATE_BEFORE = 1;
$SHOWSTATE_AFTER  = 2;

$SHOWFORM_NONE   = 0;
$SHOWFORM_BEFORE = 1;
$SHOWFORM_AFTER  = 2;

print "Content-type: text/html\n";
print "Content-disposition: filename=\"turing.html\"\n\n";

unless (&cgi_get_input(\%input)) {
    # Set default value.
    $input{'output'} = $OUTPUT_2LINES;
    $input{'showstate'} = $SHOWSTATE_AFTER;
    $input{'showform'} = $SHOWFORM_AFTER;
    &print_form;
    exit;
}

if ($input{'command'} eq "save") {
    &print_form;
    exit;
}

&fix("tape") unless &VTM::valid_tape($input{'tape'});
&fix("blank") unless &VTM::valid_cell_value($input{'blank'});
&fix("initial_state") unless &VTM::valid_state_name($input{'initial_state'});
&fix("output_options") unless (($input{'output'} == $OUTPUT_RESULT
				|| $input{'output'} == $OUTPUT_1LINE
				|| $input{'output'} == $OUTPUT_2LINES)
			       && ($input{'showstate'} == $SHOWSTATE_NONE
				   || $input{'showstate'} == $SHOWSTATE_BEFORE
				   || $input{'showstate'} == $SHOWSTATE_AFTER)
			       && ($input{'showform'} == $SHOWFORM_NONE
				   || $input{'showform'} == $SHOWFORM_BEFORE
				   || $input{'showform'} == $SHOWFORM_AFTER));

if ($fix_required) {
    &print_form;
    exit;
}

$Machine = new VTM() || die "Couldn't create a VTM: $!";
$Machine->infinite_tape(0);    # Turn infinite tape off.

# Convert to UNIX style new lines.
if ($input{'instructions'} =~ /\r\n/) {
    $input{'instructions'} =~ s/\r//g;
}
elsif ($input{'instructions'} =~ /\r/) {
    $input{'instructions'} =~ s/\r/\n/g;
}

$line_no = 1;
foreach $line (split(/\n/, $input{'instructions'})) {
    $ret_val = $Machine->parse_instruction($line);
    if ($ret_val == 0) {
	push(@instruction_errors, "$line_no: error");
	&fix("instructions");
    }
    elsif ($ret_val == 2) {
	push(@instruction_errors, "$line_no: instruction for initial state"
	     . " and cell already defined");
    }
    $line_no++;
}

if ($fix_required) {
    &print_form;
    exit;
}

$Machine->tape($input{'tape'});
$Machine->blank($input{'blank'});
$Machine->start_state($input{'initial_state'});
$Machine->half_step(1) if $input{'output'} == $OUTPUT_2LINES;

print <<EOT;
<HTML>
<HEAD><TITLE>Virtual Turing Machine $version</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF">
<A HREF="$VTM_HOMEPAGE">Virtual Turing Machine $version Homepage</A>
EOT

&print_html_form if $input{'showform'} == $SHOWFORM_BEFORE;

$Machine->start() || die "Couldn't start the Turing machine";
print "<PRE>";
do {
    &print_line if $input{'output'} != $OUTPUT_RESULT;
    $status = $Machine->step();
} while (!$Machine->is_halted() && !$Machine->is_in_infinite_loop());
&print_line if $input{'output'} == $OUTPUT_RESULT;

if ($Machine->is_in_infinite_loop()) {
    &print_line if $input{'output'} != $OUTPUT_RESULT;
    print "Infinite loop detected.\n";
}
print "</PRE>";

&print_html_form if $input{'showform'} == $SHOWFORM_AFTER;

print <<EOT;
</BODY>
</HTML>
EOT

exit;


###############################################################################


sub print_form {
    print <<EOT;
<HTML>
<HEAD><TITLE>Virtual Turing Machine $version</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF">
<A HREF="$VTM_HOMEPAGE">Virtual Turing Machine $version Homepage</A>
<P>
EOT
    &print_html_form;
    print <<EOT;
</BODY>
</HTML>
EOT
    return;
}




sub print_html_form {
    my (%output, %showstate, %showform);
    $output{$input{'output'}} = ' CHECKED';
    $showstate{$input{'showstate'}} = ' CHECKED';
    $showform{$input{'showform'}} = ' CHECKED';
    print <<EOT;
<FORM ACTION="$CGI_URL" METHOD="post">
<UL>
<LI>$s{'tape'}The Tape:$e{'tape'}<BR>
<INPUT TYPE="text" NAME="tape" SIZE="60" VALUE="$input{'tape'}">
<LI>$s{'blank'}Blank character:$e{'blank'}<BR>
<INPUT TYPE="text" NAME="blank" SIZE="1" VALUE="$input{'blank'}">
<LI>$s{'initial_state'}Initial state:$e{'initial_state'}<BR>
<INPUT TYPE="text" NAME="initial_state" SIZE="10" VALUE="$input{'initial_state'}">
<LI>$s{'instructions'}Instructions:$e{'instructions'}<BR>
<TEXTAREA NAME="instructions" ROWS="20" COLS="60">$input{'instructions'}</TEXTAREA>
EOT

    if (@instruction_errors) {
	print "<BR><TT>";
	foreach $line (@instruction_errors) {
	    print "$line<BR>";
	}
	print "</TT><BR>";
    }

    print <<EOT;
<LI>I want to:
<UL><LI><INPUT TYPE="radio" NAME="command" VALUE="execute" CHECKED> 
        $s{'output_options'}execute this script and output:$e{'output_options'}
<UL><LI><INPUT TYPE="radio" NAME="output" VALUE="$OUTPUT_RESULT"$output{$OUTPUT_RESULT}> 
        the result of the tape only
<LI><INPUT TYPE="radio" NAME="output" VALUE="$OUTPUT_1LINE"$output{$OUTPUT_1LINE}> 
        one line per step
<LI><INPUT TYPE="radio" NAME="output" VALUE="$OUTPUT_2LINES"$output{$OUTPUT_2LINES}>
        two lines per step<BR>&nbsp;
<LI><INPUT TYPE="radio" NAME="showstate" VALUE="$SHOWSTATE_NONE"$showstate{$SHOWSTATE_NONE}>
        don't show the state
<LI><INPUT TYPE="radio" NAME="showstate" VALUE="$SHOWSTATE_BEFORE"$showstate{$SHOWSTATE_BEFORE}>
        show the state before the tape
<LI><INPUT TYPE="radio" NAME="showstate" VALUE="$SHOWSTATE_AFTER"$showstate{$SHOWSTATE_AFTER}>
        show the state after the tape<BR>&nbsp;
<LI><INPUT TYPE="radio" NAME="showform" VALUE="$SHOWFORM_NONE"$showform{$SHOWFORM_NONE}>
        and don't show the form again
<LI><INPUT TYPE="radio" NAME="showform" VALUE="$SHOWFORM_BEFORE"$showform{$SHOWFORM_BEFORE}>
        and show the form before the results
<LI><INPUT TYPE="radio" NAME="showform" VALUE="$SHOWFORM_AFTER"$showform{$SHOWFORM_AFTER}>
        and show the form after the results
</UL>
<LI><INPUT TYPE="radio" NAME="command" VALUE="save">
        save this script with the values
</UL>
<LI><INPUT TYPE="submit" VALUE="Do it!"> <INPUT TYPE="reset" VALUE="Reset">
</UL>
</UL>
</FORM>
EOT
}



sub cgi_get_input {
    my ($rh_input) = @_;
    my ($query_string, @parameters, $key, $value, $retrieved);
    if ($ENV{'REQUEST_METHOD'} eq 'POST') {
	read (STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
    }
    else {
	$query_string = $ENV{'QUERY_STRING'};
    }
    $query_string =~ tr/+/ /;
    @parameters = split (/&/, $query_string);
    while (($key, $value) = split (/=/, pop (@parameters), 2)) {
	# Convert '%XX' characters.  
	$key =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack ("c", hex ($1))/ge;
	$value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack ("c", hex ($1))/ge;
	$$rh_input{$key} .= "\0" if defined ($input{$key});
	$$rh_input{$key} .= $value;
	$retrieved++;
    }
    
    return $retrieved;
}




sub fix {
    my $f = shift;
    return if exists($s{$f});
    $s{$f} = '<B><FONT COLOR="#FF0000">';
    $e{$f} = '</FONT></B>';
    $fix_required++;
}




sub print_line {
    my @t = $Machine->tape();
    my $p = $Machine->position();
    my $s = $Machine->state() if $input{'showstate'} != $SHOWSTATE_NONE;
    my $i;

    if ($input{'showstate'} == $SHOWSTATE_BEFORE) {
	print " " x (20 - length($s));
	print "$s :";
    }
    for ($i = 0; $i < $p; $i++) {
	print " $t[$i]";
    }
    print "&lt;$t[$p]&gt;";
    for ($i = $p + 1; $i < @t; $i++) {
	print "$t[$i] ";
    }
    print ": $s" if $input{'showstate'} == $SHOWSTATE_AFTER;
    print "\n";
}
