/***************************************************************************
                              exerciseconvert.h
                             -------------------
    begin                : 2004/06/04
    copyright            : (C) 2004 by Sebastian Stein
    email                : seb.kde@hpfsc.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "exerciseconvert.h"
#include "exerciseconvert.moc"

/* these includes are needed for KDE support */
#include <kglobal.h>
#include <klineedit.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <knumvalidator.h>

/* these includes are needed for Qt support */
#include <QApplication>
#include <qlabel.h>
#include <qlayout.h>
#include <qpushbutton.h>

//Added by qt3to4:
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>

/* standard C++ library includes */
#include <stdlib.h>

#include "rationalwidget.h"
#include "resultwidget.h"

/* ----- public member functions ----- */

/* constructor */
ExerciseConvert::ExerciseConvert(QWidget * parent):
		ExerciseBase(parent)
{
#ifdef DEBUG
	kDebug() << "constructor ExerciseConvert()";
#endif

	/* create a new task */
	QApplication::setOverrideCursor(Qt::WaitCursor); /* show the sand clock */
	createTask();
	QApplication::restoreOverrideCursor(); /* show the normal cursor */

	// the next thing to do on a button click would be to check the entered
	// result
	m_currentState = _CHECK_TASK;

	baseWidget = new QWidget(this);
	baseWidget->setObjectName("baseWidget");
	baseGrid = new QGridLayout( this );
        baseGrid->setObjectName( "baseGrid" );
	baseGrid->addWidget(baseWidget, 0, 0);

	// this is a VBox
	realLayout = new QVBoxLayout(baseWidget);
	realLayout->setMargin(5);
	realLayout->setSpacing(5);
	realLayout->setObjectName("realLayout");

	// add a spacer at the top of the VBox
	QSpacerItem * v_spacer = new QSpacerItem(1, 1);
	realLayout->addItem(v_spacer);

	// now a line holding the task, input fields and result
	QHBoxLayout * taskLineHBoxLayout = new QHBoxLayout();
	taskLineHBoxLayout->setObjectName("taskLineHBoxLayout");
	taskLineHBoxLayout->setSpacing(5);
	realLayout->addLayout(taskLineHBoxLayout);

	// first left is the rational widget
	m_rationalWidget = new RationalWidget(baseWidget, m_number, m_periodStart, m_periodLength);
	m_rationalWidget->setObjectName("m_rationalWidget");
	taskLineHBoxLayout->addWidget(m_rationalWidget);

	// now we have the input fields aligned in a VBox
	QVBoxLayout * inputLayout = new QVBoxLayout();
	inputLayout->setObjectName("inputLayout");
	inputLayout->setSpacing(5);
	taskLineHBoxLayout->addLayout(inputLayout);

	// to validate, that the input is an int
	KIntValidator *valnum = new KIntValidator( this );

	/* add input box so the user can enter numerator */
	numer_edit = new KLineEdit(baseWidget);
	numer_edit->setObjectName("numer_edit");
	numer_edit->setValidator( valnum ); // use the int validator
	numer_edit->setToolTip(i18n("Enter the numerator of your result"));
	inputLayout->addWidget(numer_edit);

	/* add a line between the edit boxes */
	edit_line = new QFrame(baseWidget);
	edit_line->setGeometry(QRect(100, 100, 20, 20));
	edit_line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
	inputLayout->addWidget(edit_line);

	/* add input box so the user can enter denominator */
	deno_edit = new KLineEdit(baseWidget);
	deno_edit->setObjectName("deno_edit");
	deno_edit->setValidator( valnum ); // use the int validator
	deno_edit->setToolTip(i18n("Enter the denominator of your result"));
	inputLayout->addWidget(deno_edit);

	// next is the result widget
	m_resultWidget = new ResultWidget(baseWidget, m_result);
	m_resultWidget->setObjectName("m_resultWidget");
	taskLineHBoxLayout->addWidget(m_resultWidget);
	m_resultWidget->hide();

	// at the right end we have a label just showing CORRECT or WRONG
	result_label = new QLabel(baseWidget);
	result_label->setObjectName("result_lable");
	//result_label->setText(i18n("WRONG")); // no need to set this, since it's hidden anyway (jpw)
	taskLineHBoxLayout->addWidget(result_label);
	result_label->hide();

	// add another spacer in the middle of the VBox
	v_spacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
	taskLineHBoxLayout->addItem(v_spacer);

	// --- that is the end of the horizontal line ---

	// add another spacer in the middle of the VBox
	v_spacer = new QSpacerItem(1, 1);
	realLayout->addItem(v_spacer);

	// the lower part of the VBox holds just a right aligned button
	QHBoxLayout * lowerHBox = new QHBoxLayout();
	lowerHBox->setObjectName("lowerHBox");
	lowerHBox->setSpacing(1);
	realLayout->addLayout(lowerHBox);
	lowerHBox->addStretch(100);

	// the right aligned button
	m_checkButton = new QPushButton( baseWidget );
	m_checkButton->setObjectName( "m_checkButton" );
	m_checkButton->setText(i18n("&Check Task"));
	m_checkButton->setDefault(true); // is the default button of the dialog
	m_checkButton->setToolTip(i18n("Click on this button to check your result. The button will not work if you have not entered a result yet."));
	lowerHBox->addWidget(m_checkButton, 1, Qt::AlignRight);
	QObject::connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckButtonClicked()));

	// that the user can start typing without moving the focus
	numer_edit->setFocus();

	// show the whole layout
	baseWidget->show();

	// add tooltip and qwhatsthis help to the widget
	setToolTip(i18n("In this exercise you have to convert a number into a fraction."));
	setWhatsThis( i18n("In this exercise you have to convert a given number into a fraction by entering numerator and denominator. Do not forget to reduce the result."));
}

