/*
    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 "arch_headers.h"
#include "Config.h"
#include "SimpleWord.h"
#include "MEParser/MEParser.h"
#include "MEParser/ResourcesHolder.h"
#include "Options.h"

#include "boost/date_time/posix_time/posix_time.hpp"
namespace me_architect
{

	Config* Config::m_pInstance = NULL;
	Config::Config()
		: m_sWorkdir("./wd/")
	{
		m_bFilterDisamb = true;
		m_bChceckDocuments = false;
		m_OperatorParser = boost::shared_ptr<JOSKIPI::Parsers::CAntlrParser>(
			new JOSKIPI::Parsers::CAntlrParser()  );
		LexicalUnits::MEParser::ResourcesHolder::SetJOSKIPIParser(m_OperatorParser);
	}

	Config* Config::GetInstance()
	{
		return m_pInstance;
	}

	void Config::Delete()
	{
		if(m_pInstance)
		{
			delete m_pInstance;
			m_pInstance = NULL;
		}
	}

	Config* Config::Initalize(int ac, char *av[])
	{
		
		if(m_pInstance != NULL )
			Delete();

		m_pInstance = new Config();
		Options options(ac, av, m_pInstance);
		// load documents
		if(m_pInstance->m_CorpusType == XML)
			m_pInstance->LoadDocuments();
		// load lexical units
		m_pInstance->LoadRowUnits();
		// load operators (& column LU)
		m_pInstance->LoadOperators();
		return m_pInstance;
	}//void Config::Initalize()

	void Config::LoadRowUnits()
	{
		LoadRowME();
		std::cout << "Loading row single word lexical units" << std::endl;


		for(size_t i = 0; i < m_vRowTermFiles.size(); i++)
		{
			std::vector<LexicalUnits::LexicalUnitPtr> words;
			std::vector<std::string> tokens;
			Corpus::Parser::Tokenize(m_vRowTermFiles[i], tokens, "|");

			if(tokens.size() != 2 )
				throw std::runtime_error("format for opertors: lex_file1;flex1,...,lex_fileN;flexN");

			LoadSimpleWords(tokens[0], tokens[1], words);
			std::string flex = tokens[1];
			boost::trim(flex);
			for(std::vector<LexicalUnits::LexicalUnitPtr>::iterator i = words.begin(); i != words.end(); i++)
			{
				int b = (*i)->GetBaseId();
				std::string base = Corpus::CWordDictionary::GetInstance()->GetAt(b);
				m_vLexicalUnits.push_back(*i);
				rowLU.push_back(m_vLexicalUnits.size() - 1);

				rowToFlex[ rowLU.size() -1 ] = flex;
			}
		}

		std::cout << "Loaded " << rowLU.size() << " row units..." << std::endl;

	}

	void Config::LoadRowME()
	{
		std::cout << "Loading row ME" << std::endl;
		using LexicalUnits::LexicalUnitPtr;

		for(size_t fileNo = 0; fileNo < m_vRowUnits.size(); fileNo++)
		{
			LexicalUnits::MEParser::MEParser parser;
			parser.Parse(m_vRowUnits[fileNo].c_str());

			std::vector<LexicalUnitPtr> loaded = parser.GetUnits();

			for(size_t i = 0; i < loaded.size(); i++)
			{
				m_vLexicalUnits.push_back(loaded[i]);
				rowLU.push_back(m_vLexicalUnits.size() - 1);
				rowToFlex[ rowLU.size() -1 ] = Corpus::CTag::GetFlexClassString( loaded[i]->GetFlexClass() );
			}
		}

	}//void Config::LoadME()

	void Config::LoadOperators()
	{
		using namespace boost::posix_time;
		std::vector<std::string> tokens;

		std::cout << "Loading column lexical units and operators operators...\n";

		for(size_t i = 0; i < m_vOperatorDefines.size(); i++)
		{
			tokens.clear();
			Corpus::Parser::Tokenize(m_vOperatorDefines[i], tokens, "|");
			if(tokens.size() != 3 && tokens.size() != 2 )
				throw std::runtime_error("format for opertors: operator_file1;lex_file1[;flex1],...,operator_fileN;lex_fileN[;flexN]");

			std::string operatorString = LoadSingleOperatorString(tokens[0]);

			std::vector<LexicalUnits::LexicalUnitPtr> words;
			if(tokens.size() == 3)
				LoadSimpleWords(tokens[1], tokens[2], words);
			else
				LoadMEForColumns(tokens[1], words);
			Corpus::CWordDictionary *dict = Corpus::CWordDictionary::GetInstance();
			int counter = 0;
			int max = words.size();
			ptime start = microsec_clock::local_time();
			time_duration total_time;

			for(std::vector<LexicalUnits::LexicalUnitPtr>::iterator j = words.begin(); j != words.end(); j++)
			{
				const std::string &word = dict->GetAt((*j)->GetBaseId());
				std::string copy = boost::algorithm::replace_all_copy(operatorString, "^Zmienna", word);
				JOSKIPI::Operators::CCondition* condition = m_OperatorParser->ParseCondition(copy);
				m_vConditions.push_back(CondPtr(condition));
				counter++;

				//add LU
				m_vLexicalUnits.push_back(*j);
				//add column LU
				columnLU.push_back( m_vLexicalUnits.size() - 1 );
				if(tokens.size() == 3)
				{
					std::string flex = tokens[2];
					boost::trim(flex);
					columnToFlex[ columnLU.size() -1 ] = flex;
				}
				else columnToFlex[columnLU.size() -1 ] = Corpus::CTag::GetFlexClassString( (*j)->GetFlexClass() );

				ptime end = microsec_clock::local_time();
				time_duration len = end - start;
				if( len.total_milliseconds() > 5000)
				{
					total_time += len;
					long total_time_ms = total_time.total_milliseconds();
					double op_per_sec = double(counter*1000) / double(max);
					long eta_ms = (total_time_ms * (max-counter) ) / counter;
					std::cout << "Operator nr " << counter << " / " << words.size() 
						<< " | operator/sec = " << op_per_sec << " ETA: " << to_simple_string( milliseconds(eta_ms) ) 
						<< std::endl;
					start = microsec_clock::local_time();
				}

			}// for each LU
		}//for(size_t i = 0; i < m_vOperatorDefines.size(); i++)

		std::cout << "Loaded " << columnLU.size() << " column units..." << std::endl;
	}//void Config::LoadOperators()

	void Config::LoadMEForColumns(const std::string &filename, std::vector<LexicalUnits::LexicalUnitPtr> &out)
	{
		using LexicalUnits::LexicalUnitPtr;

		LexicalUnits::MEParser::MEParser parser;
		parser.Parse(filename.c_str());

		std::vector<LexicalUnitPtr> loaded = parser.GetUnits();

		for(size_t i = 0; i < loaded.size(); i++)
		{
			out.push_back(loaded[i]);
		}
	}//void Config::LoadMEForColumns(

	void Config::LoadSimpleWords(const std::string &filename, std::string& flex,
				std::vector<LexicalUnits::LexicalUnitPtr>&out)
	{
		//get flex clas
		Corpus::TFlexClass flex_class = GetFlexClass(flex);
		//load words
		std::vector<std::string> words;
		LoadStringVectror(filename, words);
		//conver to SimpleWord
		Corpus::CWordDictionary *dict = Corpus::CWordDictionary::GetInstance();
		for(size_t i = 0; i < words.size(); i++)
		{
			Corpus::CTag tag(flex_class);
			tag.Categories = Corpus::mask_All;
			std::string w = words[i];
			boost::trim(w);
			boost::shared_ptr<LexicalUnits::Word> word(new LexicalUnits::Word(tag, dict->Add( w) ));
			LexicalUnits::LexicalUnitPtr lu( new LexicalUnits::SimpleWord(word) );

			out.push_back(lu);
		}
	}//void Config::LoadSimpleWords(const std::string &filename, std::string& flex,

	Corpus::TFlexClass Config::GetFlexClass(const std::string& str)
	{
		Corpus::TFlexClass flex = 0;
		std::vector<std::string> flexes;
		boost::split(flexes, str, boost::is_any_of("|"));
		for( size_t i = 0;  i < flexes.size(); i++ )
		{
			std::string &f = flexes[i];
			boost::trim(f);
			flex |= Corpus::CTag::GetFlexClass(f);
		}

		return flex;
	}//Corpus::TFlexClass Config::GetFlexClass(const std::string& str)

	std::string Config::LoadSingleOperatorString(const std::string& filename)
	{
		std::ifstream file(filename.c_str());
		std::string line;
		std::stringstream str;

		if(!file)
			throw std::runtime_error("File not found: "+filename);

		while(file)
		{
			std::getline(file, line);
			str << line << std::endl;
		}

		return str.str();
	}//std::string Config::LoadSingleOperatorString(const std::string& filename)

	void Config::LoadDocuments()
	{
		std::cout << "Loading document list...\n";
		LoadStringVectror(m_sCorpusFile, documents);
		std::vector<std::string> toRemove;
		std::cout << "Number of documents: " << documents.size() << std::endl;
		if(m_bChceckDocuments)
		{
			std::cout << "Checking documnets...\n";

			for(size_t i = 0; i < documents.size(); i++)
			{
				if(!boost::filesystem::exists(documents[i]))
				{
					toRemove.push_back(documents[i]);
					std::cerr << "File " << documents[i] << " does not exists.\n";
				}

				if( i % 1000 == 0 )
					std::cout << "Checking document " << i << " / " << documents.size()
						<< std::endl;
			}//for(size_t i = 0; i < documents.size(); i++)
			if(toRemove.size() ==  0 )
				std::cout << "All documents exists...\n";
			else
				std::cerr << toRemove.size() << " documents does not exists\n";
		}//if(m_bChceckDocuments)
	}//void Config::LoadDocuments()

	void Config::LoadStringVectror(const std::string &filename, std::vector<std::string> &vec)
	{
		std::ifstream in(filename.c_str());
		if(!in)
			throw std::runtime_error("Cannot open file " + filename);
		std::string line;
		while(std::getline(in,line))
		{
			if(line.size() )
				vec.push_back(line);
		}//while(in)
	}//void Config::LoadStringVectror(const std::string &filename, std::vector<std::string> &vec)
}

