#include "noun.h"

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

#include "utf8utf16.h"

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

namespace NN
{

std::vector<NounRecord> NounRecordArr;

std::vector<NounDeclencionCaseTableRecord> nounDeclencionCaseTable;

NounRecord::NounRecord()
{
	SetAllFields();
}

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

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

	SetWord(lineArr[1]);
	SetAllFields(lineArr);
	FillTranslationUnit(lineArr, 12);
	
}

NounGender NounRecord::getGender() const
{
	return WStringToNounGender(properties.get<std::wstring>(L"gender"));
}

bool NounRecord::getHaveSingleForm() const
{
	return properties.get<bool>(L"haveSingleForm");
}

bool NounRecord::getHaveMultipleForm() const
{
	return properties.get<bool>(L"haveMultipleForm");
}

bool NounRecord::getHaveStandardMultipleForm() const
{
	return properties.get<bool>(L"haveStandardMultipleForm");
}

bool NounRecord::getHaveStandardMultipleFormWithMissingLastVowel() const
{
	return properties.get<bool>(L"haveStandardMultipleFormWithMissingLastVowel");
}

bool NounRecord::getHaveStandardMultipleFormEnding() const
{
	return properties.get<bool>(L"haveStandardMultipleFormEnding");
}

bool NounRecord::getHaveAlternativeMultipleFormEnding() const
{
	return properties.get<bool>(L"haveAlternativeMultipleFormEnding");
}

std::wstring NounRecord::getSpecialMultipleForm() const
{
	return properties.get<std::wstring>(L"specialMultipleForm");
}

bool NounRecord::getCanBeAnimate() const
{
	return properties.get<bool>(L"canBeAnimate");
}

bool NounRecord::getCanBeInanimate() const
{
	return properties.get<bool>(L"canBeInanimate");
}

void NounRecord::SetAllFields()
{
	properties.put(L"gender", L"NG_MALE");
	properties.put(L"haveSingleForm", false);
	properties.put(L"haveMultipleForm", false);
	properties.put(L"haveStandardMultipleForm", false);
	properties.put(L"haveStandardMultipleFormWithMissingLastVowel", false);
	properties.put(L"haveStandardMultipleFormEnding", false);
	properties.put(L"haveAlternativeMultipleFormEnding", false);

	properties.put(L"specialMultipleForm", "");

	properties.put(L"canBeAnimate", false);
	properties.put(L"canBeInanimate", false);
}

void NounRecord::SetAllFields(const std::vector<std::wstring>& lineArr)
{


	properties.put(L"gender", NormalizeNounGenderString(lineArr[2]));

	properties.put(L"haveSingleForm", lineArr[3] == L"1" ? true : false);
	properties.put(L"haveMultipleForm", lineArr[4] == L"1" ? true : false);
	properties.put(L"haveStandardMultipleForm", lineArr[5] == L"1" ? true : false);
	properties.put(L"haveStandardMultipleFormWithMissingLastVowel", lineArr[6] == L"1" ? true : false);
	properties.put(L"haveStandardMultipleFormEnding", lineArr[7] == L"1" ? true : false);
	properties.put(L"haveAlternativeMultipleFormEnding", lineArr[8] == L"1" ? true : false);

	properties.put(L"specialMultipleForm", lineArr[9]);

	properties.put(L"canBeAnimate", lineArr[10] == L"1" ? true : false);
	properties.put(L"canBeInanimate", lineArr[11] == L"1" ? true : false);
}


NounStruct::NounStruct()
/*
	: nounGrammaticalCase(NGC_P1_NOMINATIVE)
	, nounCount(NC_SINGULAR)
	, animated(false)*/
{

}

NounStruct::NounStruct(NounGrammaticalCase iNounGrammaticalCase, NounCount iNounCount, bool iAnimated)
/*
	: nounGrammaticalCase(iNounGrammaticalCase)
	, nounCount(iNounCount)
	, animated(iAnimated)
	*/
{
	modificatorTree.put(L"grammaticalCase", NounGrammaticalCaseToWString(iNounGrammaticalCase));
	modificatorTree.put(L"animated", iAnimated);
	modificatorTree.put(L"count", NounCountToWString(iNounCount));
}

/*
boost::property_tree::wptree NounStruct::GetModificators() const
{
	return modificatorTree;
}*/

NounPair::NounPair()
{
}

NounPair::NounPair(NounStruct iNounModificator, NounRecord iNoun)
	: nounModificator(iNounModificator)
	, noun(iNoun)
{
}

const WordModificatorInterface& NounPair::wordModificator() const
{
	return nounModificator;
}

const WordHolder& NounPair::word() const
{
	return noun;
}

std::wstring NounPair::getType()
{
	return L"noun";
}


