/*
    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 "wsdreader.h"
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
#include <stdio.h>


namespace Corpus2{

bool WSDReader::registered = TokenReader::register_path_reader<WSDReader>(
	"wsdreader","inner, config");

	WSDReader::WSDReader(const Tagset &tagset, const std::string &filename)
		: TokenReader(tagset), inner_filename_(filename)
	{


	}

	WSDReader::~WSDReader()
	{
		pclose(pipeout_fp);
	}

	Token* WSDReader::get_next_token()
	{
		if(currentSentence->empty())
			currentSentence=get_next_sentence();

		std::vector<Token*> tokens = currentSentence->tokens();
		if(token_index<tokens.size())
		{
			return tokens.at(token_index++);
		}
		else
		{
			currentSentence=get_next_sentence();

			if(currentSentence==NULL)
			{
				return NULL;
			}
			tokens = currentSentence->tokens();
			token_index=0;
			return tokens.at(token_index++);
		}
	}

	Sentence::Ptr WSDReader::get_next_sentence()
	{
		currentSentence = inner_reader_->get_next_sentence();
		if(currentSentence==0)
		{
			return currentSentence;
		}
		Wccl::SentenceContext sc(currentSentence);
		token_index=0;
		return process_sentence(sc);
	}

	Sentence::Ptr WSDReader::process_sentence(Wccl::SentenceContext & sc)
	{
		std::string quest = "PYTHONIOENCODING=utf_8 python -W ignore::DeprecationWarning -c \"from lexcsd.utils.config import load_config;from lexcsd.classification.classifiers.featuresclassifier import FeaturesClassifier;config = load_config('"+classifier_conf+"');fc = FeaturesClassifier(config); head = fc.read_classifiers_models('"+models_dir+"');";

		// map of tokens base and ids in sentence
		std::map<std::string, std::vector<int> > disamb_bases_id;
		size_t sssize = sc.size();
		std::string sent = "";
		// number of disambiguated bases
		int disamb_number = 0;
		// iterator po liście znanych słów niejednoznacznych
		std::set<std::string>::iterator it_set;

		//searching disambiguated words
		for(int i = 0; i < sc.size() ;++i){
			sc.set_position(i);
			Corpus2::Token *pToken = sc.at(i);
			sent+=pToken->orth_utf8()+ " ";
			std::vector<Lexeme>& lexemes = pToken->lexemes();

			if(!lexemes.size()){
				sssize = sc.size();
				continue;
			}
			int seg=0;
			BOOST_FOREACH (const Lexeme& lex, lexemes){

				if(lex.is_disamb()){
					std::string base = lex.lemma_utf8();
					if(base!="\"" && base!="\'" && base!=":" && base!="[" && base!="]" && base!=",")
					{
						base.erase(
						    remove( base.begin(), base.end(), '\'' ),
						    base.end()
						    );
						base.erase(
						    remove( base.begin(), base.end(), '`' ),
						    base.end()
						    );
						//sent += base + " ";
					}
					else
						continue;
					it_set = disamb_words.find(base);
					if(it_set!=disamb_words.end())
					{
						//std::cout << "Seg: " << base << std::endl;
						seg++;
						std::map<std::string, std::vector<int> >::iterator bases_it;
						bases_it = disamb_bases_id.find(base);

						//adding token's id to vector for found base
						if(bases_it!=disamb_bases_id.end())
						{
							std::vector<int> vec = bases_it->second;
							vec.push_back(i);
							disamb_bases_id[bases_it->first] = vec;

						}
						// creating vector with token id for found base
						else
						{
							std::vector<int> ids;
							ids.insert(ids.begin(), i);
							disamb_bases_id.insert(std::pair<std::string,std::vector<int> >(base, ids));
							disamb_number++;
						}
					}
				}
			}
			sssize = sc.size();
		}
		// processing disambiguated bases
		if(disamb_number>0)
		{	// base and vector meanings
			std::map<std::string, std::vector<std::string> > feature_result;
			quest+="fc.analyze_user_sentence('"+ sent + "', '"+work_dir+"', head, False, True);\"";
			std::cout << sent << std::endl;
			//connecting with featureclassifier

			FILE* pipe = popen(quest.c_str(), "r");
			char buffer[128];
			std::string result = "";
			while(!feof(pipe))
			{
				if(fgets(buffer, 128, pipe) != NULL)
					result += buffer;
			}

			pclose(pipe);
			// handling featureclassifier results
			if(result.find("Disambiguated words")==result.npos)
				return sc.get_sentence_ptr();
			result = result.substr(22 , result.length()-23);
			if(result.length()==0)
				return sc.get_sentence_ptr();
			std::cout << result << std::endl;
			for(int i=0; i<disamb_number; i++)
			{
				if(result.length()==0)
					break;
				std::string word;
				std::string sense;
				std::vector<std::string> senses;

				word = result.substr(result.find_first_of(":")+1, result.length()-1);
				sense = result.substr(result.find_first_of("["), result.find_first_of("]")-result.find_first_of("[")+1);
				word = word.substr(0, word.find_first_of("  "));
				sense = sense.substr(sense.find_first_of(":")+1, sense.length());
				result = result.substr(result.find_first_of("]")+1, result.length()-1);

				size_t pos=sense.find_first_of(", ");
				while(pos!=sense.npos)
				{
					senses.insert(senses.begin(), sense.substr(0, pos));
					//std::cout << "\tSens jed.: " << sense.substr(0, pos) <<std::endl;
					sense = sense.substr(sense.find_first_of(":")+1, sense.length());
					pos= sense.find_first_of(", ");
				}
				senses.insert(senses.begin(), sense.substr(0, sense.find_first_of("']")));
				//std::cout << "\tSens jedn.: " << sense.substr(0, sense.find_first_of("']")) << std::endl;


				feature_result.insert(std::pair<std::string, std::vector<std::string> >(word, senses));

			}

			//replace base
			std::string new_orth_utf8;
			Corpus2::Sentence::Ptr sent = sc.get_sentence_ptr();
			std::vector<Token*> &tokens = sent->tokens();
			std::map<std::string, std::vector<int> >::reverse_iterator iter;
			for(iter=disamb_bases_id.rbegin(); iter!=disamb_bases_id.rend(); iter++)
			{
				std::vector<std::string> new_bases;
				std::vector<int> ids;
				std::vector<int>::iterator it_int;
				std::map<std::string, std::vector<std::string> >::iterator it2;
				it2 = feature_result.find((*iter).first);
				if(it2!=feature_result.end())
					new_bases = it2 -> second;
				ids=(*iter).second;

				for(int i=ids.size()-1; i>=0; i--)
				{
					if(i+1>new_bases.size())
						continue;
					Token* tok = tokens [ids.at(i)];
					std::vector<Lexeme>& lexemes = tok->lexemes();

					BOOST_FOREACH (Corpus2::Lexeme& lex, lexemes)
					{
						if(lex.is_disamb())
						{
							std::string base = lex.lemma_utf8();
							lex.set_lemma_utf8(new_bases.at(i));
							std::string newbase = lex.lemma_utf8();
						//	std::cout << "Base change from: "<< base << " to " << newbase << std::endl;
						}
					}
				}
			}
		}
		return sc.get_sentence_ptr();
	}



	boost::shared_ptr<Chunk> WSDReader::get_next_chunk()
	{
		currentChunk=inner_reader_->get_next_chunk();
		if(currentChunk == NULL)
		{
			return currentChunk;
		}
		std::vector< boost::shared_ptr<Corpus2::Sentence> > s1 = currentChunk->sentences();
		std::vector< boost::shared_ptr<Corpus2::Sentence> >::iterator it;
		for(it=s1.begin(); it!=s1.end(); it++)
		{
			if(it==s1.begin())
				currentSentence=*it;
			Wccl::SentenceContext sc(*it);
			process_sentence(sc);
		}

		token_index=0;
		return currentChunk;
	}

	void WSDReader::set_option(const std::string& option)
	{

		if(boost::algorithm::starts_with(option, "inner:")) {

			std::string inner = option.substr(6);
			inner_reader_ = create_path_reader(inner, this->tagset(),
											inner_filename_);
			token_index=0;
			currentSentence= boost::make_shared<Sentence>();
		}

		if(boost::algorithm::starts_with(option, "config:")){

			std::string filename = option.substr(7);
			if(!boost::filesystem::exists(filename))
				std::cerr<< "Config file "<< filename << "doesn't exist";
			FILE * pFile;
			char mystring [100];

			pFile = fopen (filename.c_str() , "r");
			while(! feof (pFile)){
				if ( fgets (mystring , 100 , pFile) != NULL )
				{
					std::string line =std::string(mystring);
					std::vector<std::string> tokens;
					boost::algorithm::split(tokens, line, boost::is_any_of("="));
					if(tokens[0]=="work-dir")
					{
						work_dir=tokens[1].substr(0, tokens[1].length()-2);
						if(!boost::filesystem::is_directory(work_dir))
							std::cerr << "work-dir in config file isn't directory" << std::endl;
					}
					if(tokens[0]=="classifier-config")
					{
						classifier_conf=tokens[1].substr(0, tokens[1].length()-1);
						if(!boost::filesystem::exists(classifier_conf))
							std::cerr << "Classifier config file doesn't exist" << std::endl;

					}
					if(tokens[0]=="models-dir")
					{
						models_dir=tokens[1].substr(0, tokens[1].length()-1);
						if(!boost::filesystem::is_directory(models_dir))
							std::cerr << "models-dir in config file isn't directory" << std::endl;
					}
					if(tokens[0]=="weka-server")
					{

						weka_server=tokens[1].substr(0, tokens[1].length()-1);
						if(!boost::filesystem::exists(weka_server))
							std::cerr << "wekaServer jar doesn't exist" << std::endl;
						std::string command = "java -jar "+weka_server;
						pipeout_fp = popen(command.c_str(), "w");
					}
				}
			}
			fclose (pFile);

			std::string quest = "PYTHONIOENCODING=utf_8 python -c \"from lexcsd.utils.config import load_config;from lexcsd.classification.classifiers.featuresclassifier import FeaturesClassifier;config = load_config('"+classifier_conf+"');fc = FeaturesClassifier(config); head = fc.read_classifiers_models('"+models_dir+"');fc.print_head_words(head) \"";
			std::string result = exec(quest.c_str());
			disamb_words = split_list(result);

		}
	}

	std::set<std::string> WSDReader::split_list(std::string list)
	{
		std::string tail;
		std::set<std::string> elements, results;
		size_t pos;

		tail=list;
		tail = tail.substr(0, tail.length()-1);
		while( (pos = tail.find_first_of(",")) != tail.npos )
		{
			if(pos>0)
			{
				elements.insert(tail.substr(0, pos));
			}
			tail = tail.substr(pos+1);
		}

		if(tail.length() > 0)
		{
			elements.insert(tail);
		}

		std::set<std::string>::iterator it;
		for(it=elements.begin(); it!=elements.end(); it++)
		{
			std::string el = *it;
			el = el.substr(el.find_first_of(":")+1, el.length()-1);
			results.insert(el);
		}

		return results;

	}

	void WSDReader::validate()
	{
		
		if(inner_reader_ == NULL)
			throw Corpus2Error("Inner reader not initialised.");
	}

	std::string WSDReader::get_option(const std::string& option) const
	{
		if(boost::algorithm::starts_with(option, "inner:")
			&& inner_reader_ != NULL)
			return option;
		return inner_reader_->get_option(option);
	}


	std::string WSDReader::exec(const char* cmd) {
		FILE* pipe = popen(cmd, "r");

		if (!pipe) return "ERROR";
		char buffer[128];
		std::string result = "";
		while(!feof(pipe)) {
			if(fgets(buffer, 128, pipe) != NULL)
				result += buffer;
		}
		//std::cout << result << std::endl;
		pclose(pipe);
		return result;
	}


}// ns Corpus2
