#!/usr/bin/perl -w

use lib "./lib";		# so Perl can find EvaP
use Getopt::EvaP;
use File::Basename;
use subs qw/ build_game get_choice manage_game_save_states pick_compiler_path play_game init main fini /;
use subs qw/ remove_initial_game_files setup_initial_game_files /;
use vars qw/ @MM %OPT @PDT $pickCompiler /;
use strict;

init;
main;
fini;

sub main {

    $pickCompiler = pick_compiler_path;

    if ( $OPT{ 'build-game' } ) {
	build_game;
    } else {
	play_game;
    }

} # end main

sub init {

@PDT = split /\n/, <<'END';
PDT play-text-adventure-games, platag
    game, g : key Adventure, Qork, keyend = D_PLATAG_G, Qork
    manage-game-save-states, s : switch
    game-save-states-folder, gssf : file = D_PLATAG_GSSF, ~/.TAG
    build-folder, bf : file = D_PLATAG_BF, `pwd`
    build-game, bg : boolean = D_PLATAG_BG, NO
    f77-compiler, fc : key gnu, intel, pgi, keyend = D_PLATAG_FC, gnu
PDTEND no_file_list
END

    @MM = split /\n/, <<"END";

play-text-adventure-games, platag

    Play the games of Adventure and Qork.

    Database files Adventure.text.db and Qork.text.db, as well as the initial game
    save state files Adventure.save and Qork.save are presumed to exist.  Run this
    command with -build-game YES to create them, if you are an Implementer. Use -s
    to manage any Adventure and Qork game save states that you may have.
.game
    Specifies the game you wish to play, Adventure or Qork.
.manage-game-save-states
    Specify -s to manage your saved game states, either to select or remove items.
    When you make a selection from the list of saved states, the game is restored
    from that save state and play is resumed at that point. The default choice is
    the game state from your last SAVE command. Game states you choose to delete
    are removed permanently.
.game-save-states-folder
    Folder containing Adventure and Qork game save state files.
.build-folder
    Pathname to Text Adventure Games distribution and build files.
.build-game
    YES to compile a Text Adventure Game and create the text database and       
    initial save state files.  NO to use them and play.
.f77-compiler
    I have created FORTRAN source that works with the GNU, Intel and PGI compiler
    suites, so make your choice.
END

    EvaP \@PDT, \@MM, \%OPT;	# evaluate parameters

    die "Unknown game '$OPT{ 'game' }', it must be \"Adventure\" or \"Qork\", precisely."
      unless $OPT{ 'game' } =~ m/^Adventure|Qork$/;

} # end init

sub fini {

} # end fini

# Subroutines