NounDeclencion WStringToNounDeclencion(std::wstring str)
{

	if (str == L"FIRST_A_IFORM_INANIMATE")
	{
		return FIRST_A_IFORM_INANIMATE;
	}
	if (str == L"FIRST_A_IFORM_ANIMATE")
	{
		return FIRST_A_IFORM_ANIMATE;
	}
	if (str == L"FIRST_A_UFORM_INANIMATE")
	{
		return FIRST_A_UFORM_INANIMATE;
	}
	if (str == L"FIRST_A_UFORM_ANIMATE")
	{
		return FIRST_A_UFORM_ANIMATE;
	}
	if (str == L"FIRST_YA_FORM_INANIMATE")
	{
		return FIRST_YA_FORM_INANIMATE;
	}
	if (str == L"FIRST_YA_FORM_ANIMATE")
	{
		return FIRST_YA_FORM_ANIMATE;
	}

	if (str == L"SECOND_MALE_IFORM_INANIMATE")
	{
		return SECOND_MALE_IFORM_INANIMATE;
	}
	if (str == L"SECOND_MALE_IFORM_ANIMATE")
	{
		return SECOND_MALE_IFORM_ANIMATE;
	}
	if (str == L"SECOND_MALE_UFORM_INANIMATE")
	{
		return SECOND_MALE_UFORM_INANIMATE;
	}
	if (str == L"SECOND_MALE_UFORM_ANIMATE")
	{
		return SECOND_MALE_UFORM_ANIMATE;
	}
	if (str == L"SECOND_MALE_SSFORM_INANIMATE")
	{
		return SECOND_MALE_SSFORM_INANIMATE;
	}
	if (str == L"SECOND_MALE_SSFORM_ANIMATE")
	{
		return SECOND_MALE_SSFORM_ANIMATE;
	}

	if (str == L"SECOND_I_SHORT_INANIMATE")
	{
		return SECOND_I_SHORT_INANIMATE;
	}
	if (str == L"SECOND_I_SHORT_ANIMATE")
	{
		return SECOND_I_SHORT_ANIMATE;
	}


	if (str == L"SECOND_NEUTRAL_E_FORM")
	{
		return SECOND_NEUTRAL_E_FORM;
	}
	if (str == L"SECOND_NEUTRAL_O_FORM")
	{
		return SECOND_NEUTRAL_O_FORM;
	}
	if (str == L"THIRD_FORM_INANIMATE")
	{
		return THIRD_FORM_INANIMATE;
	}
	if (str == L"THIRD_FORM_ANIMATE")
	{
		return THIRD_FORM_ANIMATE;
	}

	std::cout << "Error in WStringToNounDeclencion!" << std::endl;
	return FIRST_A_IFORM_INANIMATE;

}


bool NounEndingDivision::operator<(const NounEndingDivision& other) const
{
	if (base != other.base)
	{
		return base < other.base;
	}
	else
	{
		if (ending != other.ending)
		{
			return ending < other.ending;
		}
		else
		{
			return divisionCase < other.divisionCase;
		}
	}
}

/*
bool NounStruct::operator<(const NounStruct& other) const
{
	if (nounGrammaticalCase != other.nounGrammaticalCase)
	{
		return nounGrammaticalCase < other.nounGrammaticalCase;
	}
	else
	{
		if (nounCount != other.nounCount)
		{
			return nounCount < other.nounCount;
		}
		else
		{
			
				return animated < other.animated;
		
		}
	}
}
*/

std::vector<std::wstring> GetAllNounEndingArr()
{
	std::vector<std::wstring> result
	{
		L"",

		L"й",
		L"ев",

		L"а",
		L"и",
		L"е",
		L"у",
		L"ой",
		L"ы",
		L"ом",
		L"ь",
		L"я",
		L"ю",
		L"ем",
		L"о",
		L"ью",
		L"ам",
		L"ами",
		L"ах",
		L"ов",
		L"ей",
		L"ям",
		L"ях",
		L"я",
		L"ями",
	};

	return result;
}

bool NounIsInDictionary(std::wstring nounNominative)
{
	for (auto& noun : NounRecordArr)
	{
		if (noun.GetWord() == nounNominative)
		{
			return true;
		}
	}

	return false;

}

std::wstring convertToStandardPluralForm(std::wstring s)
{
	std::wstring pluralForm = s;

	if (pluralForm[pluralForm.size() - 1] == L'а' && charIsIFormConsolant(pluralForm[pluralForm.size() - 2]))
	{
		pluralForm[pluralForm.size() - 1] = L'и';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'а' && charIsUFormConsolant(pluralForm[pluralForm.size() - 2]))
	{
		pluralForm[pluralForm.size() - 1] = L'ы';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'я')
	{
		pluralForm[pluralForm.size() - 1] = L'и';
	}
	else if (charIsIFormConsolant(pluralForm[pluralForm.size() - 1]))
	{
		pluralForm += L'и';
	}
	else if (charIsUFormConsolant(pluralForm[pluralForm.size() - 1]))
	{
		pluralForm += L'ы';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'ь')
	{
		pluralForm[pluralForm.size() - 1] = L'и';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'й')
	{
		pluralForm[pluralForm.size() - 1] = L'и';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'о')
	{
		pluralForm[pluralForm.size() - 1] = L'а';
	}
	else if (pluralForm[pluralForm.size() - 1] == L'е')
	{
		pluralForm[pluralForm.size() - 1] = L'я';
	}
	else
	{
		std::cout << "Error in convertToStandardPluralForm" << std::endl;
	}

	return pluralForm;
}


