/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "checker.h"
#include <algorithm>
#include <QFile>
#include <QTextCodec>
#include <QSettings>
#include <QCoreApplication>
#include <util/util.h>
#include <util/sll/prelude.h>
#include "knowndictsmanager.h"

Q_DECLARE_METATYPE (QSet<QString>)

namespace LC
{
namespace Rosenthal
{
	Checker::Checker (const KnownDictsManager *knownMgr)
	: KnownMgr_ { knownMgr }
	{
		connect (knownMgr,
				SIGNAL (languagesChanged (QStringList)),
				this,
				SLOT (setLanguages (QStringList)));
		setLanguages (knownMgr->GetLanguages ());

		QSettings settings { QCoreApplication::organizationName (),
				QCoreApplication::applicationName () + "_Rosenthal_LearntWords" };
		LearntWords_ = settings.value ("LearntWords").value<QSet<QString>> ();
	}

	namespace
	{
#ifdef HUNSPELL_HAS_CXX_OVERLOADS
		std::string ToString (const QByteArray& word)
		{
			return { word.data (), static_cast<size_t> (word.size ()) };
		}
#endif

		bool Spell (Hunspell *hs, const QByteArray& word)
		{
#ifdef HUNSPELL_HAS_CXX_OVERLOADS
			return hs->spell (ToString (word));
#else
			return hs->spell (word.data ());
#endif
		}

		QStringList Suggest (Hunspell *hs, QTextCodec *codec, const QByteArray& word)
		{
#ifdef HUNSPELL_HAS_CXX_OVERLOADS
			return Util::Map (hs->suggest (ToString (word)),
					[codec] (const std::string& str)
					{
						return codec->toUnicode (str.c_str ());
					});
#else

			char **wlist = 0;
			const int ns = hs->suggest (&wlist, word.data ());
			if (!ns || !wlist)
				return {};

			QStringList result;
			for (int i = 0; i < std::min (ns, 10); ++i)
				result << codec->toUnicode (wlist [i]);
			hs->free_list (&wlist, ns);
			return result;
#endif
		}
	}

	QStringList Checker::GetPropositions (const QString& word) const
	{
		QStringList result;
		for (const auto& item : Hunspells_)
		{
			const auto& encoded = item.Codec_->fromUnicode (word);
			if (!Spell (item.Hunspell_.get (), encoded))
				result += Suggest (item.Hunspell_.get (), item.Codec_, encoded);
		}
		return result;
	}

	bool Checker::IsCorrect (const QString& word) const
	{
		if (LearntWords_.contains (word))
			return true;

		return std::any_of (Hunspells_.begin (), Hunspells_.end (),
				[&word] (const HunspellItem& item)
				{
					return Spell (item.Hunspell_.get (), item.Codec_->fromUnicode (word));
				});
	}

	void Checker::LearnWord (const QString& word)
	{
		LearntWords_ << word;

		QSettings settings { QCoreApplication::organizationName (),
				QCoreApplication::applicationName () + "_Rosenthal_LearntWords" };
		settings.setValue ("LearntWords", QVariant::fromValue (LearntWords_));
	}

	void Checker::setLanguages (const QStringList& languages)
	{
		Hunspells_.clear ();

		for (const auto& language : languages)
		{
			const auto& primaryPath = KnownMgr_->GetDictPath (language);
			HunspellItem item;
			item.Hunspell_ = std::make_unique<Hunspell> ((primaryPath + ".aff").toLatin1 (),
					(primaryPath + ".dic").toLatin1 ());
			item.Codec_ = QTextCodec::codecForName (item.Hunspell_->get_dic_encoding ());

			if (item.Codec_)
				Hunspells_.push_back (std::move (item));
		}
	}
}
}
