/*****************************************************************************/
/*  Klavaro - a flexible touch typing tutor                                  */
/*  Copyright (C) 2005, 2006, 2007, 2008 Felipe Castro                       */
/*  Copyright (C) 2009, 2010, 2011 Free Software Foundation                  */
/*                                                                           */
/*  This program is free software, licensed under the terms of the GNU       */
/*  General Public License as published by the Free Software Foundation,     */
/*  either version 3 of the License, or (at your option) any later version.  */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program.  If not, see .    */
/*****************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include "main.h"
#include "auxiliar.h"
#include "keyboard.h"
#include "adaptability.h"
#include "accuracy.h"
static struct
{
	gunichar uchr;
	gulong wrong;
	gulong correct;
} terror[MAX_CHARS_EVALUATED];
static gint terror_n;
/* Simple reset
 */
void
accur_reset ()
{
	terror_n = 0;
}
gint
accur_n_get (void)
{
	return (terror_n);
}
gchar *
accur_char_utf8 (gint i)
{
	gchar *utf8;
	gint n;
	if (i < 0 || i >= terror_n)
		return (g_strdup (" "));
	utf8 = g_malloc (UTF8_BUFFER);
	n = g_unichar_to_utf8 (terror[i].uchr, utf8);
	if (n < 1)
		return (g_strdup (" "));
	utf8[n] = '\0';
	return (utf8);
}
gulong
accur_wrong_get (gint i)
{
	if (i < 0 || i >= terror_n)
		return -1;
	return (terror[i].wrong);
}
/**********************************************************************
 * Open the accuracy accumulator
 */
void
accur_init ()
{
	const gchar delim[] = "\t\n\r";
	gint i;
	gchar *tmp;
	gchar *data;
	gboolean success;
	gunichar uchr;
	gulong wrong;
	gulong correct;
	memset (&terror, 0, sizeof (terror));
	terror_n = 0;
	tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL);
	success = g_file_get_contents (tmp, &data, NULL, NULL);
	g_free (tmp);
	if (!success)
		return;
	tmp = strtok (data, delim);
	for (i = 0; i < MAX_CHARS_EVALUATED; i++)
	{
		if (tmp == NULL)
			break;
		uchr = g_utf8_get_char_validated (tmp, -1);
		if (uchr == (gunichar)-1 || uchr == (gunichar)-2) 
			break;
		tmp = strtok (NULL, delim);
		if (tmp == NULL)
			break;
		wrong = strtoul (tmp, NULL, 10);
		tmp = strtok (NULL, delim);
		if (tmp == NULL)
			break;
		correct = strtoul (tmp, NULL, 10);
		
		if (wrong > 0 && correct <= ERROR_INERTIA)
		{
			terror[i].uchr = uchr;
			terror[i].wrong = wrong;
			terror[i].correct = correct;
			terror_n = i + 1;
		}
		else
			break;
		tmp = strtok (NULL, delim);
	}
	g_free (data);
}
/**********************************************************************
 * Accumulates correctly typed characters
 */
void
accur_correct (gunichar uchr)
{
	gint i;
	if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
		return;
	for (i = 0; i < terror_n; i++)
	{
		if (uchr == terror[i].uchr)
		{
			if (terror[i].correct > ERROR_INERTIA)
			{
				terror[i].wrong -= (terror[i].wrong == 0 ? 0 : 1);
				terror[i].correct = 1;
			}
			else
				terror[i].correct++;
			return;
		}
	}
}
/**********************************************************************
 * Accumulates mistyped characters
 */