std::wstring convertFromStandardToAlternativePluralForm(std::wstring s)
{
	if (s[s.size() - 1] == L'и')
	{
		s[s.size() - 1] = L'я';
	}
	if (s[s.size() - 1] == L'ы')
	{
		s[s.size() - 1] = L'а';
	}

	return s;
}

std::set<std::wstring> getPluralForm(NounRecord noun)
{
	std::set<std::wstring> result;

	if (noun.getSpecialMultipleForm() != L"")
	{
		result.insert(noun.getSpecialMultipleForm());
	}

	if (noun.getHaveSingleForm())
	{
		if (noun.getHaveStandardMultipleForm())
		{
			std::wstring pluralForm = convertToStandardPluralForm(noun.GetWord());

			if (noun.getHaveStandardMultipleFormEnding())
			{
				result.insert(pluralForm);
			}

		
			if (noun.getHaveAlternativeMultipleFormEnding())
			{
				result.insert(convertFromStandardToAlternativePluralForm(pluralForm));
			}

		}
		
		if (noun.getHaveStandardMultipleFormWithMissingLastVowel())
		{
			std::wstring pluralForm = convertToStandardPluralForm(noun.GetWord());

			wchar_t prevsschar = pluralForm[pluralForm.size() - 4];

			if (charIsMissingVowelSoftenerConsolant(prevsschar))
			{
				pluralForm[pluralForm.size() - 3] = L'ь';
			}
			else
			{
				pluralForm.erase(pluralForm.begin() + pluralForm.size() - 3);
			}

			if (noun.getHaveStandardMultipleFormEnding())
			{
				result.insert(pluralForm);
			}

			
			if (noun.getHaveAlternativeMultipleFormEnding())
			{
				result.insert(convertFromStandardToAlternativePluralForm(pluralForm));
			}
		}



	}
	else
	{
		result.insert(noun.GetWord());
	}
		

	return result;
}

bool NounPluralFormIsInDictionary(std::wstring nounNominativePlural)
{
	for (auto& noun : NounRecordArr)
	{ 
		if (noun.getHaveMultipleForm())
		{
			if (noun.precalculatedNominativePluralSet.count(nounNominativePlural) != 0)
			{
				return true;
			}
		}
	}

	return false;
}

NounRecord GetNounRecordFromDictionary(std::wstring nounNominative)
{
	for (auto& noun : NounRecordArr)
	{
		if (noun.GetWord() == nounNominative)
		{
			return noun;
		}
	}

	return{};

}

NounRecord GetNounRecordFromDictionary_ByPluralForm(std::wstring nounNominativePlural)
{
	for (auto& noun : NounRecordArr)
	{
		if (noun.getHaveMultipleForm())
		{
			if (noun.precalculatedNominativePluralSet.count(nounNominativePlural) != 0)
			{
				return noun;
			}
		}
	}
	return{};

}




bool charIsMissingVowelSoftenerConsolant(wchar_t c)
{
	//This test belongs to missing vowel case.
	//лев -> львы (because л, then е replaced by soft sign)
	//немец -> немцы (because not л, the е is not replaced, just missing)

	std::wstring consolants = L"л";

	for (wchar_t ic : consolants)
	{
		if (c == ic)
		{
			return true;
		}
	}

	return false;
}


std::set<NounEndingDivision> getPossibleNounEndingDivisionSet(std::wstring noun)
{
	std::set<NounEndingDivision> result;

	auto allNounEndingArr = GetAllNounEndingArr();

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

			

			if ((charIsVowel(nounBase[nounBase.size() - 1])) || //Might be exact the й case
				(charIsConsolant(nounBase[nounBase.size() - 1]) || nounBase[nounBase.size() - 1] == L'ь' || nounBase[nounBase.size() - 1] == L'ъ'))
			{
				result.insert({ nounBase, ending, NounEndingDivision::DC_COMMON });
			}

			//Check missed vowel (simple case)
			if (charIsConsolant(nounBase[nounBase.size() - 1]) && charIsConsolant(nounBase[nounBase.size() - 2]))
			{
				result.insert({ nounBase, ending, NounEndingDivision::DC_LOST_VOWEL_O });
			}

			if (charIsConsolant(nounBase[nounBase.size() - 1]) && nounBase[nounBase.size() - 2] == L'ь' && charIsMissingVowelSoftenerConsolant(nounBase[nounBase.size() - 3]))
			{
				result.insert({ nounBase, ending, NounEndingDivision::DC_LOST_VOWEL_E });
			}

			if (charIsConsolant(nounBase[nounBase.size() - 1]) && charIsConsolant(nounBase[nounBase.size() - 2]) && !charIsMissingVowelSoftenerConsolant(nounBase[nounBase.size() - 2]))
			{
				result.insert({ nounBase, ending, NounEndingDivision::DC_LOST_VOWEL_E });
			}

		}
	}

	return result;
}



