#!/usr/bin/perl
#*****************************************************************************
#  Tangobulario (a program to practice your vocabulary of foreign words)
#
# Copyright (c) 2008-2009 Nancho Alvarez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see <http://www.gnu.org/licenses/>.

#******************************************************************************

$file="palabras.txt";
$repaso_extension=".review";
@sv=(); #secciones validas
$tanda=25;
$escribir_flag="yes";
$listarsecciones="no";
$exponente=1.5;
$reverse="no";
$repaso="no";
&parser;
if ($repaso eq "yes") {@sv=()} #in review-mode disable sections

if (!open (LEER, $file)) {print "Error de lectura: $file - $!\n"; &usage; exit 1}
#open(LEER, $file) || die "Error de lectura: $file\n$!";  
@lines = <LEER>;
close(LEER);

$j=0;
@preguntables=();
@s=(); #todas las secciones
if (@sv==0) {$flag=1} else {$flag=0}
$seccionactual="";
foreach(@lines){
	if (/^\[(.+)\]$/) {
		$seccionactual=$1;
		if ($1 =~ /=/) {print "Warning: section name '$seccionactual' contains the symbol '='\n";}
		push(@s, $seccionactual."\n");
		&activarflag;
	}
	elsif (/=/ and $flag){
		push(@preguntables, $j);
		&subdivide;
	}
	$j++;	
}
if ($listarsecciones eq "yes") {
	foreach(@s){print }
	exit 0;
}
if (@preguntables==0) {die "There are no words\n"}
if ($tanda>@preguntables) {$tanda=@preguntables}


@probabilidades=();
&asignarprobabilidades;

@elegidos=();
if ($repaso eq "no"){
	&elegir;
	foreach(@elegidos){ $veces[$_]++ } #increment only once
}
else {
	&leer_repaso;
	$tanda=@elegidos;
}

$answer="yes";
$preguntar_solo_falladas="no";
@falladas_primera_vez=();
$primera_vez="yes";
while ($answer eq "yes") { #bucle principal mas externo
	$ronda=1;
	if ($preguntar_solo_falladas eq "yes") {
		@lista=@falladas_primera_vez;
		$aciertos=$tanda-@falladas_primera_vez;
	}
	else {
		@lista=@elegidos;
		$aciertos=0
	}
	while($aciertos<$tanda){ #here starts a round
		@falladas=();
		$primera_pregunta="yes";
		while(@lista){ #here starts a question
			$cuantos=@lista;
			if ($primera_pregunta eq "yes" and $cuantos > 1){
				$cuantos--;
				# hack: the first question of each round 
				# should be different from the last failed one 
			}
			$i=int(rand $cuantos);
			$primera_pregunta="no";
			&asignar_idiomas;
			print "$idioma1: "; 
			chomp($respuesta=<>);
			if ($respuesta eq "=" and $escribir_flag eq "yes"){
				&corregir;
			}
			elsif ($respuesta eq "=="){
				$aciertos++;
				splice(@lista, $i, 1);
				print "---> $idioma2\n";
			}
			elsif ($respuesta eq "=exit"){
				&abortar;
			}
			elsif ($respuesta eq $idioma2){
				$aciertos++;
				splice(@lista, $i, 1);
			}
			else {
				print "***** WRONG ******: $idioma2\n";
				$fallos[$lista[$i]]++;
				push(@falladas, $lista[$i]);
				splice(@lista, $i, 1);
			}
		}
		my $f=@falladas;
		print "\n\n--- round: $ronda --- failed: $f ---\n\n\n\n";
		if ($primera_vez eq "yes"){
			@falladas_primera_vez=@falladas;
			$primera_vez="no"
		}
		$ronda++;
		@lista=@falladas;
	}
	$answer=&menu; 
}

#THE END


