#include "adjective.h"
#include <string>

#include <iostream> //Xperimental -- for debug only

#include "utf8utf16.h"

#include "boost/regex.hpp"
#include "boost/algorithm/string/regex.hpp"

namespace AJ
{

	std::vector<AdjectiveRecord> AdjectiveRecordArr;

	std::vector<AdjectiveDeclencionCaseTableRecord> adjectiveDeclencionCaseTable;


	AdjectiveRecord::AdjectiveRecord()
		: standardShortFormAvailable(false)
	{

	}

	AdjectiveRecord::AdjectiveRecord(std::wstring line)
	{
		std::vector<std::wstring> lineArr;

		boost::split_regex(lineArr, line, boost::wregex(L"	"));

		nominativeMaleForm = lineArr[1];

		standardShortFormAvailable = lineArr[2] == L"1" ? true : false;

		specialShortForm = lineArr[3];

		for (int i = 0; i < 3; i++)
		{
			TranslationUnit translationUnit;

			translationUnit.meaning = lineArr[4 + i * 3];
			translationUnit.comment = lineArr[4 + i * 3 + 1];
			translationUnit.example = lineArr[4 + i * 3 + 2];

			if (translationUnit.meaning != L"")
			{
				translationUnitArr.push_back(translationUnit);
			}
		}

	}


	std::vector<std::wstring> GetAllAdjectiveEndingArr()
	{
		std::vector<std::wstring> result;

		result.push_back(L"ий");
		result.push_back(L"его");
		result.push_back(L"ему");
		result.push_back(L"им");
		result.push_back(L"ем");
		result.push_back(L"ие");
		result.push_back(L"их");
		result.push_back(L"ими");
		result.push_back(L"ой");
		result.push_back(L"ый");
		result.push_back(L"ого");
		result.push_back(L"ому");
		result.push_back(L"ым");
		result.push_back(L"ом");
		result.push_back(L"ые");
		result.push_back(L"ых");
		result.push_back(L"ыми");
		result.push_back(L"ая");
		result.push_back(L"ей");
		result.push_back(L"ую");
		result.push_back(L"ее");
		result.push_back(L"ое");

		return result;
	}

	AdjectiveDeclencion WStringToAdjectiveDeclencion(std::wstring str)
	{
		if (str == L"IFORM_MALE_INANIMATE")
		{
			return IFORM_MALE_INANIMATE;
		}
		if (str == L"IFORM_MALE_ANIMATE")
		{
			return IFORM_MALE_ANIMATE;
		}
		if (str == L"UFORM_MALE_INANIMATE")
		{
			return UFORM_MALE_INANIMATE;
		}
		if (str == L"UFORM_MALE_ANIMATE")
		{
			return UFORM_MALE_ANIMATE;
		}
		if (str == L"UOFORM_MALE_INANIMATE")
		{
			return UOFORM_MALE_INANIMATE;
		}
		if (str == L"UOFORM_MALE_ANIMATE")
		{
			return UOFORM_MALE_ANIMATE;
		}
		if (str == L"IOFORM_MALE_INANIMATE")
		{
			return IOFORM_MALE_INANIMATE;
		}
		if (str == L"IOFORM_MALE_ANIMATE")
		{
			return IOFORM_MALE_ANIMATE;
		}

		if (str == L"IFORM_FEMALE_INANIMATE")
		{
			return IFORM_FEMALE_INANIMATE;
		}
		if (str == L"IFORM_FEMALE_ANIMATE")
		{
			return IFORM_FEMALE_ANIMATE;
		}
		if (str == L"UFORM_FEMALE_INANIMATE")
		{
			return UFORM_FEMALE_INANIMATE;
		}
		if (str == L"UFORM_FEMALE_ANIMATE")
		{
			return UFORM_FEMALE_ANIMATE;
		}
		if (str == L"UOFORM_FEMALE_INANIMATE")
		{
			return UOFORM_FEMALE_INANIMATE;
		}
		if (str == L"UOFORM_FEMALE_ANIMATE")
		{
			return UOFORM_FEMALE_ANIMATE;
		}
		if (str == L"IOFORM_FEMALE_INANIMATE")
		{
			return IOFORM_FEMALE_INANIMATE;
		}
		if (str == L"IOFORM_FEMALE_ANIMATE")
		{
			return IOFORM_FEMALE_ANIMATE;
		}



		if (str == L"IFORM_NEUTRAL")
		{
			return IFORM_NEUTRAL;
		}
		if (str == L"UFORM_NEUTRAL")
		{
			return UFORM_NEUTRAL;
		}
		if (str == L"UOFORM_NEUTRAL")
		{
			return UOFORM_NEUTRAL;
		}
		if (str == L"IOFORM_NEUTRAL")
		{
			return IOFORM_NEUTRAL;
		}

		std::cout << "Error in WStringToAdjectiveDeclencion";
		return IFORM_MALE_INANIMATE;
	}