std::vector<NounTuple> GetPossibleNounTupleArr(std::wstring nounEnding)
{
	std::vector<NounTuple> result;

	for (auto& noun : nounDeclencionCaseTable)
	{
		for (int i = 0; i < NGC_SIZE * NC_SIZE; i++)
		{
			if (noun.grammaticalCaseTable[i].ending.count(nounEnding) != 0)
			{
				result.push_back(NounTuple{ noun.nounDeclencion, noun.grammaticalCaseTable[i].count, noun.grammaticalCaseTable[i].grammaticalCase });
			}
		}

	}
	return result;
}

std::vector<NounTuple> FilterNounTupleArrByNounDeclentionSet(std::vector<NounTuple> nounTupleArr, std::set<NounDeclencion> filter)
{
	std::vector<NounTuple> result;

	for (auto nounTuple : nounTupleArr)
	{
		if (filter.count(std::get<0>(nounTuple)) != 0)
		{
			result.push_back(nounTuple);
		}
	}

	return result;
}




std::set<std::wstring> GetNounNoninative(std::wstring nounBase, NounDeclencion nounDeclencion, NounCount nounCount)
{
	std::set<std::wstring> result;

	NounDeclencionCaseTableRecord nounDeclencionCaseTableRecord = nounDeclencionCaseTable[static_cast<int>(nounDeclencion)];

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

	return result;
}

std::wstring GetNounNoninativeSpecialPluralA(std::wstring nounBase, NounDeclencion nounDeclencion)
{

	if (charIsIFormConsolant(nounBase[nounBase.size() - 1]))
	{
		return nounBase + L"я";
	}

	if (charIsUFormConsolant(nounBase[nounBase.size() - 1]))
	{
		return nounBase + L"а";
	}

	if (charIsVowel(nounBase[nounBase.size() - 1]))
	{
		return nounBase + L"я";
	}

	std::cout << "Error in GetNounNoninative" << std::endl;

	return L"";
}



bool FirstAIFormInanimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeInanimate() && (nounRecord.GetLastChar() == L'а' && charIsIFormConsolant(nounRecord.GetPrevLastChar()));
}

bool FirstAIFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeAnimate() && (nounRecord.GetLastChar() == L'а' && charIsIFormConsolant(nounRecord.GetPrevLastChar()));
}

bool FirstAIFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeInanimate() && (
		(nounRecord.GetLastChar() == L'а' && charIsIFormConsolant(nounRecord.GetPrevLastChar())) ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'и' && charIsIFormConsolant(nounRecord.GetPrevLastChar()))
		);
}

bool FirstAIFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeAnimate() && (
		(nounRecord.GetLastChar() == L'а' && charIsIFormConsolant(nounRecord.GetPrevLastChar())) ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'и' && charIsIFormConsolant(nounRecord.GetPrevLastChar()))
		);
}


bool FirstAUFormInanimateSingularCondition(const NounRecord& nounRecord) 
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeInanimate() && (nounRecord.GetLastChar() == L'а' && charIsUFormConsolant(nounRecord.GetPrevLastChar()));
}

bool FirstAUFormAnimateSingularCondition(const NounRecord& nounRecord) 
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeAnimate() && (nounRecord.GetLastChar() == L'а' && charIsUFormConsolant(nounRecord.GetPrevLastChar()));
}


bool FirstAUFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeInanimate() && (
		(nounRecord.GetLastChar() == L'а' && charIsUFormConsolant(nounRecord.GetPrevLastChar())) ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'ы' && charIsUFormConsolant(nounRecord.GetPrevLastChar()))
		);
}

bool FirstAUFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeAnimate() && (
		(nounRecord.GetLastChar() == L'а' && charIsUFormConsolant(nounRecord.GetPrevLastChar())) ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'ы' && charIsUFormConsolant(nounRecord.GetPrevLastChar()))
		);
}


bool FirstYaFormInanimateSingularCondition(const NounRecord& nounRecord) 
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeInanimate() && (nounRecord.GetLastChar() == L'я');
}

bool FirstYaFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeAnimate() && (nounRecord.GetLastChar() == L'я');
}

bool FirstYaFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeInanimate() && (
		(nounRecord.GetLastChar() == L'я') ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'и')
		);
}

bool FirstYaFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getCanBeAnimate() && (
		(nounRecord.GetLastChar() == L'я') ||
		!nounRecord.getHaveSingleForm() && (nounRecord.GetLastChar() == L'и')
		);
}