/* destructor */
ExerciseConvert::~ExerciseConvert()
{
#ifdef DEBUG
	kDebug() << "destructor ExerciseConvert()";
#endif

	/* no need to delete any child widgets, Qt does it by itself */
}

/** resets the current state, creates a new task and count the last task as
 * wrong, if it wasn't solved (in _NEXT_TASK state) yet
 * mainly used after changing the task parameters */
void ExerciseConvert::forceNewTask()
{
#ifdef DEBUG
	kDebug() << "forceNewTask ExerciseConvert()";
#endif

	if (m_currentState == _CHECK_TASK)
	{
		// emit the signal for wrong
		signalExerciseSolvedWrong();
	}
	m_currentState = _CHECK_TASK;
	m_checkButton->setText(i18n("&Check Task"));

	// generate next task
	(void) nextTask();
}


/* ------ public slots ------ */

void ExerciseConvert::update()
{
	// call update of components
	m_rationalWidget->updateAndRepaint();
	m_resultWidget->updateAndRepaint();

	// update for itself
	((QWidget *) this)->update();
}


/* ------ private member functions ------ */

void ExerciseConvert::createTask()
{
	// the tasks are hardcoded here; there are some algorithms to convert
	// rational numbers to fractions, but it is not worth the effort here
	switch(int((double(rand()) / RAND_MAX) * 18 + 1))
	{
		case  0 :   m_number = KGlobal::locale()->formatNumber(0.5, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 2);
						break;
		case  1 :   m_number = KGlobal::locale()->formatNumber(0.3, 1);
						m_periodStart = 2;
						m_periodLength = 1;
						m_result = ratio(1, 3);
						break;
		case  2 :   m_number = KGlobal::locale()->formatNumber(0.6, 1);
						m_periodStart = 2;
						m_periodLength = 1;
						m_result = ratio(2, 3);
						break;
		case  3 :   m_number = KGlobal::locale()->formatNumber(0.25, 2);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 4);
						break;
		case  4 :   m_number = KGlobal::locale()->formatNumber(0.75, 2);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(3, 4);
						break;
		case  5 :   m_number = KGlobal::locale()->formatNumber(0.2, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 5);
						break;
		case  6 :   m_number = KGlobal::locale()->formatNumber(0.4, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(2, 5);
						break;
		case  7 :   m_number = KGlobal::locale()->formatNumber(0.6, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(3, 5);
						break;
		case  8 :   m_number = KGlobal::locale()->formatNumber(0.8, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(4, 5);
						break;
		case  9 :   m_number = KGlobal::locale()->formatNumber(0.16, 2);
						m_periodStart = 3;
						m_periodLength = 1;
						m_result = ratio(1, 6);
						break;
		case 10 :   m_number = KGlobal::locale()->formatNumber(0.142857, 6);
						m_periodStart = 2;
						m_periodLength = 6;
						m_result = ratio(1, 7);
						break;
		case 11 :   m_number = KGlobal::locale()->formatNumber(0.125, 3);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 8);
						break;
		case 12 :   m_number = KGlobal::locale()->formatNumber(0.375, 3);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(3, 8);
						break;
		case 13 :   m_number = KGlobal::locale()->formatNumber(0.1, 1);
						m_periodStart = 2;
						m_periodLength = 1;
						m_result = ratio(1, 9);
						break;
		case 14 :   m_number = KGlobal::locale()->formatNumber(0.1, 1);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 10);
						break;
		case 15 :   m_number = KGlobal::locale()->formatNumber(0.05, 2);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 20);
						break;
		case 16 :   m_number = KGlobal::locale()->formatNumber(0.01, 2);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 100);
						break;
		case 17 :   m_number = KGlobal::locale()->formatNumber(0.83, 2);
						m_periodStart = 3;
						m_periodLength = 1;
						m_result = ratio(5, 6);
						break;
		default :
		case 18 :   m_number = KGlobal::locale()->formatNumber(0.001, 3);
						m_periodStart = 2;
						m_periodLength = 0;
						m_result = ratio(1, 1000);
						break;
	}

	return;
}