	std::set<AdjectiveEndingDivision> getPossibleAdjectiveEndingDivisionSet(std::wstring noun)
	{
		std::set<AdjectiveEndingDivision> result;

		auto allAdjectiveEndingArr = GetAllAdjectiveEndingArr();

		for (auto ending : allAdjectiveEndingArr)
		{
			if (boost::ends_with(noun, ending))
			{
				std::wstring adjectiveBase = boost::replace_last_copy(noun, ending, "");



				if (charIsConsolant(adjectiveBase[adjectiveBase.size() - 1]))
				{
					result.insert({ adjectiveBase, ending, AdjectiveEndingDivision::DC_COMMON });
				}

			}
		}

		return result;
	}

	std::vector<AdjectiveTuple> GetPossibleTupleArr(std::wstring ending)
	{
		std::vector<AdjectiveTuple> result;

		for (auto& adjective : adjectiveDeclencionCaseTable)
		{
			for (int i = 0; i < NGC_SIZE * NC_SIZE; i++)
			{
				if (adjective.grammaticalCaseTable[i].ending.count(ending) != 0)
				{
					result.push_back(AdjectiveTuple{ adjective.adjectiveDeclencion, adjective.grammaticalCaseTable[i].count, adjective.grammaticalCaseTable[i].grammaticalCase });
				}
			}

		}

		return result;
	}

	bool AdjectiveIsInDictionary(std::wstring nominative)
	{
		for (auto& adjective : AdjectiveRecordArr)
		{
			if (adjective.nominativeMaleForm == nominative)
			{
				return true;
			}
		}

		return false;
	}

	AdjectiveRecord GetAdjectiveRecordFromDictionary(std::wstring nominative)
	{
		for (auto& adjective : AdjectiveRecordArr)
		{
			if (adjective.nominativeMaleForm == nominative)
			{
				return adjective;
			}
		}

		return{};
	}


	/*
	std::set<std::wstring> GetAdjectiveNominative(std::wstring base, AdjectiveDeclencion declencion, NounCount nounCount)
	{
		std::set<std::wstring> result;

		AdjectiveDeclencionCaseTableRecord declencionCaseTableRecord = adjectiveDeclencionCaseTable[static_cast<int>(declencion)];

		for (auto& grammaticalTableRecord : declencionCaseTableRecord.grammaticalCaseTable)
		{
			if (grammaticalTableRecord.grammaticalCase == NGC_P1_NOMINATIVE && grammaticalTableRecord.count == nounCount)
			{
				for (auto& e : grammaticalTableRecord.ending)
				{
					result.insert(base + e);
				}

			}
		}

		return result;
	}*/

	std::set<std::wstring> GetNominativeMaleSingular(std::wstring base)
	{
		std::wstring result;

		if (charIsIFormConsolant(base[base.size() - 1]))
		{
			return{ base + L"ий", base + L"ой" };
		}

		if (charIsUFormConsolant(base[base.size() - 1]))
		{
			return{ base + L"ый", base + L"ой" };
		}

		return{};

	}

	bool IsDeclencionAnimated(AdjectiveDeclencion declention)
	{
		switch (declention)
		{
		case AJ::IFORM_MALE_ANIMATE:
		case AJ::UFORM_MALE_ANIMATE:
		case AJ::UOFORM_MALE_ANIMATE:
		case AJ::IOFORM_MALE_ANIMATE:
		case AJ::IFORM_FEMALE_ANIMATE:
		case AJ::UFORM_FEMALE_ANIMATE:
		case AJ::UOFORM_FEMALE_ANIMATE:
		case AJ::IOFORM_FEMALE_ANIMATE:
			return true;
			break;
		default:
			return false;
			break;
		}
	}


