/***************************************************************************
                              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 "exercisefactorize.h"
#include "exercisefactorize.moc"

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

/* these includes are needed for Qt support */
#include <qapplication.h>
#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 "factorizedwidget.h"
#include "primenumber.h"
#include "rationalwidget.h"
#include "resultwidget.h"
#include "settingsclass.h"

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

/* constructor */
ExerciseFactorize::ExerciseFactorize(QWidget * parent):
		ExerciseBase(parent)
{
#ifdef DEBUG
	kDebug() << "constructor ExerciseFactorize()";
#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;

	Form1Layout = new QVBoxLayout( this );
	Form1Layout->setMargin( 11 );
	Form1Layout->setSpacing( 6 );
	Form1Layout->setObjectName( "Form1Layout");

	layout9 = new QVBoxLayout();
	layout9->setObjectName( "layout9" );
	layout9->setSpacing( 6 );
	layout9->setMargin( 0 );

	layout4 = new QHBoxLayout();
	layout4->setObjectName( "layout4" );
	layout4->setSpacing( 6 );
	layout4->setMargin( 0 );

	m_taskLabel = new QLabel( this );
	m_taskLabel->setObjectName( "m_taskLabel" );
	layout4->addWidget( m_taskLabel );

	m_equalSignLabel = new QLabel( this );
	m_equalSignLabel->setObjectName( "m_equalSignLabel" );
	layout4->addWidget( m_equalSignLabel );

	m_factorsEnteredEdit = new KLineEdit( this );
	m_factorsEnteredEdit->setObjectName( "m_factorsEnteredEdit" );
	layout4->addWidget( m_factorsEnteredEdit );
	m_factorsEnteredEdit->setReadOnly(true);
	m_factorsEnteredEdit->setEnabled(false);
	QPalette pal;
	pal.setColor( m_factorsEnteredEdit->foregroundRole(), QColor(0, 0, 0) );
	m_factorsEnteredEdit->setPalette(pal);

	m_factorsWidget = new FactorizedWidget( this, m_factorsResult);
	m_factorsWidget->setObjectName("m_factorsWidget");
	layout4->addWidget( m_factorsWidget );
	m_factorsWidget->hide();

	result_label = new QLabel( this );
	result_label->setObjectName( "result_label" );
	layout4->addWidget( result_label );
	spacer1 = new QSpacerItem( 25, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
	layout4->addItem( spacer1 );

	layout9->addLayout( layout4 );
	spacer2 = new QSpacerItem( 20, 21, QSizePolicy::Minimum, QSizePolicy::Expanding );
	layout9->addItem( spacer2 );

	layout2 = new QVBoxLayout();
        layout2->setSpacing( 6 );
        layout2->setObjectName( "layout2" );

	layout1 = new QGridLayout();
        layout1->setSpacing( 6 );
        layout1->setObjectName( "layout1" );

	m_factor3Button = new QPushButton( this );
	m_factor3Button->setObjectName( "m_factor3Button" );
	layout1->addWidget( m_factor3Button, 0, 1 );

	m_factor2Button = new QPushButton( this );
	m_factor2Button->setObjectName( "m_factor2Button" );
	layout1->addWidget( m_factor2Button, 0, 0 );

	m_factor13Button = new QPushButton( this );
	m_factor13Button->setObjectName( "m_factor13Button" );
	layout1->addWidget( m_factor13Button, 1, 1 );

	m_factor7Button = new QPushButton( this );
	m_factor7Button->setObjectName( "m_factor7Button" );
	layout1->addWidget( m_factor7Button, 0, 3 );

	m_factor11Button = new QPushButton( this );
	m_factor11Button->setObjectName( "m_factor11Button" );
	layout1->addWidget( m_factor11Button, 1, 0 );

	m_factor19Button = new QPushButton( this );
	m_factor19Button->setObjectName( "m_factor19Button" );
	layout1->addWidget( m_factor19Button, 1, 3 );

	m_factor5Button = new QPushButton( this );
	m_factor5Button->setObjectName( "m_factor5Button" );
	layout1->addWidget( m_factor5Button, 0, 2 );

	m_factor17Button = new QPushButton( this );
	m_factor17Button->setObjectName( "m_factor17Button" );
	layout1->addWidget( m_factor17Button, 1, 2 );

	layout2->addLayout( layout1 );

	m_removeLastFactorButton = new QPushButton( this );
	m_removeLastFactorButton->setObjectName( "m_removeLastFactorButton" );
	layout2->addWidget( m_removeLastFactorButton );
	layout9->addLayout( layout2 );

	spacer4 = new QSpacerItem( 20, 21, QSizePolicy::Minimum, QSizePolicy::Expanding );
	layout9->addItem( spacer4 );

	layout7 = new QHBoxLayout();
	layout7->setObjectName( "layout7" );
	layout7->setSpacing( 6 );
	layout7->setMargin( 0 );
	spacer3 = new QSpacerItem( 361, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
	layout7->addItem( spacer3 );

	m_checkButton = new QPushButton( this );
	m_checkButton->setObjectName( "m_checkButton" );
	layout7->addWidget( m_checkButton );
	layout9->addLayout( layout7 );
	Form1Layout->addLayout( layout9 );

	// the current task
	QString tmp_str;
	tmp_str.setNum(m_taskNumber);
	m_taskLabel->setText(tmp_str);

	// now set the color for the task label
	pal = QPalette();
	pal.setColor( m_taskLabel->foregroundRole(), SettingsClass::numberColor() );
	m_taskLabel->setPalette( pal );

	// the equal sign
	m_equalSignLabel->setText("=");

	// now set the color for the equal sign
	pal = QPalette();
	pal.setColor( m_equalSignLabel->foregroundRole(), SettingsClass::operationColor() );
	m_equalSignLabel->setPalette( pal );

	// the wrong/correct label, we hide it
	//result_label->setText(i18n("WRONG")); // hidden, so no need to set the text (jpw)
	result_label->hide();

	// the prime factor buttons
	m_factor2Button->setText( i18n( "2" ) );
	m_factor3Button->setText( i18n( "3" ) );
	m_factor5Button->setText( i18n( "5" ) );
	m_factor7Button->setText( i18n( "7" ) );
	m_factor11Button->setText( i18n( "11" ) );
	m_factor13Button->setText( i18n( "13" ) );
	m_factor17Button->setText( i18n( "17" ) );
	m_factor19Button->setText( i18n( "19" ) );
	QObject::connect(m_factor2Button, SIGNAL(clicked()), this, SLOT(slotFactor2ButtonClicked()));
	QObject::connect(m_factor3Button, SIGNAL(clicked()), this, SLOT(slotFactor3ButtonClicked()));
	QObject::connect(m_factor5Button, SIGNAL(clicked()), this, SLOT(slotFactor5ButtonClicked()));
	QObject::connect(m_factor7Button, SIGNAL(clicked()), this, SLOT(slotFactor7ButtonClicked()));
	QObject::connect(m_factor11Button, SIGNAL(clicked()), this, SLOT(slotFactor11ButtonClicked()));
	QObject::connect(m_factor13Button, SIGNAL(clicked()), this, SLOT(slotFactor13ButtonClicked()));
	QObject::connect(m_factor17Button, SIGNAL(clicked()), this, SLOT(slotFactor17ButtonClicked()));
	QObject::connect(m_factor19Button, SIGNAL(clicked()), this, SLOT(slotFactor19ButtonClicked()));

	// add tooltips to the factor buttons
	m_factor2Button->setToolTip(i18n("Add prime factor 2."));
	m_factor3Button->setToolTip(i18n("Add prime factor 3."));
	m_factor5Button->setToolTip(i18n("Add prime factor 5."));
	m_factor7Button->setToolTip(i18n("Add prime factor 7."));
	m_factor11Button->setToolTip(i18n("Add prime factor 11."));
	m_factor13Button->setToolTip(i18n("Add prime factor 13."));
	m_factor17Button->setToolTip(i18n("Add prime factor 17."));
	m_factor19Button->setToolTip(i18n("Add prime factor 19."));

	// the remove last factor button
	m_removeLastFactorButton->setText( i18n( "&Remove Last Factor" ) );
	m_removeLastFactorButton->setEnabled(false);
	QObject::connect(m_removeLastFactorButton, SIGNAL(clicked()), this, SLOT(slotRemoveLastFactorButtonClicked()));
	m_removeLastFactorButton->setToolTip(i18n("Removes the last entered prime factor."));

	// the check task button
	m_checkButton->setText( i18n( "&Check Task" ) );
	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."));
	QObject::connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckButtonClicked()));
	m_checkButton->setDefault(true); // is the default button of the dialog

	// that the user can start choosing the prime factors
	m_factor2Button->setFocus();

	// set the tab order
	setTabOrder(m_factor2Button, m_factor3Button);
	setTabOrder(m_factor3Button, m_factor5Button);
	setTabOrder(m_factor5Button, m_factor7Button);
	setTabOrder(m_factor7Button, m_factor11Button);
	setTabOrder(m_factor13Button, m_factor17Button);
	setTabOrder(m_factor17Button, m_factor19Button);
	setTabOrder(m_factor19Button, m_removeLastFactorButton);

	// add tooltip and qwhatsthis help to the widget
	setToolTip(i18n("In this exercise you have to factorize a given number."));
	setWhatsThis( i18n("In this exercise you have to factorize a given number. You have to enter all prime factors of the number. You can add a prime factor by clicking on the corresponding button. The chosen prime factors will be shown in the input field. Do not forget to enter all prime factors, even when a prime factor repeats several times."));
}

