/***************************************************************************
 *   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 <cstdio>
#include <iostream>
#include <vector>

#include <QCoreApplication>
//#include <QLabel>
//#include <QProgressDialog>
#include <QString>
#include <QFile>
#include <QtDebug>

#include <yf/random/random.hpp>

#include "FortunesGetter.hpp"
#include "UserProfile.hpp"
#include "Setup.hpp"

using std::vector;

bool Fortune::operator<(const Fortune& other) const
{
	return this->phrase < other.phrase;
}

//-----------------------------------------------------------------------------

FortuneCatalog::FortuneCatalog(const QString& path)
	: path(path)
{
	qDebug("Reading fortune catalog from %s", qPrintable(path));

	QFile fin(path);
	if (!fin.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		qWarning("Cannot load the fortune catalog %s", qPrintable(path));
	}
	else
	{
		QTextStream in(&fin);
		in.setCodec("UTF-8");

		QString phrase, author;
		while (!(in.atEnd()))
		{
			phrase = in.readLine();
			author = in.readLine();
			if (!(phrase.isEmpty()))
			{
				Fortune tmpFortune = { phrase, author };
				fortuneSet.insert(tmpFortune);
			}
		}
		fin.close();
	}
}

void FortuneCatalog::addFortune(const Fortune& fortune)
{
	fortuneSet.insert(fortune);

	// adding new fortune to file
	QFile fout(path);
	if (!fout.open(QIODevice::Append | QIODevice::Text))
	{
		qWarning("Cannot renew the fortune catalog %s", qPrintable(path));
	}
	QTextStream out(&fout);
	out.setCodec("UTF-8");
	out << fortune.phrase << endl << fortune.author << endl;
}

bool FortuneCatalog::wasSuchFortune(const Fortune& fortune) const
{
	return (fortuneSet.find(fortune) != fortuneSet.end());
}

//-----------------------------------------------------------------------------

FortunesGetter::FortunesGetter(const UserProfile& profile)
	: profile(profile), fortuneCatalog(pathToUserFortuneCatalog)
{}

Fortune FortunesGetter::getFortune(const QString& pattern)
{
	/*
	QLabel* waitLabel = new QLabel(QCoreApplication::translate("Getting fortune dialog", "Please wait. Selecting fortune..."));
	waitLabel->setForegroundRole(QPalette::Dark);
	waitLabel->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
	waitLabel->setFrameStyle(QFrame::StyledPanel);
	waitLabel->setWindowFlags(waitLabel->windowFlags() | Qt::FramelessWindowHint);
	waitLabel->setWindowModality(Qt::WindowModal);
	waitLabel->show();
	waitLabel->setFocus(Qt::OtherFocusReason);

	QProgressDialog progressDialog(QCoreApplication::translate("Getting fortune dialog", "Please wait. Selecting fortune..."),
		QCoreApplication::translate("Getting fortune dialog", "Hide"),
		0, 1);
	progressDialog.setMinimumDuration(1000);
	progressDialog.setWindowModality(Qt::WindowModal);

	QCoreApplication::processEvents();
	sleep(3);
	*/

	Fortune result;

	QString fortuneArguments = profile.getFortuneArgumentsLine();
	QString fortuneCommand = QString("fortune -n %1 -s %2 -m '%3' 2>/dev/null")
			.arg(maxSymbolsInFortune)
			.arg(fortuneArguments)
			.arg(pattern);
	if (fortuneArguments.isEmpty()) // english language
	{
		// try to workaround fortune bug:
		// when locale isn't english, i haven't found a correct way to select
		// only english fortunes
		//
		// so supposing that most systems have the default "C" locale
		fortuneCommand.prepend(QString::fromLatin1("LC_ALL=C "));
	}

	qDebug("Fortune command: %s", qPrintable(fortuneCommand));

	vector<Fortune> candidates;
	Fortune tmp;

	FILE* pipe = popen(qPrintable(fortuneCommand), "r");
	QFile fin;
	if (!fin.open(pipe, QIODevice::ReadOnly))
	{
		qFatal("Cannot attach to a fortune pipe");
	}
	QTextStream pin(&fin);

	QString buf;
	while (!pin.atEnd())
	{
		QCoreApplication::processEvents();

		buf = pin.readLine();
		if (buf == QString::fromLatin1("%"))
		{
			// fortune is finished
			tmp.phrase = tmp.phrase.simplified();
			tmp.author = tmp.author.simplified();
			if (!tmp.phrase.isEmpty() &&
					profile.getLayout().isAdmissibleFortune(tmp.phrase) &&
					!fortuneCatalog.wasSuchFortune(tmp))
			{
				candidates.push_back(tmp);
			}
			tmp.phrase = "";
			tmp.author = "";
		}
		else
		{
			int dashesIndex = buf.indexOf(QString::fromLatin1("--"));
			if (dashesIndex != -1)
			{
				/*
				 bla-bla-bla-aaaaaaaaaaaaaaa
				 la-la-la-aaaaaaaaaaaaaa
				       -- B.L. Blabla
					   |||^
				*/
				if (buf.size() > dashesIndex + 3)
				{
					tmp.author += buf.mid(dashesIndex + 3);
				}
			}
			else
			{
				tmp.phrase += buf + " ";
			}
		}
	}
	fin.close();
	pclose(pipe);

	if (!candidates.empty())
	{
		result = candidates[ yf::random::rnd_uint32(static_cast<uint32>(candidates.size())) ];
		fortuneCatalog.addFortune(result);
	}
	else
	{
		if (!pattern.isEmpty())
		{
			// backing to non-pattern query
			result = this->getFortune(QString());
		}
		else
		{
			result.phrase = "No fortunes found :(";
			qCritical() << "No fortunes found from call '" << qPrintable(fortuneCommand) << "'";
		}
	}

	/*
	waitLabel->hide();
	delete waitLabel;
	progressDialog.setValue(1);
	*/

	return result;
}