	NounGender GetGenderFromDeclencion(AdjectiveDeclencion declention)
	{
		switch (declention)
		{
		case AJ::IFORM_MALE_INANIMATE:
		case AJ::IFORM_MALE_ANIMATE:
		case AJ::UFORM_MALE_INANIMATE:
		case AJ::UFORM_MALE_ANIMATE:
		case AJ::UOFORM_MALE_INANIMATE:
		case AJ::UOFORM_MALE_ANIMATE:
		case AJ::IOFORM_MALE_INANIMATE:
		case AJ::IOFORM_MALE_ANIMATE:
			return NG_MALE;
			break;
		case AJ::IFORM_FEMALE_INANIMATE:
		case AJ::IFORM_FEMALE_ANIMATE:
		case AJ::UFORM_FEMALE_INANIMATE:
		case AJ::UFORM_FEMALE_ANIMATE:
		case AJ::UOFORM_FEMALE_INANIMATE:
		case AJ::UOFORM_FEMALE_ANIMATE:
		case AJ::IOFORM_FEMALE_INANIMATE:
		case AJ::IOFORM_FEMALE_ANIMATE:
			return NG_FEMALE;
			break;
		default:
			return NG_NEUTRAL;
			break;
		}
	}


	bool IFormTest(const AdjectiveRecord& r)
	{
		return charIsIFormConsolant(r.nominativeMaleForm[r.nominativeMaleForm.size() - 3]);
	}

	bool UFormTest(const AdjectiveRecord& r)
	{
		return charIsUFormConsolant(r.nominativeMaleForm[r.nominativeMaleForm.size() - 3]);
	}


	std::map<AdjectiveDeclencion, std::function < bool(const AdjectiveRecord&) >> DeclentionConditionMap;