/* destructor */
ExerciseFactorize::~ExerciseFactorize()
{
#ifdef DEBUG
	kDebug() << "destructor ExerciseFactorize()";
#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 ExerciseFactorize::forceNewTask()
{
#ifdef DEBUG
	kDebug() << "forceNewTask ExerciseFactorize()";
#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 ExerciseFactorize::update()
{
	// now set the color for the task label
	QPalette pal;
	pal.setColor( m_taskLabel->foregroundRole(), SettingsClass::numberColor() );
	m_taskLabel->setPalette( pal );

	// now set the color for the equal sign
	pal = QPalette();
	pal.setColor( m_equalSignLabel->foregroundRole(), SettingsClass::operationColor() );
	m_equalSignLabel->setPalette( pal );

	// and the factors
	m_factorsWidget->updateAndRepaint();

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


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

void ExerciseFactorize::createTask()
{
	uint uint_number;
	primenumber tmp_primenumber;

	// just pick one number out of the possible numbers to factorize
	m_taskNumber = possibleTasks[uint((double(rand()) / RAND_MAX) * numberPossibleTasks)];

	// now get the primefactors of the taskNumber
	m_factorsResult.clear();
	uint_number = m_taskNumber;
	tmp_primenumber.move_first();
	do
	{
		// check if the current primenumber is a divisor
		if (uint_number % tmp_primenumber.get_current() != 0)
		{
			// no, it is not a divisor, go on with next primenumber
			tmp_primenumber.move_forward();
		} else {
			// current primenumber is a divisor
			m_factorsResult.append(tmp_primenumber.get_current());
			uint_number = uint(uint_number / tmp_primenumber.get_current());
		}
	} while (uint_number != 1);

	return;
}

/**	- checks, if the user solved the task correctly
		- emits signals if task was solved correctly or wrong */
void ExerciseFactorize::showResult()
{
	QString tmp_str, tmp_str2; /* to build a string for a label */
	QPalette pal;
	uint uint_result = 0;

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

	// disable prime factor buttons
	m_factor2Button->setEnabled(false);
	m_factor3Button->setEnabled(false);
	m_factor5Button->setEnabled(false);
	m_factor7Button->setEnabled(false);
	m_factor11Button->setEnabled(false);
	m_factor13Button->setEnabled(false);
	m_factor17Button->setEnabled(false);
	m_factor19Button->setEnabled(false);

	// disable factor removal button as well
	m_removeLastFactorButton->setEnabled(false);

	// show the result
	m_factorsWidget->setFactors(m_factorsResult);
	m_factorsWidget->show();

	// now calculate the product of the prime factors entered by the user
	for (int tmp_uint = 0; tmp_uint < m_factorsEntered.count(); tmp_uint++)
	{
		if (tmp_uint == 0)
		{
			uint_result = m_factorsEntered[0];
		} else {
			uint_result *= m_factorsEntered[tmp_uint];
		}
	}

	if (uint_result == m_taskNumber)
	{
		// 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);
	} 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);

	} /* if (entered_result == result) */

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

	return;
}

/** generate the next task and show it to the user */
void ExerciseFactorize::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."));

	// enable prime factor buttons
	m_factor2Button->setEnabled(true);
	m_factor3Button->setEnabled(true);
	m_factor5Button->setEnabled(true);
	m_factor7Button->setEnabled(true);
	m_factor11Button->setEnabled(true);
	m_factor13Button->setEnabled(true);
	m_factor17Button->setEnabled(true);
	m_factor19Button->setEnabled(true);

	// disable the factor removal button, there are no factors to be removed yet
	m_removeLastFactorButton->setEnabled(false);

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

	/* clear user input */
	m_factorsEntered.clear();
	m_factorsResult.clear();
	m_factorsEnteredEdit->setText("");

	m_factor2Button->setFocus();

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

	// update the task widget
	QString tmp_str;
	tmp_str.setNum(m_taskNumber);
	m_taskLabel->setText(tmp_str);

	return;
}

