#!/usr/bin/perl 
# vim:ts=4

# Copyright (C) 2010 Paula Keezer.  

use strict;
use Getopt::Long;
use Term::ReadKey;
use Acme::AGMorse qw(SetMorseVals SendMorseChr SendMorseMsg);

##########################################
#  Command line defaults #################
##########################################
my $DefaultInputFile = '<&STDIN';
my $DefaultOutputFile = '>&STDOUT';
my $Verbose = "";
my $Monitor = 0;


##########################################
#  Global strings (yuk) ##################
##########################################
my $InputFile;
my $OutputFile;
my $Speed=20;			   	# Size of speed to use;
my $Weight=30;				# Weight
my $WordSource;				# Words to be suffix transformed
my $MaxMnt=30;		   		# Maximum number of minutes to run
my $Tone = 700;				# Frequency in Hz of Monitor tone
my $Duration = 5;			# default duration is 5 minutes
my $help;
my $cl;						# current line 
my $cf=__FILE__;			# current file
my $mySTDIN;

##########################################
#  function prototypes ###################
##########################################
sub ExitNice;
sub loadWordSource;			#Words to be suffix transformed;
sub transformWordHashKeys;	#OwnerIncludesMinusExcludes; 
sub transformSelectWord;	#make a string of a particular suffix
sub openoutputCW;
sub closeoutputCW;
sub outputCW;		#;
sub inpWaiting; 
sub openSTDIN; 
sub closeSTDIN;
##########################################
#  body  #################################
##########################################