// Second declention

bool SecondMaleIFormInanimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && charIsIFormConsolant(nounRecord.GetLastChar());
}

bool SecondMaleIFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && (
		charIsIFormConsolant(nounRecord.GetLastChar()) ||
		!nounRecord.getHaveSingleForm() && charIsIFormConsolant(nounRecord.GetPrevLastChar()) && nounRecord.GetLastChar() == L'и'
		);

}


bool SecondMaleIFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && charIsIFormConsolant(nounRecord.GetLastChar());
}

bool SecondMaleIFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && (
		charIsIFormConsolant(nounRecord.GetLastChar()) ||
		!nounRecord.getHaveSingleForm() && charIsIFormConsolant(nounRecord.GetPrevLastChar()) && nounRecord.GetLastChar() == L'и'
		);
}


bool SecondMaleUFormInanimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && charIsUFormConsolant(nounRecord.GetLastChar());
}

bool SecondMaleUFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && (
		charIsUFormConsolant(nounRecord.GetLastChar()) ||
		!nounRecord.getHaveSingleForm() && charIsUFormConsolant(nounRecord.GetPrevLastChar()) && nounRecord.GetLastChar() == L'ы'
		);
}


bool SecondMaleUFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && charIsUFormConsolant(nounRecord.GetLastChar());
}

bool SecondMaleUFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && (
		charIsUFormConsolant(nounRecord.GetLastChar()) ||
		!nounRecord.getHaveSingleForm() && charIsUFormConsolant(nounRecord.GetPrevLastChar()) && nounRecord.GetLastChar() == L'ы'
		);
}


bool SecondMaleSSFormInanimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && nounRecord.GetLastChar() == L'ь';
}

bool SecondMaleSSFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeInanimate() && (
		nounRecord.GetLastChar() == L'ь' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'и'
		);
}


bool SecondMaleSSFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && nounRecord.GetLastChar() == L'ь';
}

bool SecondMaleSSFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_MALE && nounRecord.getCanBeAnimate() && (
		nounRecord.GetLastChar() == L'ь' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'и'
		);
}


bool SecondIShortInanimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeInanimate() && nounRecord.GetLastChar() == L'й';
}

bool SecondIShortAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeAnimate() && nounRecord.GetLastChar() == L'й';
}

bool SecondIShortInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeInanimate() && nounRecord.GetLastChar() == L'й';
}

bool SecondIShortAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getCanBeAnimate() && nounRecord.GetLastChar() == L'й';
}





bool SecondNeutralEFormSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_NEUTRAL && nounRecord.GetLastChar() == L'е';
}

bool SecondNeutralEFormPluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_NEUTRAL && (
		nounRecord.GetLastChar() == L'е' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'я'
		);
}

bool SecondNeutralOFormSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_NEUTRAL && nounRecord.GetLastChar() == L'о';
}

bool SecondNeutralOFormPluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_NEUTRAL && (
		nounRecord.GetLastChar() == L'о' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'а'
		);
}

bool ThirdFormInanimateSingularCondition(const NounRecord& nounRecord) 
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_FEMALE && nounRecord.getCanBeInanimate() && nounRecord.GetLastChar() == L'ь';
}

bool ThirdFormAnimateSingularCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveSingleForm() && nounRecord.getGender() == NG_FEMALE && nounRecord.getCanBeAnimate() && nounRecord.GetLastChar() == L'ь';
}


bool ThirdFormInanimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_FEMALE && nounRecord.getCanBeInanimate() && (
		nounRecord.GetLastChar() == L'ь' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'и'
		);
}

bool ThirdFormAnimatePluralCondition(const NounRecord& nounRecord)
{
	return nounRecord.getHaveMultipleForm() && nounRecord.getGender() == NG_FEMALE && nounRecord.getCanBeAnimate() && (
		nounRecord.GetLastChar() == L'ь' ||
		!nounRecord.getHaveSingleForm() && nounRecord.GetLastChar() == L'и'
		);
}


std::map<std::pair<NounDeclencion, NounCount>, std::function < bool(const NounRecord&) >> DeclentionConditionMap;

