/***************************************************************************
 *   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 <algorithm>
#include <ctime>

#include <QApplication>

#include <yf/defs.hpp>

#include "StatsLogic.hpp"

using std::make_pair;

StatsLogic::StatsLogic(const UserProfile& profile)
	: profile(profile)
{
	const vector<ExerciseResult>& exercises = profile.exercises();

	Stat stat = Stat();
	for (size_t i = 1; i < exercises.size(); ++i)
		// 0-th exercise is "null" and isn't needed
	{
		AddInfo addInfo;
		addInfo.exerciseNumber = i;
		addInfo.timestamp = exercises[i].timestamp;

		stat.exerciseCount = 1;
		stat.symbolCount = exercises[i].symbolCount;
		stat.mistakeAverage = exercises[i].mistakes.size() / 2;
		stat.rhythmAverage = exercises[i].rhythmPercent;
		stat.speedAverage = exercises[i].millisecsSpent ? (
			exercises[i].symbolCount * yf::millisec_in_second
			* yf::sec_in_minute / exercises[i].millisecsSpent) : 0;
		stat.time = static_cast<stat_t>(exercises[i].millisecsSpent)/yf::millisec_in_second;
		stat.combinations.clear();
		stat.combinations += exercises[i].worseTriLetter;
		stat.mistakes.clear();
		stat.mistakes += exercises[i].mistakes;

		this->stats.push_back(make_pair(stat, addInfo));
	}

	initCategorizeInfo();
}

StatsLogic::CategorizedStats StatsLogic::categorize(CategorizeType categorizeBy, CategorizeType summarizeBy)
{
	CategorizedStats result;
	if (stats.empty())
	{
		qWarning("No statistics found...");
	}
	else
	{
		QString curCategory = category(stats[0].second, categorizeBy); // first category
		QString curSummarizeId;
		vector<DisplayStat> displayStats;

		for (size_t i = 0; i < stats.size(); ++i)
		{
			DisplayStat displayStat;
			displayStat.stat = stats[i].first;

			QString statCategory = category(stats[i].second, categorizeBy);
			if (statCategory != curCategory)
			{
				result.push_back(make_pair(curCategory, displayStats));
				displayStats.clear();
				curCategory = statCategory;
			}

			QString statSummarizeId = category(stats[i].second, summarizeBy);
			if (statSummarizeId != curSummarizeId)
			{
				displayStat.caption = statSummarizeId;
				displayStats.push_back(displayStat);
				curSummarizeId = statSummarizeId;
			}
			else
			{
				statIncrement(displayStats[displayStats.size()-1].stat, displayStat.stat);
			}
		}
		result.push_back(make_pair(curCategory, displayStats));
	}

	return result;
}

QString myStrFTime(const time_t* timestamp, const char* format)
{
	char dayString[1024];
	struct tm time;
	localtime_r(timestamp, &time);
	strftime(dayString, sizeof(dayString), format, &time);

	return QString::fromUtf8(dayString);
}

QString StatsLogic::category(const AddInfo& addInfo, CategorizeType categorizeType)
{
	QString result;
	switch (categorizeType)
	{
		case byExercise:
			result = qApp->translate("StatsLogic::category", "exercise #%1").arg(addInfo.exerciseNumber);
			break;

		case byDay:
			result = myStrFTime(&addInfo.timestamp, "%F");
			break;

		case byWeek:
			result = myStrFTime(&addInfo.timestamp, "%Y,%V");
			break;

		case byMonth:
			result = myStrFTime(&addInfo.timestamp, "%B %Y");
			break;

		case byYear:
			result = myStrFTime(&addInfo.timestamp, "%Y");
			break;

		case byAll:
			result = qApp->translate("StatsLogic::category", "all");
			break;
	}
	return result;
}

void StatsLogic::logIncrement(stat_t& stat, size_t newValue)
{
	const float koef = 1.7;
	stat = (stat * koef + newValue) / (koef + 1);
}

void StatsLogic::statIncrement(Stat& stat, const Stat& newStat)
{
	stat.exerciseCount += 1;
	stat.symbolCount += newStat.symbolCount;
	stat.time += newStat.time;
	stat.combinations += newStat.combinations;
	stat.mistakes += newStat.mistakes;
	logIncrement(stat.mistakeAverage, newStat.mistakeAverage);
	logIncrement(stat.rhythmAverage, newStat.rhythmAverage);
	logIncrement(stat.speedAverage, newStat.speedAverage);
}

StatsLogic::stat_t StatsLogic::Stat::index() const
{
	using std::min;
	using std::max;

	const stat_t indexPercent = 10;
	const stat_t speedIndexMax = indexPercent*40;
	const stat_t mistakeIndexMax = indexPercent*30;
	const stat_t rhythmIndexMax = indexPercent*15;
	const stat_t exerciseCountIndexMax = indexPercent*5;
	const stat_t symbolCountIndexMax = indexPercent*10;

	stat_t exerciseCountIndex = (this->exerciseCount) / 10;
	stat_t symbolCountIndex = (this->symbolCount) / 1000;
	stat_t speedIndex = (this->speedAverage) / 2;
	stat_t rhythmIndex = (this->rhythmAverage) / 100 * rhythmIndexMax;
	stat_t mistakeIndex = (mistakeIndexMax) - this->mistakeAverage*indexPercent*5;

	stat_t index = 0;
	index += min(exerciseCountIndex, exerciseCountIndexMax);
	index += min(symbolCountIndex, symbolCountIndexMax);
	index += min(speedIndex, speedIndexMax);
	index += rhythmIndex;
	index += max(0.0f, mistakeIndex);

	Q_ASSERT(index > 0.0 && index < 1000.0);
	return index;
}

const vector<StatsLogic::CategorizeInfo>& StatsLogic::categorizeInfo() const
{
	return myCategorizeInfo;
}

void StatsLogic::initCategorizeInfo()
{
	myCategorizeInfo.push_back(CategorizeInfo(byExercise, qApp->translate("StatsWidget::initCagegorizeInfo", "exercise")));
	myCategorizeInfo.push_back(CategorizeInfo(byDay, qApp->translate("StatsWidget::initCagegorizeInfo", "day")));
	myCategorizeInfo.push_back(CategorizeInfo(byWeek, qApp->translate("StatsWidget::initCagegorizeInfo", "week")));
	myCategorizeInfo.push_back(CategorizeInfo(byMonth, qApp->translate("StatsWidget::initCagegorizeInfo", "month")));
	myCategorizeInfo.push_back(CategorizeInfo(byYear, qApp->translate("StatsWidget::initCagegorizeInfo", "year")));
	myCategorizeInfo.push_back(CategorizeInfo(byAll, qApp->translate("StatsWidget::initCagegorizeInfo", "all")));
}