void ExerciseFactorize::addFactor(uint factor)
{
	// add the new entered factor
	m_factorsEntered.append(factor);

	// a factor was added, so the user can can remove one again
	m_removeLastFactorButton->setEnabled(true);

	// update the line edit
	updateEnteredEdit();

	return;
}

void ExerciseFactorize::updateEnteredEdit()
{
	// the string to be shown in the entered edit
	QString str_output = "";
	QString str_tmp;

	// find the end of the list
	uintList::iterator it;


	for (it = m_factorsEntered.begin(); it != m_factorsEntered.end(); ++it)
	{
		// convert the factor into a string
		str_tmp.setNum(*it);

		// add the factor with a *
		if (it == m_factorsEntered.begin())
		{
			str_output = str_tmp;
		} else {
			str_output += " * " + str_tmp;
		}
	}

	// set the text into the line edit
	m_factorsEnteredEdit->setText(str_output);

	return;
}

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

void ExerciseFactorize::slotCheckButtonClicked()
{
	if (m_currentState == _CHECK_TASK)
	{
		// if nothing has been entered by the user, we don't check the result yet
		if (m_factorsEntered.count() == 0)
			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();
	}

	// update the line edit
	updateEnteredEdit();

	return;
}

void ExerciseFactorize::slotFactor2ButtonClicked()
{
	addFactor(2);

	return;
}

void ExerciseFactorize::slotFactor3ButtonClicked()
{
	addFactor(3);

	return;
}

void ExerciseFactorize::slotFactor5ButtonClicked()
{
	addFactor(5);

	return;
}

void ExerciseFactorize::slotFactor7ButtonClicked()
{
	addFactor(7);

	return;
}

void ExerciseFactorize::slotFactor11ButtonClicked()
{
	addFactor(11);

	return;
}

void ExerciseFactorize::slotFactor13ButtonClicked()
{
	addFactor(13);

	return;
}

void ExerciseFactorize::slotFactor17ButtonClicked()
{
	addFactor(17);

	return;
}

void ExerciseFactorize::slotFactor19ButtonClicked()
{
	addFactor(19);

	return;
}

void ExerciseFactorize::slotRemoveLastFactorButtonClicked()
{
	if (m_factorsEntered.count() > 0)
	{
		// find the end of the list
		uintList::iterator it = m_factorsEntered.end();
		--it;

		// remove last item
		m_factorsEntered.erase(it);
	}

	// if the list is empty, we have to disable the delete button
	if (m_factorsEntered.count() == 0)
		m_removeLastFactorButton->setEnabled(false);

	// update the line edit
	updateEnteredEdit();

	return;
}
