/*
    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 <boost/program_options.hpp>

namespace hal_architect
{

	Config* Config::m_pInstance = NULL;
	Config::Config(int ac, char *av[])
	{
		m_OperatorParser = boost::shared_ptr<JOSKIPI::Parsers::CAntlrParser>(
			new JOSKIPI::Parsers::CAntlrParser () );
		LexicalUnits::MEParser::ResourcesHolder::SetJOSKIPIParser(m_OperatorParser);
		m_bChceckDocuments = false;

		m_ContextType = Window;
		m_bFilterDisamb = true;
		InitalizeProgramOptions(ac, av);

	}

	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(ac, av);
		// load operators (& column LU)
		m_pInstance->LoadColumnUnits();
		// load lexical units
		m_pInstance->LoadRowUnits();

		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::LoadColumnUnits()
	{
		LoadColumnME();

		for(size_t i = 0; i < m_vColumnTermFiles.size(); i++)
		{
			std::vector<LexicalUnits::LexicalUnitPtr> words;
			std::vector<std::string> tokens;
			Corpus::Parser::Tokenize(m_vColumnTermFiles[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);
				columnLU.push_back(m_vLexicalUnits.size() - 1);
				columnToFlex[ columnLU.size() -1 ] = flex;
			}
		}

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

	void Config::LoadRowME()
	{
		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);
			}
		}

	}//void Config::LoadME()

	void Config::LoadColumnME()
	{
		using LexicalUnits::LexicalUnitPtr;

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

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

			for(size_t i = 0; i < loaded.size(); i++)
			{
				m_vLexicalUnits.push_back(loaded[i]);
				columnLU.push_back(m_vLexicalUnits.size() - 1);
			}
		}

	}//void Config::LoadME()

	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();
		std::set<std::string> wordsSet;
		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) );
			wordsSet.insert(w);
			out.push_back(lu);
		}
		if(wordsSet.size()!=out.size())
			std::cerr<<"Probable "<<out.size()-wordsSet.size()<<" duplicates in lexical units.\n";
	}//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)


	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)
	void Config::InitalizeProgramOptions(int ac, char* av[])
	{
		namespace po = boost::program_options;
		std::string configfile, sMode;
		
		po::options_description cmdline("Command line only options", 100);
		po::options_description basic("Basic options", 100);

		cmdline.add_options()
			("help", "Prints this message")
			("cfg-file", po::value<std::string>(&configfile)->default_value(std::string("hal_architect.cfg")), "Config file")
			;

		basic.add_options()
			("corpus", po::value<std::vector<std::string> >(& m_sCorpusFiles), "File with paths to corporas")
			("info", po::value<std::string>(& m_sCorpusInfo), "Corpus description")
			("workdir", po::value<std::string>(& workdir), "Workdir (output dir for matrix)")
			("matrix-name", po::value<std::string>(& m_sMatrixName), "Matrix name" )
			("row-simple-words", po::value<std::vector<std::string> >(& m_vRowTermFiles ), "Single word lexical units for rows, format:\n"
			 "file_with_units|flex_class")
			("row-me", po::value<std::vector<std::string> >(& m_vRowUnits), "Files with multiwords expresions for rows")
			("column-simple-words", po::value<std::vector<std::string> >(& m_vColumnTermFiles ), "Single word lexical units for rows, format:\n"
			 "file_with_units|flex_class")
			("column-me", po::value<std::vector<std::string> >(& m_vColumnUnits), "Files with multiwords expresions for columns")
			("mode", po::value<std::string>(&sMode)->default_value("window"), "Mode, one of: window, sentence, hal")
			("left-window", po::value<size_t>(&m_stWindowLeft)->default_value(5), "Size of left part of window")
			("right-window", po::value<size_t>(&m_stWindowRight)->default_value(5), "Size of right part of window")
			("dist-fun", po::value<std::string>(&m_sDistanceFunction)->default_value("one"), "Distance function, one of: one, log, len, rev-log")
			;

		po::variables_map vm;
		po::options_description all("",100);
		
		all.add(cmdline).add(basic);

		po::store(po::parse_command_line(ac, av, all), vm);
		po::notify(vm); 

		if( vm.count("help") )
		{
			std::cout << "HAL_ARCHITECT, by Bartosz Broda, bartosz.broda@pwr.wroc.pl\n";
			std::cout << all << std::endl;
			exit(0);
		}
		std::ifstream cfgfile( configfile.c_str() );

		if(cfgfile)
		{
			po::store(po::parse_config_file(cfgfile, basic), vm);
			po::notify(vm);
		}
		else if( vm.count("cfg-file") )
			std::cerr << "Cannot open config file " << configfile << ". Proceeding with defaults...\n";

		// set mode:
		if(sMode == "window")
		{
			std::cout << "config: Window mode..." << std::endl;
			m_ContextType = Window;
		}
		if(sMode == "hal")
			m_ContextType = HAL;
		if(sMode == "sentence")
		{
			std::cout << "config: Sentence mode..." << std::endl;
			m_ContextType = Sentence;
		}
	}

} //namespace hal_architect

