/*
    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 "nerreader.h"
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <libcorpus2/tagsetmanager.h>
#include <boost/lexical_cast.hpp>
#include "read_utils.h"

namespace Corpus2{

bool NERReader::registered = TokenReader::register_path_reader<NERReader>(
	"nerreader","nerinner,nerd,model"); // TODO more help?

	NERReader::NERReader(const Tagset &tagset, const std::string &filename)
		: TokenReader(tagset), inner_filename_(filename)
	{
		myTagset=tagset;
	}

	NERReader::~NERReader()
	{
		// TODO implementataion
	}

	void NERReader::change_input_file(const std::string &filename)
	{
		inner_reader_ = create_path_reader(inner_name, this->tagset(),filename);
	}

	Token* NERReader::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 NERReader::get_next_sentence()
	{
		currentSentence = inner_reader_->get_next_sentence();
		if(currentSentence==0)
			return currentSentence;
		return process_sentence(currentSentence);
	}

	Sentence::Ptr NERReader::process_sentence(Corpus2::Sentence::Ptr sentence)
	{
		std::vector<Liner::linerTuple> liner_out;
		std::string sentence_str = orthwriter(sentence,myTagset);
		try
		{
			liner_out = liner->process_liner_output(liner->do_liner(sentence_str));
		}
		catch(boost::bad_lexical_cast)
		{
			liner->reset_liner();
			std::cerr<<"Liner nie przetworzy? zdania:\n"<<sentence_str;
			return sentence;
		}
		return proc_sentence(sentence,liner_out,0);
	}

	Sentence::Ptr NERReader::proc_sentence(Corpus2::Sentence::Ptr sentence,std::vector<Liner::linerTuple> liner_out,int offset)
	{

		if(liner_out.begin()!=liner_out.end())
		{
			std::vector<int> positions;
			int first=(*(liner_out.begin())).first.first;
			int last=(*(liner_out.begin())).first.second;
			std::string name=(*(liner_out.begin())).second;
			for (int i=first-offset;i<=last-offset;i++)
				positions.push_back(i-1);
			int head=get_head(sentence,positions,myTagset);
			std::string new_orth = get_new_orth(sentence,name,positions);
			std::string new_base = get_new_base(sentence,name,positions);
			liner_out.erase(liner_out.begin());
			return proc_sentence(clone_sentence(sentence,head,positions,new_orth,new_base),liner_out,offset+last-first);
		}
		return sentence;
	}

	std::string NERReader::get_new_orth(Corpus2::Sentence::Ptr sentence,std::string name,const std::vector<int>& all)
	{
		std::string new_orth = name + ":" ;
		std::vector<Token*> &tokens = sentence->tokens();
		BOOST_FOREACH (const int &pos, all)
		{
			Token* tok = tokens [pos];
			new_orth += tok->orth_utf8() + " ";
		}
		new_orth.erase(new_orth.size()-1, 1);

		return new_orth;
	}
	std::string NERReader::get_new_base(Corpus2::Sentence::Ptr sentence,std::string name,const std::vector<int>& all)
	{
		std::vector<Token*> &tokens = sentence->tokens();
		std::string new_base = name + ":" ;
		BOOST_FOREACH (const int &pos, all)
		{
			Token* tok = tokens [pos];
			Lexeme lex=tok->get_preferred_lexeme(myTagset);
			new_base += lex.lemma_utf8() + " ";
		}
		new_base.erase(new_base.size()-1, 1);

		return new_base;
	}


	boost::shared_ptr<Chunk> NERReader::get_next_chunk()
	{
		currentChunk=inner_reader_->get_next_chunk();
		if(currentChunk == NULL)
			return currentChunk;

		 boost::shared_ptr<Chunk> new_chunk = boost::make_shared<Chunk>();

		 BOOST_FOREACH (Corpus2::Sentence::Ptr sentence, currentChunk->sentences())
			 new_chunk->append( process_sentence(sentence) );

		return new_chunk;
	}

	void NERReader::set_option(const std::string& option)
	{
		if(boost::algorithm::starts_with(option, "nerinner:"))
		{
			inner_name = option.substr(9);
			inner_reader_ = create_path_reader(inner_name, this->tagset(),
											inner_filename_);
			token_index=0;
			currentSentence= boost::make_shared<Sentence>();
		}
		if(boost::algorithm::starts_with(option, "nerd:"))
		{
			nerd_path = option.substr(5);
			boost::algorithm::trim(nerd_path);
			if(!boost::filesystem::exists(nerd_path+"/nerd.py"))
				throw std::runtime_error("File "+ nerd_path + "/nerd.py does not exists");
		}
		if(boost::algorithm::starts_with(option, "model:"))
		{
			model_file = option.substr(6);
			boost::algorithm::trim(model_file);
			if(!boost::filesystem::exists(model_file))
				throw std::runtime_error("File "+ model_file + " does not exists");
		}
		if(boost::algorithm::starts_with(option, "linpath:"))
		{
			liner_path = option.substr(8);
			boost::algorithm::trim(liner_path);
		}
		if(boost::algorithm::starts_with(option, "linfile:"))
		{
			liner_file = option.substr(8);
			boost::algorithm::trim(liner_file);
		}
	}

	void NERReader::validate()
	{
		if(inner_reader_ == NULL)
			throw Corpus2Error("Nerinner reader not initialised.");
		if(liner_path == "")
			liner_path="/usr/local/bin";
		if(liner_file == "")
			liner_file="liner2";
		if(!boost::filesystem::exists(liner_path+"/"+liner_file))
			throw std::runtime_error("File "+ liner_path+"/"+liner_file + " does not exists");
		liner = boost::make_shared<Liner>(model_file,nerd_path,liner_path,liner_file);
		liner->run_liner();
	}

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

}// ns Corpus2
