/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    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 LICENSE and COPYING files for more details.
*/

#include "../lex_headers.h"
#include "Makro.h"
#include "../FixME.h"
#include "../FlexME.h"
#include "ResourcesHolder.h"


namespace LexicalUnits
{
	namespace MEParser
	{
                boost::regex Makro::m_EqualBaseRegex(".*(equal)\\( *(base|orth)\\[([[:digit:]]+)\\] *, *\\{?(\\$[[:word:]]+)\\}? *\\).*",boost::regex::icase);
		boost::regex Makro::m_EqualInFlexRegex(".*(equal|in|inter)\\( *flex\\[([[:digit:]]+)\\] *, *\\{?([^\\}]+)\\}? *\\).*",boost::regex::icase);

		Makro::Makro(const std::string & name, const std::string &  var, std::vector<std::string> conlines)
		{
			Parse(name, var, conlines);
		}

		void Makro::Parse(const std::string &  name, const std::string &  var, std::vector<std::string> conlines)
		{
			m_sMakroName = name;
                        //std::cout << "name "<< name << std::endl;
			m_vCondition = conlines;
			ExtractVarsMask(var);
		}

		void Makro::ExtractVarsMask (const std::string & vars )
		{
			Corpus::CTag allTag = Corpus::CTag(Corpus::mask_All);
			allTag.Categories = Corpus::mask_All;

			// prepare var index
                        //std::map<std::string, size_t> varToId;
			std::vector<std::string> tok;
			boost::split(tok, vars, boost::is_any_of(","));
                        for(size_t i = 0; i <  tok.size();i++){
				tokens.insert( std::pair<std::string, size_t>( boost::trim_copy(tok[i]), i ));
				//std::cout << " - " <<  tok[i] << std::endl;
			}
			// find occurances (one for position, discard subsequent)of :
			// a) occurance of base form on position
			// b) occurance of flex mask on position
			std::map<int, std::string> posToVar;
			std::map<int, Corpus::CTag> posToMask;
			//assumption - if orths and bases used number of tokens is even
			int orthDisplacement = tokens.size() / 2;
			for(size_t i = 0; i < m_vCondition.size(); i++)
			{
                                boost::smatch matchBase; //, matchFlex;
				//check for base
				//std::cout << m_vCondition[i] << std::endl;
				if( boost::regex_match(m_vCondition[i],matchBase,m_EqualBaseRegex) )
				{
						int position = boost::lexical_cast<int>(matchBase[3].str());
						std::string base = matchBase[4];
						base = boost::trim_copy(base);
						std::string orthOrBase = matchBase[2];
						if( !posToVar.count(position) )
							posToVar[position] = base;
						int basePos = tokens[base];
						//std::cout << " orth or base |" <<  orthOrBase << "|" << std::endl;
						if(orthOrBase == "orth")
							basePos = basePos - orthDisplacement;
						m_vVarToBase.insert( std::pair<std::string, size_t>( base, basePos ));
				}

				//check for flex
				if( boost::regex_match(m_vCondition[i],matchBase,m_EqualInFlexRegex) )
				{

					int position = boost::lexical_cast<int>(matchBase[2].str());

					if( posToMask.count(position) )
						continue;
					//std::cout << " aa " << position << std::endl;
					std::string varstr = matchBase[3];

					std::vector<std::string> vars;
					boost::split(vars, varstr, boost::is_any_of(","));

					Corpus::TFlexClass flex = 0;
					for( size_t i = 0;i<vars.size() ;i++ )
						flex |= Corpus::CTag::GetFlexClass(vars[i]);

					posToMask[position] = Corpus::CTag(flex);
					posToMask[position].Categories = Corpus::mask_All;
				}
			}//for(size_t i = 0; i < m_vCondition.size(); i++)

			// fill in the actual data
			for(std::map<int, std::string>::iterator iter = posToVar.begin();
				iter != posToVar.end(); iter++)
			{
				int position = iter->first;
				std::string& var = iter->second;
				Corpus::CTag tag = posToMask[position];
								//std::cout << " var " << var << position << std::endl;
                                m_vVarToMask.push_back( std::pair<std::string, Corpus::CTag>(var,tag) );

				//std::cout << var << " -> " << std::hex << tag.FlexClass << ", " << tag.Categories<< std::endl;
			}

		}//void Makro::ExtractVarsMask (const std::string & vars )