#####################################################################
sub menu{
	my $repetir_menu="yes";
	$preguntar_solo_falladas="no";
	while ($repetir_menu eq "yes"){
		print "Again (Y/n/x/s/f/h)?: ";
		$respuesta=<>;
		chomp ($respuesta);
		if ($respuesta =~ /^[hH]/) {
			print 	"y - again\n",
				"n - exit (saving statistics and review)\n",
				"x - exit without saving\n",
				"s - save statistics\n",
				"f - ask only failed questions\n",
				"h - this help\n";
			print "read-only mode: <n> and <q> are equivalent, and <s> has no effect\n" 
			unless ($escribir_flag eq "yes");
			print "\n";
		}
		if ($respuesta =~ /^[xX]/) {
			$answer=0; 
			$escribir_flag="no";
			$repetir_menu="no";
		}
		if ($respuesta =~ /^[nN]/) {
			$answer=0;
			$repetir_menu="no";
			if ($escribir_flag eq "yes"){
				&escribir;
				&escribir_repaso unless ($repaso eq "yes");
			}
		}
		if (($respuesta =~ /^[yY]/) || ($respuesta eq "")) {
			$repetir_menu="no";
		}
		if ($respuesta =~ /^[sS]/) {
			if ($escribir_flag eq "yes") {
				&escribir;
				print "statistics saved\n";
			}
			else {print "no effect\n"}
		}
		if ($respuesta =~ /^[fF]/) {
			if (@falladas_primera_vez==0){
				print "no failed answers in the first round\n"
			}
			else{
				$preguntar_solo_falladas="yes";
				$repetir_menu="no"
			}
		}
	}
	return $answer;
}
##################################################
sub abortar{
	#saving in the first round alters statistics, so it is disabled
	if ($primera_vez eq "no") {
		if ($escribir_flag eq "yes"){
			&escribir;
			&escribir_repaso unless ($repaso eq "yes");
		}
	}
	exit;
}
##################################################
sub asignar_idiomas{
	if ($reverse eq "no"){
			$idioma1=$espanol[$lista[$i]];
			$idioma2=$aleman[$lista[$i]];
		}
		else{
			$idioma2=$espanol[$lista[$i]];
			$idioma1=$aleman[$lista[$i]];
		}
}
##################################################
sub corregir{
	print "corregir \"$espanol[$lista[$i]]\": ";
	my $a=<>;
	chomp ($a);
	if ($a eq "") {$a = $espanol[$lista[$i]]}
	print "corregir \"$aleman[$lista[$i]]\": ";
	my $b=<>;
	chomp ($b);
	if ($b eq "") {$b = $aleman[$lista[$i]]}
	print "\"$a=$b\" correcto? (s/n): ";
	my $c=<>;
	chomp ($c);
	if ($c eq "s") {
		$espanol[$lista[$i]]=$a;
		$aleman[$lista[$i]]=$b;
		&escribir;
		print "corregido\!\n";
	}
}
##################################################
sub leer_repaso{
	my $file_repaso=$file . $repaso_extension;
	open(REPASO, $file_repaso) || die "Error de lectura: $file_repaso\n$!";  
	@elegidos = <REPASO>;
	close(REPASO);
	foreach(@elegidos){
		if ($veces[$_] !~ /^[1-9][0-9]*$/){
			die "Incompatibility between files $file and $file_repaso\n"
		}
	}
}
##################################################
sub elegir{
	srand;
	foreach(0..$tanda-1){
		my $t=$probabilidades[0];
		my $r=rand;
		my $i=0;
		while($t<$r){$t=$t+$probabilidades[$i+1]; $i++}
		push(@elegidos, splice(@preguntables, $i, 1));
		my $p=splice(@probabilidades, $i, 1);
		foreach(@probabilidades){
			$_=$_/(1-$p);  #corregimos las probabilidades
		}
	}
}
##################################################
sub asignarprobabilidades{
	my $maximo=1;
	my $numero_de_ceros=0;
	my $suma=0;
	foreach (@preguntables){
		if ($veces[$_]==0){
			push(@probabilidades, -1);
			$numero_de_ceros++;
		}
		else{
			my $aux=&peso($veces[$_],$fallos[$_]);
			push(@probabilidades, $aux);
			if ($maximo<$aux) {$maximo=$aux}
			$suma=$suma+$aux;
		}
	}
	$suma=$suma+$numero_de_ceros*$maximo;
	foreach(@probabilidades){
		$_=($_==-1)? $maximo/$suma: $_/$suma;
	}
}
##################################################
sub peso{
	# Feel free to write your own function.
	# @_[0] is the number of times a word has been selected,
	# @_[1] is the number of failures,
	# $exponente is a global variable than can be used as a parameter.
	# The return should be the relative probability of each word:
	# for example, a word with return value of 3 
	# is 6 times more likely to be chosen
	# than a word with return value 0.5
	((1+@_[1])/@_[0])**$exponente
	#other example: 2**(1+@_[1]/@_[0])-1
	
}
##################################################
sub activarflag{
	if (@sv==0) {return }
	foreach(@sv){
		if ($_ eq $seccionactual) {$flag=1; return}
	}
	$flag=0;
}
################################################
sub subdivide{
	my @four=split /[=\n]/;
	$espanol[$j]=$four[0];
	$aleman[$j]=$four[1];
	$veces[$j]  = ($four[2] eq "")? 0 : $four[2]; 
	$fallos[$j] = ($four[3] eq "")? 0 : $four[3];
}
#######################################################
sub escribir{
	foreach(@elegidos){
		$lines[$_]=$espanol[$_]."=".$aleman[$_]."=". $veces[$_]."=".$fallos[$_]."\n";
	}
	if (open(ESCRIBIR, ">$file")){ 
		print ESCRIBIR @lines;
		close(ESCRIBIR);
	}
	else {print "Error de escritura: ", $file, "\n"; }
}
#####################################################
sub escribir_repaso{
	my $file_repaso=$file . $repaso_extension;
	if (open(REPASO, ">$file_repaso")){ 
		foreach(@elegidos){
			print REPASO $_;
			print REPASO "\n";
		}
		close(REPASO);
	}
	else {print "Error de escritura: ", $file_repaso, "\n"; }
	
}
#####################################################
sub parser{
	my $fichero_flag="no";
	my $seccion_flag="no";
	my $exponente_flag="no";
	foreach(@ARGV){
		if ($_ =~ /^-([0-9]+$)/) {$tanda = $1}
		elsif ($_ eq "-h") {&ayuda }
		elsif ($_ eq "-ro") {$escribir_flag="no"}
		elsif ($_ eq "-l") {$listarsecciones="yes"}
		elsif ($_ eq "-r") {$reverse="yes"}
		elsif ($_ eq "-v") {$repaso="yes"}
		elsif ($_ eq "-s") {$seccion_flag="yes"}
		elsif ($_ eq "-e") {$exponente_flag="yes"}
		else {
			if ($seccion_flag eq "yes"){
				push (@sv, split /,/);
				$seccion_flag="no";
			}
			elsif ($exponente_flag eq "yes"){
				$exponente = $_;
				$exponente_flag = "no";
				die "Error: exponent must be a positive number\n" unless $exponente =~ /^\d*\.?\d+$/ ;
			}
			elsif ($fichero_flag eq "no"){
				$file = $_;
				$fichero_flag="yes"
			}
			else {&usage; exit 1}
		}
	}
	$#ARGV=-1;
}
#####################################################
sub ayuda{
	print "Tangobulario 0.7 Copyright (C) 2008-2009 Nancho Alvarez\n",
	"http://tomasluisdevictoria.org/nancho/tangobulario\n\n",
	"This program comes with ABSOLUTELY NO WARRANTY. ",
	"This is free software, and you are welcome to ",
	"redistribute it under certain conditions. ",
	"For details see <http://www.gnu.org/licenses/>.\n\n";
	print "Tangobulario is a small program to assist you in learning vocabulary of foreign languages. ",
	"It uses a database file which contains ",
	"words and their translation. ",
	"It also mantains a statistic of the number of times a ",
	"word has been asked and failed.\n\n";
	print "Each line of the database is a pair of words (and ",
	"optionally its associated statistics) separated by an '=' sign. ",
	"The file can contain also section names enclosed by square brackets.\n\n";
	&usage;
	print "\nOptions:\n",
	"-# (# is a natural number)\n",
	"\t the number of words to use. Default is 25.\n",
	"-s section1,section2,...\n",
	"\t comma-separated list of sections (no spaces)\n",
	"-e exponent (exponent is a small non-negative real number)\n",
	"\t it affects the use of statistics. Default value is 1.5\n",
	"-l",
	"\t show a list of all sections contained in the database\n",
	"-r",
	"\t reverse the direction of the questions\n",
	"-v",
	"\t review mode: it asks the same words as in the previous run\n",
	"-ro",
	"\t read only: do not save statistics and review information\n",
	"-h",
	"\t this help\n\n";
	print "Other commands (inside the program):\n",
	"=",
	"\t edition mode (disabled if -ro option is given)\n",
	"==",
	"\t accepts the answer as correct\n",
	"=exit",
	"\t exit the program saving statistics if applicable\n\n";
	exit 0;
}
#####################################################
sub usage{
	print "Usage: \n", "tangobulario [-#] [-h] [-l] [-ro] [-r] [-v] [-s section1,section2,...] [-e exponent] [database_file]\n";  
}

