/***************************************************************************
 *   Copyright (C) 2005-2008 by Eugene V. Lyubimkin aka jackyf             *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License                  *
 *   (version 3 or above) 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 GPL                        *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA               *
 ***************************************************************************/
#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QFrame>
#include <QKeyEvent>
#include <QMessageBox>
#include <QTimer>
#include <QLabel>
#include <QLCDNumber>
#include <QAction>
#include <QProgressDialog>

#include <yf/defs.hpp>
#include <yf/time/time.hpp>

#include "MainWidget.hpp"
#include "KeyboardWidget.hpp"
#include "RichboxWidget.hpp"
#include "StatsWidget.hpp"
#include "UserProfile.hpp"
#include "Setup.hpp"

MainWidget::MainWidget(const UserProfile& userProfile, QWidget*)
	: profile(userProfile)
{
	this->readGuiSettings();

	// timer
	this->timer = new QTimer(this);
	this->timer->setInterval(500);
	connect(this->timer, SIGNAL(timeout()), this, SLOT(timerInterrupt()));

	// time stat
	QLabel* timeLabel = new QLabel();
	timeLabel->setText(tr("Time, sec:"));
	timeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

	this->timeLCD = new QLCDNumber;
	this->timeLCD->setDigitCount(4);

	// mistake stat
	QLabel* mistakesLabel = new QLabel();
	mistakesLabel->setText(tr("Mistakes:"));
	mistakesLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

	QLabel* mistakesDividerLabel = new QLabel();
	mistakesDividerLabel->setText(tr("of"));

	this->mistakesLCD = new QLCDNumber;
	this->mistakesLCD->setDigitCount(1);
	this->maxMistakesLCD = new QLCDNumber;
	this->maxMistakesLCD->setDigitCount(1);

	// speed stat
	QLabel* speedLabel = new QLabel();
	speedLabel->setText(tr("Speed, symb./sec.:"));
	speedLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

	this->speedLCD = new QLCDNumber;
	this->speedLCD->setDigitCount(4);

	// setting buttons
	this->startButton = new QPushButton(tr("Start"));
	this->startButton->setShortcut(QString("Ctrl+N"));
	connect(this->startButton, SIGNAL(clicked()), this, SIGNAL(startExercise()));

	this->stopButton = new QPushButton(tr("Stop"));
	this->stopButton->setShortcut(QString("Ctrl+K"));
	connect(this->stopButton, SIGNAL(clicked()), this, SIGNAL(terminateExercise()));

	this->statsButton = new QPushButton(tr("Stats"));
	this->statsButton->setShortcut(QString("Ctrl+P"));
	connect(this->statsButton, SIGNAL(clicked()), this, SLOT(showStats()));

	this->endButton = new QPushButton(tr("Quit"));
	this->endButton->setShortcut(QString("Ctrl+Q"));
	connect(this->endButton, SIGNAL(clicked()), qApp, SLOT(quit()));

	// setting main widgets
	this->keyboardWidget = new KeyboardWidget(userProfile.getLayout());

	this->richBoxWidget = new RichBoxWidget;
	this->richBoxWidget->setEnabled(false);
	this->richBoxWidget->document()->setDefaultFont(QFont("Monospace", 12));

	// "screer" of the richbox
	QFrame* hideFrame = new QFrame;
	hideFrame->setFocusPolicy(Qt::NoFocus);
	hideFrame->setAutoFillBackground(false);

	this->setDefaultState();

	// layout setting
	QHBoxLayout* statsLayout = new QHBoxLayout;
	statsLayout->addStrut(32);
	statsLayout->addWidget(timeLabel, 1);
	statsLayout->addWidget(this->timeLCD);
	statsLayout->addStretch(10);
	statsLayout->addWidget(speedLabel, 1);
	statsLayout->addWidget(this->speedLCD);
	statsLayout->addStretch(10);
	statsLayout->addWidget(mistakesLabel, 1);
	statsLayout->addWidget(this->mistakesLCD);
	statsLayout->addWidget(mistakesDividerLabel, 1);
	statsLayout->addWidget(this->maxMistakesLCD);

	QGridLayout* layout = new QGridLayout;

	QHBoxLayout* bottomLayout = new QHBoxLayout;
	bottomLayout->addWidget(this->startButton);
	bottomLayout->addWidget(this->stopButton);
	bottomLayout->addWidget(this->statsButton);
	bottomLayout->addWidget(this->endButton);

	layout->addLayout(statsLayout, 0, 0);
	layout->addWidget(this->richBoxWidget, 1, 0);
	layout->addWidget(hideFrame, 1, 0);
	layout->addWidget(this->keyboardWidget, 2, 0, Qt::AlignHCenter);
	layout->addLayout(bottomLayout, 3, 0);

	layout->setSizeConstraint(QLayout::SetFixedSize);

	this->setLayout(layout);

	// connecting signals
	connect(richBoxWidget, SIGNAL(errorOnSymbol()), this, SIGNAL(errorOnSymbol()));
	connect(richBoxWidget, SIGNAL(typedSymbol()), this, SIGNAL(typedSymbol()));
	connect(richBoxWidget, SIGNAL(errorOnSymbol()), this, SLOT(onNextSymbol()));
	connect(richBoxWidget, SIGNAL(typedSymbol()), this, SLOT(onNextSymbol()));
	connect(richBoxWidget, SIGNAL(markSymbol(QChar)), keyboardWidget, SLOT(markSymbol(QChar)));

	// for all LCDs
	foreach(QLCDNumber* lcd, this->findChildren<QLCDNumber*>())
	{
		lcd->setSegmentStyle(QLCDNumber::Flat);
		lcd->setFrameStyle(QFrame::StyledPanel);
	}

	// for all buttons
	foreach(QPushButton* button, this->findChildren<QPushButton*>())
	{
		button->setFocusPolicy(Qt::NoFocus);
		button->setToolTip(button->shortcut().toString());
	}
}