sub build_game {

    # Create the game executable and make the first run that creates
    # the initial game save state and random text message database.

    if ( not -s "$OPT{ 'build-folder' }/src/$OPT{ 'game' }.f" or
	 not -s "$OPT{ 'build-folder' }/src/$OPT{ 'game' }.text" ) {
	print "Bah, begone!  You are not an Implementer.\n";
	exit 2
    }

    system <<"ENDofCMNDS";
    echo Building $OPT{ 'game' }.bin, $OPT{ 'game' }.text.db and $OPT{ 'game' }.save.init ...
    FC='none'
    if [ \"$OPT{ 'f77-compiler' }\" = \"gnu\" ] ; then
	FC='gfortran -w -cpp'
    fi
    if [ \"$OPT{ 'f77-compiler' }\" = \"intel\" ] ; then
	FC='ifort -fpp'
	echo '*** NOTE: expect several #6371 warning messages.'
    fi
    if [ \"$OPT{ 'f77-compiler' }\" = \"pgi\" ] ; then
	FC='pgf77 -Mpreprocess -D__PGI_COMPILER'
    fi
    if [ \"\$FC\" = \"none\" ] ; then
        echo Logic error, no FORTRAN 77 compiler found.
        exit 5
    fi
    $pickCompiler
    \$FC -o $OPT{ 'build-folder' }/bin/$OPT{ 'game' }.bin $OPT{ 'build-folder' }/src/$OPT{ 'game' }.f
    stat=\$?
    if [ \$stat = 0 ] ; then
	/bin/rm $OPT{ 'build-folder' }/etc/$OPT{ 'game' }.save.init 2> /dev/null
	/bin/rm $OPT{ 'build-folder' }/etc/$OPT{ 'game' }.text.db   2> /dev/null
	BUILD_$OPT{ 'game' }=1 $OPT{ 'build-folder' }/bin/$OPT{ 'game' }.bin
    else
	echo $OPT{ 'game' } build failed due to compiler errors.
    fi
ENDofCMNDS

} # end build_game

sub get_choice { # return integer choice

    my $command       = shift;
    my $defaultChoice = shift;
    my $maxChoice     = shift;

    my $ans = -1;
  CHOICE:
    while ( 1 ) {
	print "\nPlease choose which $OPT{ 'game' } game save state to $command, 0 - $maxChoice, or 'q' to quit", ($defaultChoice == -1 ? '' : " [$defaultChoice]"), ">";
	$ans = <>;
	chomp $ans;
	exit 1 if $ans =~ m/^q$/i or $ans =~ m/^quit$/i;
	if ( $ans =~ m/^$/ and $defaultChoice != -1 ) {
	    $ans = $defaultChoice;
	    last CHOICE;
	}
	if ( $ans =~ m/^\d+$/ and $ans >= 0 and $ans <= $maxChoice ) {
	    last CHOICE;
	}
    } # whilend get command answer

    return $ans;

} # end get_choice

sub manage_game_save_states {

    my( @gss ) = `ls -t1 $OPT{ 'game-save-states-folder' }/$OPT{ 'game' }.save-* 2> /dev/null`;
    return if $#gss == -1;

    chomp @gss;
    my( @human_gss );
    my $default = '';
    foreach my $g ( @gss ) {
	my $b = basename $g;
	if ( $b =~ m/^$OPT{ 'game' }.save-.*$/ ) {
	    push @human_gss, $b;
	    system "/usr/bin/cmp " . dirname($g) . "/$OPT{ 'game' }.save $g > /dev/null";
	    $default = $b if $? == 0;
	}
    }

  MANAGE:
    while ( 1 ) { # restore or delete?

	print "\nI see ", ($#human_gss == 0 ? 'one' : 'various'), " $OPT{ 'game' } game save state file", ($#human_gss == 0 ? '' : 's'), " that you have created, which you may restore\n";
	print "and continue game play, or delete permanently.\n\n";
	print " GSS            When       Score   Moves\n";
	print "----   ------------------- -----  ------\n";
	my $o = 0;
	my $defaultChoice = -1;
	my $ans = '';
	foreach my $g ( @human_gss ) {
	    my( $dt, $tm, $sc, $mv ) = $g =~ m/^$OPT{ 'game' }.save-(\d\d\d\d\.\d\d\.\d\d)-(\d\d:\d\d:\d\d)-(\d\d\d)-(\d\d\d\d\d\d\d)$/;
	    printf( "%4d   %s %s %5d %7d%s\n", $o, $dt, $tm, $sc, $mv, ($g eq $default ? " (current)" : '') );
	    $defaultChoice = $o if $g eq $default;
	    $o++;
	}
	$o--;

	print "\nRestore ('r') or delete('d') a game save state, 'n' to begin a new game, or 'q' to quit [r]>";
	$ans = <>;
	chomp $ans;
	exit 1 if $ans =~ m/^q$/i or $ans =~ m/^quit$/i;
	if ( $ans =~ m/^r$/i or $ans =~ m/^restore$/i or $ans =~ m/^$/ ) {
	    $ans = get_choice "restore", $defaultChoice, $o;
	    my $qs = dirname($gss[$ans]) . "/$OPT{ 'game' }.save";
	    system "/bin/cp $gss[$ans] $qs";
	    print "Failed restoring game save state: $?\n" if $?;
	    print "\n";
	    last MANAGE;
	} elsif ( $ans =~ m/^d$/i or $ans =~ m/^delete$/i ) {
	    $ans = get_choice "delete", $defaultChoice, $o;
	    system "/bin/rm $gss[$ans]";
	    print "Failed deleting game save state: $?\n" if $?;
	    splice @gss,       $ans, 1;
	    splice @human_gss, $ans, 1;
	    last MANAGE if $#human_gss == -1;
	    print "\n";
	    next MANAGE
	} elsif ( $ans =~ m/^n$/i or $ans =~ m/^new$/i ) {
	    remove_initial_game_files;
	    setup_initial_game_files;
	    last MANAGE;
	} else {
	    print "\n##### Unknown command '$ans', only 'r', 'd', 'n' and 'q' available. #####\n";
	    next MANAGE;
	} # ifend restore or delete
	
    } # whilend MANAGE restore or delete?

} # end manage_game_save_states

sub play_game {

    # Play a Text Adventure Game.  Possibly show our player a list of saved
    # games states and allow them to resume play from any one of them. For
    # OS X I have several pre-built static game executables to choose from.

    setup_initial_game_files;

    if ( $OPT{ 'manage-game-save-states' } ) {
        manage_game_save_states;
    }

    system <<"ENDofCMNDS";
    $pickCompiler
    if [ -h $OPT{ 'build-folder' }/bin/$OPT{ 'game' }.bin  ] || [ ! -f $OPT{ 'build-folder' }/bin/$OPT{ 'game' }.bin ]  ; then
        if [ -e /usr/bin/sw_vers ] ; then             # if OS X
            cd $OPT{ 'build-folder' }/bin
            v=`/usr/bin/sw_vers -productVersion`      # 10.x.y
            v=`/bin/echo \$v | /usr/bin/cut -d. -f 2` # x
            oslvl='10.6'                              # assume 10.7 and older
            /usr/bin/perl -e '\$s = 0; \$s = 1 if '\$v' > 7 ; exit \$s'
            if [ \$? = 1 ] ; then
                oslvl='10.8'                          # but is 10.8 and newer
            fi
            /bin/ln -fs osx-static/$OPT{ 'game' }-\$oslvl.bin $OPT{ 'game' }.bin
        fi
    fi
    cd $OPT{ 'game-save-states-folder' }
    BUILD_$OPT{ 'game' }=0 $OPT{ 'build-folder' }/bin/$OPT{ 'game' }.bin
ENDofCMNDS

} # end play_game

sub pick_compiler_path {

    # Define a shell command that will be inserted before invoking a game. Here we
    # use "module load", but a script that sets bin and library paths will suffice.

    if ( $OPT{ 'f77-compiler' } eq 'gnu' ) {
        return 'module unload pgi/15.1 2> /dev/null ; module unload intel/15.0.2 2> /dev/null ; module load gcc/4.8.2 2> /dev/null';
    } elsif ( $OPT{ 'f77-compiler' } eq 'intel' ) {
	return 'module unload pgi/15.1 2> /dev/null ; module unload gcc/4.8.2 2> /dev/null ; module load intel/15.0.2 2> /dev/null';
    } elsif ( $OPT{ 'f77-compiler' } eq 'pgi' ) {
	return 'module unload intel/15.0.2 2> /dev/null ; module unload gcc/4.8.2 2> /dev/null ; module load pgi/15.1 2> /dev/null';
    } else {
        die "Logic error, no FORTRAN 77 compiler found.";
    }

} # end pick_compiler_path

sub remove_initial_game_files {

    system <<"ENDofCMNDS";
    /bin/mkdir $OPT{ 'game-save-states-folder' } 2> /dev/null
    cd $OPT{ 'game-save-states-folder' }
    /bin/rm $OPT{ 'game' }.save
    /bin/rm $OPT{ 'game' }.text.db
ENDofCMNDS

} # end remove_initial_game_files

sub setup_initial_game_files {

    system <<"ENDofCMNDS";
    /bin/mkdir $OPT{ 'game-save-states-folder' } 2> /dev/null
    cd $OPT{ 'game-save-states-folder' }
    err=0
    if [ ! -s "$OPT{ 'game' }.save" ] ; then
	if [ ! -e "$OPT{ 'build-folder' }/etc/$OPT{ 'game' }.save.init" ] ; then
            echo "An Implementer is required to build $OPT{ 'game' }.save.init"
            err=\$(( err + 1 ))
        else
	    /bin/cp $OPT{ 'build-folder' }/etc/$OPT{ 'game' }.save.init $OPT{ 'game' }.save
        fi
    fi
    if [ ! -s "$OPT{ 'game' }.text.db" ] ; then
	if [ ! -e "$OPT{ 'build-folder' }/etc/$OPT{ 'game' }.text.db" ] ; then
            echo "An Implementer is required to build $OPT{ 'game' }.text.db"
            err=\$(( err + 1 ))
        else
            /bin/cp $OPT{ 'build-folder' }/etc/$OPT{ 'game' }.text.db $OPT{ 'game' }.text.db
        fi
    fi
    if [ \$err != 0 ] ; then
        exit 5
    fi
ENDofCMNDS
    die "$OPT{ 'game' } setup errors, the Dungeon is closed." if $?;

} # end setup_initial_game_files