void SetupDeclentionMap()
{

	DeclentionConditionMap[{FIRST_A_IFORM_INANIMATE, NC_SINGULAR}] = std::bind(FirstAIFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_IFORM_ANIMATE, NC_SINGULAR}] = std::bind(FirstAIFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_IFORM_INANIMATE, NC_PLURAL}] = std::bind(FirstAIFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_IFORM_ANIMATE, NC_PLURAL}] = std::bind(FirstAIFormAnimatePluralCondition, std::placeholders::_1);

	DeclentionConditionMap[{FIRST_A_UFORM_INANIMATE, NC_SINGULAR}] = std::bind(FirstAUFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_UFORM_ANIMATE, NC_SINGULAR}] = std::bind(FirstAUFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_UFORM_INANIMATE, NC_PLURAL}] = std::bind(FirstAUFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_A_UFORM_ANIMATE, NC_PLURAL}] = std::bind(FirstAUFormAnimatePluralCondition, std::placeholders::_1);

	DeclentionConditionMap[{FIRST_YA_FORM_INANIMATE, NC_SINGULAR}] = std::bind(FirstYaFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_YA_FORM_ANIMATE, NC_SINGULAR}] = std::bind(FirstYaFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_YA_FORM_INANIMATE, NC_PLURAL}] = std::bind(FirstYaFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{FIRST_YA_FORM_ANIMATE, NC_PLURAL}] = std::bind(FirstYaFormAnimatePluralCondition, std::placeholders::_1);

	//Second form
	DeclentionConditionMap[{SECOND_MALE_IFORM_INANIMATE, NC_SINGULAR}] = std::bind(SecondMaleIFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_IFORM_INANIMATE, NC_PLURAL}] = std::bind(SecondMaleIFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_IFORM_ANIMATE, NC_SINGULAR}] = std::bind(SecondMaleIFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_IFORM_ANIMATE, NC_PLURAL}] = std::bind(SecondMaleIFormAnimatePluralCondition, std::placeholders::_1);

	DeclentionConditionMap[{SECOND_MALE_UFORM_INANIMATE, NC_SINGULAR}] = std::bind(SecondMaleUFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_UFORM_INANIMATE, NC_PLURAL}] = std::bind(SecondMaleUFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_UFORM_ANIMATE, NC_SINGULAR}] = std::bind(SecondMaleUFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_UFORM_ANIMATE, NC_PLURAL}] = std::bind(SecondMaleUFormAnimatePluralCondition, std::placeholders::_1);

	DeclentionConditionMap[{SECOND_MALE_SSFORM_INANIMATE, NC_SINGULAR}] = std::bind(SecondMaleSSFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_SSFORM_INANIMATE, NC_PLURAL}] = std::bind(SecondMaleSSFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_SSFORM_ANIMATE, NC_SINGULAR}] = std::bind(SecondMaleSSFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_MALE_SSFORM_ANIMATE, NC_PLURAL}] = std::bind(SecondMaleSSFormAnimatePluralCondition, std::placeholders::_1);

	DeclentionConditionMap[{SECOND_I_SHORT_INANIMATE, NC_SINGULAR}] = std::bind(SecondIShortInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_I_SHORT_INANIMATE, NC_PLURAL}] = std::bind(SecondIShortInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_I_SHORT_ANIMATE, NC_SINGULAR}] = std::bind(SecondIShortAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_I_SHORT_ANIMATE, NC_PLURAL}] = std::bind(SecondIShortAnimatePluralCondition, std::placeholders::_1);
	

	DeclentionConditionMap[{SECOND_NEUTRAL_E_FORM, NC_SINGULAR}] = std::bind(SecondNeutralEFormSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_NEUTRAL_E_FORM, NC_PLURAL}] = std::bind(SecondNeutralEFormPluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_NEUTRAL_O_FORM, NC_SINGULAR}] = std::bind(SecondNeutralOFormSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{SECOND_NEUTRAL_O_FORM, NC_PLURAL}] = std::bind(SecondNeutralOFormPluralCondition, std::placeholders::_1);


	DeclentionConditionMap[{THIRD_FORM_INANIMATE, NC_SINGULAR}] = std::bind(ThirdFormInanimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{THIRD_FORM_ANIMATE, NC_SINGULAR}] = std::bind(ThirdFormAnimateSingularCondition, std::placeholders::_1);
	DeclentionConditionMap[{THIRD_FORM_INANIMATE, NC_PLURAL}] = std::bind(ThirdFormInanimatePluralCondition, std::placeholders::_1);
	DeclentionConditionMap[{THIRD_FORM_ANIMATE, NC_PLURAL}] = std::bind(ThirdFormAnimatePluralCondition, std::placeholders::_1);


}


bool NounFitsDeclention(NounRecord nounRecord, NounTuple nounTuple)
{

	NounDeclencion nounDeclencion = std::get<0>(nounTuple);
	NounCount nounCount = std::get<1>(nounTuple);

	bool standardDeclention = DeclentionConditionMap[{nounDeclencion, nounCount}](nounRecord);

	if (standardDeclention)
	{
		return true;
	}

	return false;
}


bool IsDeclencionSecondType(NounDeclencion nounDeclention)
{
	switch (nounDeclention)
	{
	case SECOND_MALE_IFORM_INANIMATE:
	case SECOND_MALE_IFORM_ANIMATE:
	case SECOND_MALE_UFORM_INANIMATE:
	case SECOND_MALE_UFORM_ANIMATE:
	case SECOND_MALE_SSFORM_INANIMATE:
	case SECOND_MALE_SSFORM_ANIMATE:
	case SECOND_I_SHORT_INANIMATE:
	case SECOND_I_SHORT_ANIMATE:
		return true;
		break;
	default:
		return false;
		break;
	}
}