	void SetupDeclentionMap()
	{

		DeclentionConditionMap[IFORM_MALE_INANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[IFORM_MALE_ANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[UFORM_MALE_INANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[UFORM_MALE_ANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[UOFORM_MALE_INANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[UOFORM_MALE_ANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[IOFORM_MALE_INANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[IOFORM_MALE_ANIMATE] = std::bind(IFormTest, std::placeholders::_1);

		DeclentionConditionMap[IFORM_FEMALE_INANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[IFORM_FEMALE_ANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[UFORM_FEMALE_INANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[UFORM_FEMALE_ANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[IOFORM_FEMALE_INANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[IOFORM_FEMALE_ANIMATE] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[UOFORM_FEMALE_INANIMATE] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[UOFORM_FEMALE_ANIMATE] = std::bind(UFormTest, std::placeholders::_1);


		DeclentionConditionMap[IFORM_NEUTRAL] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[UFORM_NEUTRAL] = std::bind(UFormTest, std::placeholders::_1);
		DeclentionConditionMap[IOFORM_NEUTRAL] = std::bind(IFormTest, std::placeholders::_1);
		DeclentionConditionMap[UOFORM_NEUTRAL] = std::bind(UFormTest, std::placeholders::_1);


	}

	bool AdjectiveFitsDeclention(AdjectiveRecord record, AdjectiveTuple tuple)
	{

		AdjectiveDeclencion declencion = std::get<0>(tuple);

		bool standardDeclention = DeclentionConditionMap[declencion](record);

		if (standardDeclention)
		{
			return true;
		}

		return false;
	}

	std::map < AdjectiveEndingDivision::DivisionCase, std::function < bool(AdjectiveTuple) >> DivisionCaseAdjectiveTupleFilterMap;

	std::map < AdjectiveEndingDivision::DivisionCase, std::function < bool(AdjectiveTuple, AdjectiveRecord) >> DivisionCaseAdjectiveTupleRecordFilterMap;


	void FillDivisionCaseMaps()
	{

		DivisionCaseAdjectiveTupleFilterMap[AdjectiveEndingDivision::DC_COMMON] = [](AdjectiveTuple tuple)
		{
			return true;
		};

		DivisionCaseAdjectiveTupleRecordFilterMap[AdjectiveEndingDivision::DC_COMMON] = [](AdjectiveTuple tuple, AdjectiveRecord record)
		{
			return AdjectiveFitsDeclention(record, tuple);
		};

	}

	std::set<AdjectiveStruct> RecognizeAdjective(std::wstring noun)
	{
		std::set<AdjectiveStruct> result;

		auto adjectiveEndingDivisionArr = getPossibleAdjectiveEndingDivisionSet(noun);

		for (auto adjectiveEndingDivision : adjectiveEndingDivisionArr)
		{
			std::wstring base = adjectiveEndingDivision.base;
			std::wstring ending = adjectiveEndingDivision.ending;
			AdjectiveEndingDivision::DivisionCase dc = adjectiveEndingDivision.divisionCase;

			std::wstring modifiedBase = base;
			std::wstring modifiedEnding = ending;

			std::vector<AdjectiveTuple> possibleTupleArr = GetPossibleTupleArr(modifiedEnding);

			for (AdjectiveTuple tuple : possibleTupleArr)
			{
				if (DivisionCaseAdjectiveTupleFilterMap[dc](tuple))
				{
					std::set<std::wstring> nominaviteSingularSet = GetNominativeMaleSingular(modifiedBase);

					for (auto& nn : nominaviteSingularSet)
					{

						if (AdjectiveIsInDictionary(nn))
						{

							AdjectiveRecord record = GetAdjectiveRecordFromDictionary(nn);

							if (DivisionCaseAdjectiveTupleRecordFilterMap[dc](tuple, record))
							{
								result.insert({ std::get<2>(tuple), std::get<1>(tuple), GetGenderFromDeclencion(std::get<0>(tuple)), IsDeclencionAnimated(std::get<0>(tuple)), record });
							}
						}

					}

				}
			}

		}

		return result;
	}

	void LoadAdjectiveDeclencionCaseTable()
	{

		adjectiveDeclencionCaseTable.clear();

#ifdef _WIN32
		std::ifstream f("C:/Workplace/ChineseJournal/rudict/grammar_case_adjective.txt");

#else
		std::ifstream f("/home/devuser/workplace/rudict/grammar_case_adjective.txt");
#endif

		std::string line;
		std::wstring wline;

		if (f.is_open())
		{

			std::cout << "File found!" << std::endl;

			std::vector<GrammaticalTableRecord> currentGrammaticalCaseTable;
			std::wstring currentAdjectiveDeclencion;
			std::wstring currentAdjectiveCount;

			getline(f, line); //Skip one line

			while (getline(f, line))
			{
				std::vector<std::string> lineArr;

				boost::split_regex(lineArr, line, boost::regex("	"));

				if (lineArr[0] != "")
				{
					if (currentAdjectiveDeclencion == L"")
					{
						currentAdjectiveDeclencion = string_to_wstring(lineArr[0]);
					}
					else
					{
						adjectiveDeclencionCaseTable.push_back(AdjectiveDeclencionCaseTableRecord{ WStringToAdjectiveDeclencion(currentAdjectiveDeclencion), currentGrammaticalCaseTable });

						currentAdjectiveDeclencion = string_to_wstring(lineArr[0]);

						currentGrammaticalCaseTable.clear();
					}

				}

				if (lineArr[1] != "")
				{
					currentAdjectiveCount = string_to_wstring(lineArr[1]);
				}

				std::wstring endings = string_to_wstring(lineArr[3]);

				std::set<std::wstring> endingsSet;
				boost::split_regex(endingsSet, endings, boost::regex(", "));

				currentGrammaticalCaseTable.push_back({
					WStringToNounCount(currentAdjectiveCount),
					WStringToNounGrammaticalCase(string_to_wstring(lineArr[2])),
					endingsSet
				});

			}
			//Add last one
			if (currentAdjectiveDeclencion != L"")
			{
				adjectiveDeclencionCaseTable.push_back(AdjectiveDeclencionCaseTableRecord{ WStringToAdjectiveDeclencion(currentAdjectiveDeclencion), currentGrammaticalCaseTable });
			}

			f.close();
		}
		else
		{
			std::cout << "file not found!" << std::endl;
		}


	}

	void LoadFrequentAdjectiveSet()
	{


#ifdef _WIN32
		std::ifstream f("C:/Workplace/ChineseJournal/rudict/frequent_adjectives_2000.txt");

#else
		std::ifstream f("/home/devuser/workplace/rudict/frequent_adjectives_2000.txt");
#endif

		std::string line;
		std::wstring wline;

		if (f.is_open())
		{

			getline(f, line); //Skip one line

			std::cout << "File found!" << std::endl;
			while (getline(f, line))
			{

				wline = string_to_wstring(line);
				AdjectiveRecord adjectiveRecord(wline);

				AdjectiveRecordArr.push_back(adjectiveRecord);

			}
			f.close();
		}
		else
		{
			std::cout << "file not found!" << std::endl;
		}


	}


} //namespace AJ