void MainWidget::startTyping(const QString& text, const QString& author)
{
	this->startButton->setEnabled(false);
	this->statsButton->setEnabled(false);
	this->stopButton->setEnabled(true);
	this->setFocus(Qt::OtherFocusReason);
	this->richBoxWidget->startTyping(text, author);
}

void MainWidget::endTyping()
{
	this->richBoxWidget->endTyping();
	this->timer->stop();
	this->keyboardWidget->markSymbol(nullChar);
	this->setDefaultState();
}

void MainWidget::tooManyMistakes()
{
	QMessageBox::critical(this, tr("Too many mistakes"), tr("Too many mistakes in current exercise.\nExercise stopped."));
}

void MainWidget::exerciseFinished(const ExerciseResult& exerciseResult)
{
	const QString endl = QString::fromLatin1("\n");
	QString message;
	message += tr("Your results") + ":";
	message += endl + endl + tr("Letters count") + ": " + QString::number(exerciseResult.symbolCount);
	message += endl + tr("Mistakes") + ": " + QString::number(exerciseResult.mistakes.size() / 2);
		// /2 because mistakes combination is twiced
	message += endl + tr("Time spent (min:sec,millisec)") + (QString(": %1:%2,%3")).arg(
		static_cast<uint>(exerciseResult.millisecsSpent / yf::sec_in_minute / yf::millisec_in_second), 2, 10, QLatin1Char('0')).arg(
		static_cast<uint>(exerciseResult.millisecsSpent / yf::millisec_in_second % yf::sec_in_minute), 2, 10, QLatin1Char('0')).arg(
		static_cast<uint>(exerciseResult.millisecsSpent % yf::millisec_in_second), 3, 10, QLatin1Char('0'));
	message += endl + tr("Average speed, symbols per minute") + ": " + QString::number(
		exerciseResult.symbolCount * yf::millisec_in_second
		* yf::sec_in_minute / exerciseResult.millisecsSpent);
	message += endl + tr("Rhythm") + ": " + QString::number(exerciseResult.rhythmPercent) + "%";

	QMessageBox::information(this, tr("Exercise has been successfully finished!"), message);
}