void
accur_wrong (gunichar uchr)
{
	gint i;
	gint i_min = -1;
	gdouble correct_min = 1e9;
	gdouble hlp;
	if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
		return;
	for (i = 0; i < terror_n; i++)
	{
		if (uchr == terror[i].uchr)
		{
			terror[i].wrong++;
			return;
		}
		hlp = ((gdouble) terror[i].wrong) / ((gdouble) terror[i].correct + terror[i].wrong + 1);
		if (hlp <= correct_min)
		{
			correct_min = hlp;
			i_min = i;
		}
	}
	if (terror_n < MAX_CHARS_EVALUATED)
	{
		i = terror_n;
		terror_n++;
	}
	else
	{
		i = (i_min > -1 ? i_min : terror_n - 1);
	}
	terror[i].uchr = uchr;
	terror[i].wrong = 1;
	terror[i].correct = 1;
}
gulong
accur_error_total ()
{
	gint i;
	gulong n = 0;
	for (i = 0; i < terror_n; i++)
		n += (terror[i].wrong < 12345 ? terror[i].wrong : 0);
	return n;
}
/*******************************************************************************
 * Increasing order, first: wrongness, second: correctness
 */
void
accur_sort ()
{
	gint i, j;
	gunichar uchr;
	gulong correct;
	gulong wrong;
	for (i = 1; i < terror_n; i++)
	{
		for (j = i; j > 0; j--)
		{
			if (terror[j].correct < terror[j-1].correct)
			{
				uchr = terror[j].uchr;
				terror[j].uchr = terror[j-1].uchr;
				terror[j-1].uchr = uchr;
				wrong = terror[j].wrong;
				terror[j].wrong = terror[j-1].wrong;
				terror[j-1].wrong = wrong;
				correct = terror[j].correct;
				terror[j].correct = terror[j-1].correct;
				terror[j-1].correct = correct;
			}
		}
	}
	for (i = 1; i < terror_n; i++)
	{
		for (j = i; j > 0; j--)
		{
			if (terror[j].wrong > terror[j-1].wrong)
			{
				uchr = terror[j].uchr;
				terror[j].uchr = terror[j-1].uchr;
				terror[j-1].uchr = uchr;
				wrong = terror[j].wrong;
				terror[j].wrong = terror[j-1].wrong;
				terror[j-1].wrong = wrong;
				correct = terror[j].correct;
				terror[j].correct = terror[j-1].correct;
				terror[j-1].correct = correct;
			}
		}
	}
}
/*******************************************************************************
 * Creates a random weird word based on error profile
 */
gboolean
accur_create_word (gunichar word[MAX_WORD_LEN + 1])
{
	gint i, j;
	gint ind;
	gint n;
	gunichar vowels[20];
	gunichar last = 0;
	gint vlen;
	if (terror_n < 10)
		return FALSE;
	vlen = keyb_get_vowels (vowels);
	n = rand () % (MAX_WORD_LEN) + 1;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < 100; j++)
		{
			ind = rand () % terror_n;
			if (terror[ind].uchr == last)
				continue;
			if (rand () % terror[0].wrong < terror[ind].wrong)
				break;
		}
		word[i] = terror[ind].uchr;
		last = word[i];
		/* Avoid double diacritics
		 */
		if (i > 0)
			if (keyb_is_diacritic (word[i - 1]) && keyb_is_diacritic (word[i]))
			{
				word[i] = vowels[rand () % vlen];
				last = word[i];
			}
	}
	/*
	 * Null terminated unistring
	 */
	word[n] = L'\0';
	return TRUE;
}
/*******************************************************************************
 * Saves the accuracy accumulator
 */
void
accur_close ()
{
	gint i;
	gchar *tmp;
	gchar *utf8;
	FILE *fh;
	tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL);
	fh = g_fopen (tmp, "wb");
	g_free (tmp);
	if (!fh)
	{
		g_message ("Could not save the accuracy log file at %s", main_path_stats ());
		return;
	}
	accur_sort ();
	for (i = 0; i < terror_n; i++)
	{
		if (terror[i].wrong == 0 || terror[i].correct > ERROR_INERTIA)
			continue;
		utf8 = accur_char_utf8 (i);
		g_fprintf (fh, "%s\t%lu\t%lu\n", utf8, terror[i].wrong, terror[i].correct);
		g_free (utf8);
	}
	fclose (fh);
}