		LexicalUnitPtr Makro::InstantiateUnit ( UnitStubPtr pStub )
		{
			switch(pStub->GetType())
			{
			case LexicalUnitType::FIX:
			case LexicalUnitType::FLEX:
				return CreateFlexOrFixUnit(pStub);
			default:
				throw std::runtime_error("Unimplemented or unknown type of lexical unit: "
					+ LexicalUnitType::ToString(pStub->GetType()));
			}

			return LexicalUnitPtr();
		}

		LexicalUnitPtr Makro::CreateFlexOrFixUnit( UnitStubPtr pStub )
		{
			LexicalUnit *instance = NULL;
			std::vector<boost::shared_ptr<Word> > constituents;
			std::map<std::string, std::string> varToBase;
			Corpus::CWordDictionary* dict = Corpus::CWordDictionary::GetInstance();


			int baseId = pStub->GetBaseIndex();
			Corpus::TFlexClass fc = pStub->GetFlexClass();

			std::stringstream opStream;
						//std::cout <<  pStub->GetNumberOfVars() << std::endl;
						//std::cout << " makro " << m_sMakroName
						//		<< " " << pStub->GetNumberOfVars() / 1.0<< std::endl;
			for(std::vector<std::string>::iterator iter = m_vCondition.begin();
                        iter != m_vCondition.end(); iter++)

				opStream << *iter << std::endl;

			std::string condition = opStream.str();

			opStream.clear();

			//creation of words to fill constituents vector
//                        //std::cout << " makro " << m_sMakroName << " " << pStub->GetNumberOfVars() / 1.0<< std::endl;
                        //for(std::vector<std::string>::iterator a = tokens.begin(); a != tokens.end(); a++ )
                          //  std::cout <<(*a)<<std::endl;
			
			for(size_t i = 0; i < m_vVarToMask.size(); i++)
			{
				std::string varName = m_vVarToMask[i].first;
			                
			//prepare constituents
				Corpus::CTag& tag = m_vVarToMask[i].second;
				//baseFormId must be a base not an orth form
				//std::cout << "suggested pos " << m_vVarToBase[varName] << " expected pos " << tokens[varName] << std::endl;
				int baseFormId = pStub->GetVarBaseId(m_vVarToBase[varName]);		
				boost::shared_ptr<Word> w = boost::shared_ptr<Word>(new Word(tag, baseFormId));
				constituents.push_back(w);
			//prepare condition
				int wordFormId = pStub->GetVarBaseId(tokens[varName]);
			
				std::string wordForm = "\"" + dict->GetAt(wordFormId) + "\"";
				//std::cout << "constituent no "<< i  << " is " << wordForm <<std::endl;

				boost::algorithm::replace_all(condition, varName, wordForm);
			}
					   // std::cout << " parsing condition "<<condition<<std::endl;
			JOSKIPI::Operators::CCondition
				*constraint = ResourcesHolder::GetJOSKIPIParser()->ParseCondition(condition);

			JOSKIPI::Operators::CCondition *head_search = pStub->RetrieveCondition();
			
			switch(pStub->GetType())
			{
			case LexicalUnitType::FIX:
				instance = new FixME(baseId, constituents, head_search, constraint, fc) ;
				break;
			case LexicalUnitType::FLEX:
				instance = new FlexME(baseId, constituents, head_search, constraint, fc) ;
				break;
			case LexicalUnitType::SIMPLE_WORD:
				throw std::runtime_error("Makro for simple word not handled!");
			case LexicalUnitType::FIXGAP:
				throw std::runtime_error("Makro for FIXGAP word not handled!");
			case LexicalUnitType::FLEXGAP:
				throw std::runtime_error("Makro for FLEXGAP word not handled!");
			}
                        //std::cout << " fof done" << pStub->GetNumberOfVars() << std::endl;
			return LexicalUnitPtr(instance);
			
		}
	}//namespace MEParser
}//namespace LexicalUnits