/**	- checks, if the user solved the task correctly
		- emits signals if task was solved correctly or wrong */
void ExerciseConvert::showResult()
{
	QString tmp_str; /* to build a string for a label */
	QPalette pal;
	ratio entered_result;

	// change the tooltip of the check button
	m_checkButton->setToolTip(i18n("Click on this button to get to the next task."));

	numer_edit->setEnabled(false);
	deno_edit->setEnabled(false);

	m_resultWidget->setResult(m_result);
	m_resultWidget->show();

	// an empty numerator field will be interpreted as 0
	if (numer_edit->text().isEmpty() == true)
		numer_edit->setText("0");

	// an empty denominator field will be interpreted as 1
	if (deno_edit->text().isEmpty() == true)
		deno_edit->setText("1");

	/* store the entered result to check it, but without reducing */
	entered_result.setNumerator(numer_edit->text().toInt(), false);
	entered_result.setDenominator(deno_edit->text().toInt(), false);

	// check the entered result; 0/1 == 0/5 -> true,
	// but 0/1 == 0/0 -> false
	// a 0 for denominator is never allowed (always counted as wrong)
	//
	// we have to get the 0 directly from the input field, because
	// Ratio::setDenominator(0, false) will set the denominator to 1 to ensure
	// the Ratio is valid
	if ( (deno_edit->text().toInt() != 0) && ((entered_result == m_result) ||
		  (m_result.numerator() == 0 && entered_result.numerator() == 0)) )
	{
		// emit the signal for correct
		signalExerciseSolvedCorrect();

		/* yes, the user entered the correct result */
		result_label->setText(i18nc("@info:status the answer given was correct", "CORRECT"));
		pal = result_label->palette(); /* set green font color */
        pal.setColor(QPalette::Active, QPalette::Foreground, QColor(6, 179, 0));
        pal.setColor(QPalette::Inactive, QPalette::Foreground, QColor(6, 179, 0));
		result_label->setPalette(pal);
		result_label->show(); /* show the result at the end of the task */
	} else {
		// emit the signal for wrong
		signalExerciseSolvedWrong();

		/* no, the user entered the wrong result */
		result_label->setText(i18nc("@info:status the answer given was incorrect", "WRONG"));
		pal = result_label->palette(); /* set red font color */
        pal.setColor(QPalette::Active, QPalette::Foreground, QColor(Qt::red));
        pal.setColor(QPalette::Inactive, QPalette::Foreground, QColor(Qt::red));
		result_label->setPalette(pal);

		result_label->show(); /* show the result at the end of the task */

		// if the user entered a 0 for the denominator (division by 0) we have to
		// get the 0 directly from the input field, because
		// Ratio::setDenominator(0, true) will set the denominator to 1 to ensure
		// the Ratio is valid
		if (deno_edit->text().toInt() == 0)
		{
			KMessageBox::information(this,
			                         i18n("You entered a 0 as the denominator. This means division by zero, which is not allowed. This task will be counted as not correctly solved."));
		} else {
			/* maybe the entered ratio was not reduced */
			entered_result.reduce();
			if (entered_result == m_result)
				KMessageBox::information(this,
				                         i18n("You entered the correct result, but not reduced.\nAlways enter your results as reduced. This task will be counted as not correctly solved."));
		}
	} /* if (entered_result == result) */

	return;
}

/** generate the next task and show it to the user */
void ExerciseConvert::nextTask()
{
	// change the tooltip of the check button
	m_checkButton->setToolTip(i18n("Click on this button to check your result. The button will not work if you have not entered a result yet."));

	numer_edit->setEnabled(true);
	deno_edit->setEnabled(true);

	result_label->hide(); /* do not show the result at the end of the task */
	m_resultWidget->hide();

	/* clear user input */
	deno_edit->setText("");
	numer_edit->setText("");
	numer_edit->setFocus();

	/* create a new task */
	QApplication::setOverrideCursor(Qt::WaitCursor); /* show the sand clock */
	createTask();
	QApplication::restoreOverrideCursor(); /* show the normal cursor */

	// update the task widget
	m_rationalWidget->setRational(m_number, m_periodStart, m_periodLength);

	return;
}

/* ------ private slots ------ */

void ExerciseConvert::slotCheckButtonClicked()
{
	if (m_currentState == _CHECK_TASK)
	{
		// if nothing has been entered by the user, we don't check the result yet
		if (numer_edit->text().isEmpty() == true && deno_edit->text().isEmpty() ==
true)
			return;
		m_currentState = _NEXT_TASK;
		m_checkButton->setText(i18n("N&ext Task"));
		(void) showResult();
	} else {
		m_currentState = _CHECK_TASK;
		m_checkButton->setText(i18n("&Check Task"));
		(void) nextTask();
	}

	return;
}