bool IsDeclencionAnimated(NounDeclencion nounDeclention)
{
	switch (nounDeclention)
	{
	case FIRST_A_IFORM_ANIMATE:
	case FIRST_A_UFORM_ANIMATE:
	case FIRST_YA_FORM_ANIMATE:
	case SECOND_MALE_IFORM_ANIMATE:
	case SECOND_MALE_UFORM_ANIMATE:
	case SECOND_MALE_SSFORM_ANIMATE:
	case SECOND_I_SHORT_ANIMATE:
	case THIRD_FORM_ANIMATE:
		return true;
		break;
	default:
		return false;
		break;
	}
}

bool StandardTest(NounTuple nounTuple, NounRecord nounRecord)
{
	return NounFitsDeclention(nounRecord, nounTuple);
}

bool LostVowelOTest(NounTuple nounTuple, NounRecord nounRecord)
{
	return nounRecord.getHaveStandardMultipleFormWithMissingLastVowel();
}

bool LostVowelETest(NounTuple nounTuple, NounRecord nounRecord)
{
	return nounRecord.getHaveStandardMultipleFormWithMissingLastVowel();
}

std::map<NounEndingDivision::DivisionCase, std::function<std::wstring(std::wstring)>> DivisionCaseNounModificatorMap;
std::map<NounEndingDivision::DivisionCase, std::function<std::wstring(std::wstring)>> DivisionCaseEndingModificatorMap;

std::map < NounEndingDivision::DivisionCase, std::function < bool(NounTuple) >> DivisionCaseNounTupleFilterMap;

std::map < NounEndingDivision::DivisionCase, std::function < bool(NounTuple, NounRecord) >> DivisionCaseNounTupleRecordFilterMap;


void FillDivisionCaseMaps()
{
	DivisionCaseNounModificatorMap[NounEndingDivision::DC_COMMON] = [](std::wstring s){ return s; };

	DivisionCaseNounModificatorMap[NounEndingDivision::DC_LOST_VOWEL_O] = [](std::wstring s)
	{
		return std::wstring(s.begin(), s.end() - 2) + L"o" + s[s.size() - 1];
	};

	DivisionCaseNounModificatorMap[NounEndingDivision::DC_LOST_VOWEL_E] = [](std::wstring s)
	{
		if (s[s.size() - 2] == L'ь')
		{
			return std::wstring(s.begin(), s.end() - 2) + L"е" + s[s.size() - 1];
		}
		else
		{
			return std::wstring(s.begin(), s.end() - 1) + L"е" + s[s.size() - 1];
		}
	};

	
	DivisionCaseEndingModificatorMap[NounEndingDivision::DC_COMMON] = [](std::wstring s){ return s; };
	DivisionCaseEndingModificatorMap[NounEndingDivision::DC_LOST_VOWEL_O] = DivisionCaseEndingModificatorMap[NounEndingDivision::DC_COMMON];
	DivisionCaseEndingModificatorMap[NounEndingDivision::DC_LOST_VOWEL_E] = DivisionCaseEndingModificatorMap[NounEndingDivision::DC_COMMON];



	DivisionCaseNounTupleFilterMap[NounEndingDivision::DC_COMMON] = [](NounTuple t) { return true; };

	DivisionCaseNounTupleFilterMap[NounEndingDivision::DC_LOST_VOWEL_O] = [](NounTuple t)
	{
		return (std::get<1>(t) == NC_PLURAL) ||
			((std::get<2>(t) != NGC_P1_NOMINATIVE) &&
			(!(std::get<2>(t) == NGC_P4_ACCUSATIVE && !IsDeclencionAnimated(std::get<0>(t)))));
	};

	DivisionCaseNounTupleFilterMap[NounEndingDivision::DC_LOST_VOWEL_E] = DivisionCaseNounTupleFilterMap[NounEndingDivision::DC_LOST_VOWEL_O];




	DivisionCaseNounTupleRecordFilterMap[NounEndingDivision::DC_COMMON] = [](NounTuple t, NounRecord r)
	{
		return (r.getHaveStandardMultipleForm() || std::get<1>(t) == NC_SINGULAR) && StandardTest(t, r);
	};

	DivisionCaseNounTupleRecordFilterMap[NounEndingDivision::DC_LOST_VOWEL_O] = [](NounTuple t, NounRecord r)
	{
		return LostVowelOTest(t, r) && StandardTest(t, r);
	};

	DivisionCaseNounTupleRecordFilterMap[NounEndingDivision::DC_LOST_VOWEL_E] = [](NounTuple t, NounRecord r)
	{
		return LostVowelETest(t, r) && StandardTest(t, r);
	};

	

}