void MainWidget::keyPressEvent(QKeyEvent* event)
{
	QChar eventChar = event->text()[0];
	if (profile.getLayout().containsKey(eventChar))
	{
		this->richBoxWidget->keyPressed(eventChar);
	}
	else
	{
		event->ignore();
	}
}

void MainWidget::timerInterrupt()
{
	this->millisecs += this->timer->interval();
	this->setTimeLabel(this->millisecs);
	this->setSpeedLabel();
}

void MainWidget::setTimeLabel(uint32 millisecs)
{
	/*
	this->timeLabel->setText(tr("Time spent: %L1 secs").arg
		(static_cast<double>(millisecs)/yf::millisec_in_second, 0, 'f', 1));
	*/
	this->timeLCD->display(static_cast<int>(millisecs/yf::millisec_in_second));
}

void MainWidget::setSpeedLabel()
{
	QString tmp;
	if (this->millisecs)
	{
		this->speedLCD->display(static_cast<int>(this->symbolsWritten
		* yf::millisec_in_second * yf::sec_in_minute / this->millisecs));
	}
	else
	{
		this->speedLCD->display(spaceChar);
	}
	//this->speedLabel->setText(tr("Speed: ") + tmp + tr(" symbols per second"));
}

void MainWidget::setDefaultState()
{
	this->millisecs = 0;
	this->symbolsWritten = 0;
	this->timeLCD->display(spaceChar);
	this->speedLCD->display(spaceChar);
	this->mistakesLCD->display(spaceChar);
	this->maxMistakesLCD->display(spaceChar);

	this->startButton->setEnabled(true);
	this->statsButton->setEnabled(true);
	this->stopButton->setEnabled(false);
}

void MainWidget::writeGuiSettings()
{
	QSettings settings;

	settings.beginGroup("MainWidget");
	settings.setValue("pos", pos());
	settings.endGroup();
}

void MainWidget::readGuiSettings()
{
	QSettings settings;

	settings.beginGroup("MainWidget");
	move(settings.value("pos", QPoint(200, 200)).toPoint());
	settings.endGroup();
}

void MainWidget::closeEvent(QCloseEvent *event)
{
	//qDebug("MainWidget::closeEvent()");
	writeGuiSettings();
	event->accept();
}

void MainWidget::mistakeCount(int mistakeCount)
{
	Q_ASSERT(mistakeCount >= 0 && mistakeCount < 10);
	this->mistakesLCD->display(mistakeCount);
}

void MainWidget::maxMistakeCount(int maxCount)
{
	Q_ASSERT(maxCount >= 0 && maxCount < 10);
	this->maxMistakesLCD->display(maxCount);
}

void MainWidget::onNextSymbol()
{
	if (!timer->isActive())
	{
		timer->start();
	}
	this->symbolsWritten += 1;
}

void MainWidget::showStats()
{
	StatsWidget* statsWidget = new StatsWidget(profile);
	statsWidget->setWindowModality(Qt::ApplicationModal);
	statsWidget->exec();
	delete statsWidget;
}

void MainWidget::displayWaitMessage(int milliseconds)
{
	qDebug("relaxing for %d milliseconds", milliseconds);
	QProgressDialog dialog(this);
	dialog.setMinimumDuration(0);
	dialog.setMinimum(0);
	dialog.setMaximum(milliseconds);
	dialog.setCancelButton(NULL);
	dialog.setLabelText(tr("Get relax..."));
	dialog.setFixedSize(dialog.size());

	size_t divider = 10;
	for (size_t i = 0; i < milliseconds/divider; ++i)
	{
		yf::wait(divider);
		dialog.setValue(i*divider);
		QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
	}
	dialog.setValue(milliseconds);
}