##########################################
#  Command argument parsing  #############
##########################################
if (&GetOptions("v:s" => \$Verbose
    , m => \$Monitor
    , "i:s" => \$DefaultInputFile
    , "o:s" => \$DefaultOutputFile
    , "s:s" => \$Speed				# speed 10 to 40 wpm 
    , "w:s" => \$Weight				# msec between retrys
    , "t:s" => \$Tone				# Frequency in Hz for monitor tone
    , "r:s" => \$WordSource			# Word Source file
	, "d:s" => \$Duration			# Duration in minutes
    , "l:s" => \$MaxMnt				# Maximum number of minutes run 
    , h => \$help)) {
	if ($InputFile eq "") {$InputFile = $DefaultInputFile;}
	if ($OutputFile eq "") {$OutputFile = $DefaultOutputFile;}
	if ($Verbose =~ /0,/) {
		print STDERR ("arguments: \n",
				"Verbose = ", $Verbose,"\n",
				"Monitor = ", $Monitor,"\n", 
				"InputFile = ", $InputFile,"\n",
				"OutputFile = ", $OutputFile,"\n",
                "Speede= ", $Speed,"\n",
                "Weight= ", $Weight,"\n",
                "Tone= ", $Tone,"\n",
                "WordSource= ", $WordSource, "\n",
                "MaxMnt= ", $MaxMnt,
				"Help = ", $help,"\n");
	}
	if ($help) {

###################################
# help text section ###############
###################################

print STDERR <<EOF;
Name
	$cf - A template for a perl program

Synopsis
	$cf  [-v][-m][-i <filename>][-o <filename>][-u <userlistfile>][-e
    <userexcludelist>][-d <yyyymmdd>][-v virtmapping>[-h]

Description
	This provides a perl program template for:
	Command line parameter - verbose messages, monitoring messages, input/output file specifications and help
	Program blocks for -  command line parsing, help area text information, main processing loop and sub routines
Options
	-v	verbose messages to STDERR, following parameters are valid
        and can be strung together with ','
        0:  
        1:  
        2:  
        3: display  word hash after loading word file 
        4: 
        5:  
        6: 
        7: dispaly word array after moving from hash 
        8: 
        9: display selected word after random selection
        example usage: $cf [other commands] -v 0,6
	-i <file> User specified input file default is <stdin> (not used) 
	-o <file> User specified output file default is <stdout> (not used)
    -s Speed  default 20 wpm
    -w Weight of the dah element, default 30 (3 dit lengths)
    -t Tone   default 700 Hz
    -r WordSource
	-d Duration in minutes to run Jeapordy
    -l MaxMnt
	-h	This help text

Files
	output file specified by user or STDOUT as default.  Useful if you wat
	to store your word statistics in comma delimited format (import into
	excel for data analysis
	-e Word File:  Each line is considered a unique word.  You could load
	with most common words, or a large number of call signes or even complete
	sentences to build a unique Morse Code drill.  Must be either in the
	directory that contains MorseJeapordy or a fully qualified path to the file

EOF
	ExitNice;

	}

} else {
	print STDERR ("usage: $cf [-v][-m][-i][-o][-u][-e][-r][-d][-h]\n");
}
print STDERR "$OutputFile\n";
open (OUTPUTFILE, "$OutputFile") or die "Can't open $OutputFile";

#################################################################
# This is where things happen.  Yes, very linear code that uses
# many functions
#################################################################

my %wordHash;                          # read in all the words from the file
                                        
my @keyArray;  						   # array to be used for random word sel


print OUTPUTFILE "Start $cf\n";                                        
loadWordSource ($WordSource,$Verbose);
openSTDIN;
openoutputCW($Speed,$Weight,$Tone);
transformWordHashKeys ($Verbose);
my $myWord = "";
my $endTime = time()/60 +$Duration;


# Where the user interaction occurs

print OUTPUTFILE "word, retrys, cnt, tot retrys, avg retry\n";
for (my $i = 0; $i<10000;$i++) {
	my $true = 1;
	my $cnt = 0;
	$myWord = transformSelectWord ($Verbose);
	while ($true) {
		outputCW ($myWord);
		if (!inpWaiting) {
			$true = 0;
		}
		$cnt++;
	}
	my $t1 = ++$wordHash{$myWord}[0];
	my $t2 = $wordHash{$myWord}[1] += $cnt;
	printf (OUTPUTFILE "%s, %i, %i, %i, %.2f\n",$myWord, $cnt, $t1, $t2, $t2/$t1);
	if ($endTime < time()/60) { 
		ExitNice;
	}
}

print OUTPUTFILE "End $cf\n";

ExitNice;


#########################################
# not so normal clean up exit ###########
#########################################
	
sub ExitNice
{
	if ($Verbose =~ /0,/) { print STDERR "Finished processing \n";}	
	if ($Monitor) { print STDERR "ExitNice\n";}
	if ($InputFile) {
		close (INPUTFILE); 
		print STDERR "$_";
	}
	closeoutputCW;
	closeSTDIN;
	exit ();
}

sub loadWordSource {

my ($WordSource,$Verbose) = @_;
my $True = 1;
my $cl;
my $CountMon = 0;
my $In;						# variable to contain input string

use FileHandle;
if ($WordSource eq "") {
	$cl = __LINE__;
    print STDERR ("$cf #$cl: ", 'Error, no WordSource file',"\n");
    ExitNice; # temp end
}
open (INPUTFILE, "$WordSource") or die "Can't open $WordSource";

########################################
#  Start loop loading word hash loop ###
########################################

while ($True==1) {
	if ($In = <INPUTFILE>) {			#Try to open InputFile 
		chomp($In);						# dump linefeed and spaces
        $In=~s/^\s+//;
        $In=~s/\s+$//;
        if ($Verbose =~ /0(,|$)/) { 	# if we're verbose, tell about us
			$cl = __LINE__;
			print STDERR ("$cf#$cl ", 'INPUTFILE  Opened',"\n");
			print STDERR ("INPUTFILE line \#$CountMon $In\n");
		}
     	if (exists $wordHash{$In}) {}
     	else {
			$wordHash{$In}[0] = 0;
			$wordHash{$In}[1] = 0;
     	}
	}
	else {
		if ($Verbose =~ /0(,|$)/) {
			$cl = __LINE__;
			print STDERR ("$cf #$cl", 'INPUTFILE not Opened',"\n");
	    	ExitNice();			# No InputFile  data. Then 
		}
    	$True = 0;
	}
    $CountMon++;
}
close (INPUTFILE);
if ($Verbose =~ /3(,|$)/) {     # Audit  word hash content to STDERR
    print STDERR "Load WordSource hash Audit\n";
    my $key;
    my $OutString;
    my $num;
    foreach $key (sort keys %wordHash) { 
        $OutString = $key;
        $OutString = join ',',$key,$wordHash{$key}[0];
        $OutString = join ',',$key,$wordHash{$key}[1];
        print STDERR $OutString,"\n";
    }
	print STDERR ("Leaving loadWordSource\n");
}
}

sub transformWordHashKeys {	#OwnerIncludesMinusExcludes; 

my ($Verbose) = @_;    
my $key;
my $i = 0;
foreach $key (sort keys %wordHash) { 
		$keyArray[$i] = $key;
		$i++;
}

########################################################
#  move keys, sorted to a list/array
#######################################################

if ($Verbose =~ /7(,|$)/) {
    print STDERR "Transform key array audit\n";
	for($i = 0; $i <= $#keyArray; $i++) {
		print STDERR "$keyArray[$i] \n";
	}
 }
}

sub transformSelectWord {

my ($Verbose) = @_;
#################################################
# Select a random word
#################################################a

# set up random number
# select word from keyArray
my $wordRand = int rand($#keyArray);

if ($Verbose =~ /9(,|$)/) {
    print STDERR "  audit\n";
	print STDERR "$keyArray[$wordRand] \n";
}
return $keyArray[$wordRand];
}


sub openoutputCW {
	my ($cspeed,$Weight, $Tone) = @_;
	SetMorseVals($cspeed,$Weight,$Tone);
}

sub closeoutputCW {
}

sub outputCW {

my ($OutputWord) = @_;
$OutputWord .= "    ";
SendMorseMsg($OutputWord);
}

sub openSTDIN {
# open keyboard for non blocking keyboard check
	ReadMode 4;
}

sub closeSTDIN { 
# close keyboard for non blocking keyboard check
# bad things happen if you don't do this
	ReadMode 0;
}

sub inpWaiting {
# non blocking keyboard check
if (not defined ($mySTDIN= ReadKey(-1))) {return 1;} 
else {
	if ($mySTDIN eq 'e') {
		closeSTDIN;
		ExitNice ();
	}
	return 0;
 }
}                                                                  


=head1 NAME

MorseJeapordy - listen to the Morse code and hit any key (except 'e') when 
you comprehend the word

=head1 SCRIPT CATEGORIES

Educational

=head1 SYNOPSIS
   
MorseJeapordy.pl [-v][-o <filename>] [-s <speed in wpm>] [-w <dah Weight 20-60][-t <tone in Hz>] [-d <duration in minutes>] -e <path of word file>

At the command line running:
perl MorseJeapordy.pl -s 30 -w 30 -t 600 -d 5 -e 100_Common_Words.txt

will test your ability to hear Morse Code at 30 wpm, a dah weight of 30 (or 3 dit lentghts) a tone of 600Hz, a duration of 5 minutes using a file in your current directory called 100_Common_Words.txt (available from programs like G4FOK Koch morse code)

=head1 DESCRIPTION

Pass the program the Morse Code Speed and Weight along with a file of Words.
The program will start by sending 'paris' twice to calibrate your machine.
Then the program will randomly select a word from the file you provided
and send it through the local sound system.   The program will repeat
the word if you do not hit a key on the keyboard indicating that you 
recognized the word.  (yes you can cheat, but what would be the point?)
One you hit a key, the program will advance to the next random word

=head1 OPTIONS

-s	Speed from 10 wpm to 50 wpm, default 20 wpm
-w	Weight of the 'dah' element from 20 to 60 where 30 is 3 dits long
-t  Tone of Morse Monitor, default 700 Hz
-d  Duration of exercised in minutes
-e	name of file that contains one word per line, any number of lines

=head1 DIAGNOSTICS

-v	verbose messages to STDERR, following parameters are valid
        and can be strung together with ','
        0:  
        1:  
        2:  
        3: display  word hash after loading word file 
        4: 
        5:  
        6: 
        7: dispaly word array after moving from hash 
        8: 
        9: display selected word after random selection  

=head1 REQUIRES

Acme::AGMorse		A Morse Code Module for perl
Audio::Beep         A beep module with freq and duration callable from perl

You must install Audio::Beep and test with the Beep command on the command line before installing Acme::AGMorse. 

You must install Acme::AGMorse before running MorseJeapordy

the -e option and a word file.  
	Each line is considered a unique word.  You could load
	with most common words, or a large number of call signes or even complete
	sentences to build a unique Morse Code drill.  Must be either in the
	directory that contains MorseJeapordy or a fully qualified path to the file

=head1 README

This is a Linux Morse Code game that produces Morse Code sound of random words.
The user is suppose to hit any key on the keyboard when she/he recognizes the woword and the the program will score and move on to the next word.  The command line provides options for speed, dah weight, tone, length of game, and custom word file.  

You must have Audio::Beep installed and running to hear anything
You then must install Acme::AGMorse, it's make test will send a message at
20 wpm.  Once you can hear the test message, you can run MorseJeapordy with
a word file of your choice (G4FOK has a very nice set of files in his Koch
Morse training program)

More details can be found by running MorseJeapordy.pl -h
=head1 AUTHOR

Paula Keezer/NX1P  nx1p @at@ arrl.net

=cut