void AddNounPairIfNotAdded(std::vector<std::shared_ptr<WordPairInterface>>& result, const NounTuple& nounTuple, const NounRecord& nounRecord)
{
	WordPairInterface* nPair = new NounPair(NounStruct(std::get<2>(nounTuple), std::get<1>(nounTuple), IsDeclencionAnimated(std::get<0>(nounTuple))), nounRecord);

	result.push_back(std::shared_ptr<WordPairInterface>(nPair));

}



void RecognizeNoun(std::wstring noun, std::vector<std::shared_ptr<WordPairInterface>>& wordPairArr)
{

	auto nounEndingDivisionArr = getPossibleNounEndingDivisionSet(noun);

	for (auto nounEndingDivision : nounEndingDivisionArr)
	{
		std::wstring nounBase = nounEndingDivision.base;
		std::wstring nounEnding = nounEndingDivision.ending;
		NounEndingDivision::DivisionCase dc = nounEndingDivision.divisionCase;

		std::wstring modifiedNounBase = DivisionCaseNounModificatorMap[dc](nounBase);
		std::wstring modifiedNounEnding = DivisionCaseEndingModificatorMap[dc](nounEnding);

		std::vector<NounTuple> possibleTupleArr = GetPossibleNounTupleArr(modifiedNounEnding);

		//Standard check
		for (NounTuple nounTuple : possibleTupleArr)
		{

			if (DivisionCaseNounTupleFilterMap[dc](nounTuple))
			{

				if (std::get<1>(nounTuple) == NC_SINGULAR)
				{

					std::set<std::wstring> nounNominaviteSingularSet = GetNounNoninative(modifiedNounBase, std::get<0>(nounTuple), NC_SINGULAR);

					for (auto& nn : nounNominaviteSingularSet)
					{

						if (NounIsInDictionary(nn))
						{

							NounRecord nounRecord = GetNounRecordFromDictionary(nn);

							if (DivisionCaseNounTupleRecordFilterMap[dc](nounTuple, nounRecord))
							{
								AddNounPairIfNotAdded(wordPairArr, nounTuple, nounRecord);
							}
						}

					}
				}
				else
				{

					std::set<std::wstring> nounNominavitePluralSet = GetNounNoninative(nounBase, std::get<0>(nounTuple), NC_PLURAL);

					//Check all plural forms

					for (auto& nn : nounNominavitePluralSet)
					{

						if (NounPluralFormIsInDictionary(nn))
						{
							NounRecord nounRecord = GetNounRecordFromDictionary_ByPluralForm(nn);

							if (DivisionCaseNounTupleRecordFilterMap[dc](nounTuple, nounRecord))
							{
								AddNounPairIfNotAdded(wordPairArr, nounTuple, nounRecord);
							}
						}
					}
				}

			}
		}
	}



}

NounDeclencion CalculateNounDeclention(NounRecord nounRecord)
{

	//Xperimental -- need to find if here might be more than 1 declention

	for (auto& i : DeclentionConditionMap)
	{
		if (i.second(nounRecord))
		{
			return i.first.first;
		}
	}

	std::cout << "Error in CalculateNounDeclention" << std::endl;

	return{};
}


void CalculatePluralForm()
{
	for (auto& nounRecord : NounRecordArr)
	{
		nounRecord.precalculatedNominativePluralSet = getPluralForm(nounRecord);
	}
}


void LoadNounDeclencionCaseTable()
{

	nounDeclencionCaseTable.clear();

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

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

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

	if (f.is_open())
	{

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

		std::vector<GrammaticalTableRecord> currentGrammaticalCaseTable;
		std::wstring currentNounDeclencion;
		std::wstring currentNounCount;

		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 (currentNounDeclencion == L"")
				{
					currentNounDeclencion = string_to_wstring(lineArr[0]);
				}
				else
				{
					nounDeclencionCaseTable.push_back(NounDeclencionCaseTableRecord{ WStringToNounDeclencion(currentNounDeclencion), currentGrammaticalCaseTable });

					currentNounDeclencion = string_to_wstring(lineArr[0]);

					currentGrammaticalCaseTable.clear();
				}

			}

			if (lineArr[1] != "")
			{
				currentNounCount = 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(currentNounCount),
				WStringToNounGrammaticalCase(string_to_wstring(lineArr[2])),
				endingsSet
			});

		}
		//Add last one
		if (currentNounDeclencion != L"")
		{
			nounDeclencionCaseTable.push_back(NounDeclencionCaseTableRecord{ WStringToNounDeclencion(currentNounDeclencion), currentGrammaticalCaseTable });
		}

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


}

void LoadFrequentWordSet()
{


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

#else
	std::ifstream f("/home/devuser/workplace/rudict/frequent_nouns_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);
			NounRecord nounRecord(wline);

			NounRecordArr.push_back(nounRecord);

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


}

} //namespace NN