/*
    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 "windowbuilder.h"

#include <set>
#include <vector>
#include <fstream>

#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>


#include <boost/foreach.hpp>
#include <libcorpus2/tagsetmanager.h>
#include <libcorpus2/io/xcesreader.h>

//#include <libcorpus2/io/orthwriter.h>

namespace windowbuilder{

	WindowMatrixBuilder::WindowMatrixBuilder(const config &cfg)
		: config_(cfg)
	{
		// no op?
	}

	void WindowMatrixBuilder::run()
	{
		using namespace std;
		using namespace boost;
		typedef pair<string,string> strpair;
		typedef map<string,size_t> str2st;
		typedef map<strpair,size_t> index_type;

		ofstream out(config_.outfile.c_str());
		if(!out)
			throw runtime_error("Cannot open file for writing: " + config_.outfile);


		const Corpus2::Tagset& tagset = Corpus2::get_named_tagset(config_.tagset);
		//Corpus2::TokenWriter::TokenWriterPtr writer = Corpus2::TokenWriter::create_stream_writer("orth", std::cout, tagset);

		BOOST_FOREACH (std::string corpus, config_.corpora)
		{
			Corpus2::TokenReader::TokenReaderPtr pReader =
					Corpus2::TokenReader::create_path_reader(config_.reader_class,
															 tagset,
															 corpus);
			std::cout << "Processing " << corpus << std::endl;

			Corpus2::Sentence::Ptr pSentence;
			std::cout << "Starting..." << std::endl;
			size_t sent_no = 0;


			while( (pSentence=pReader->get_next_sentence()))
			{
				index_type cols;
				index_type rows;

				// index sentence
				for(size_t i = 0; i < pSentence->size(); ++i)
				{
					Corpus2::Token* pToken = pSentence->at(i);
					// TODO: all lexemes?
					Corpus2::Lexeme lex = pToken->get_preferred_lexeme(tagset);

					std::string pos_str = tagset.get_pos_name(lex.tag().get_pos());
					std::string lemma = lex.lemma_utf8();

					std::string ls = pos_str + ":" + lemma;

					if(config_.row_wordpos.size() == 0
					   || config_.row_wordpos.find(ls) != config_.row_wordpos.end())
					{
						if(config_.row_poses.size() == 0 ||
						   config_.row_poses.find(pos_str) != config_.row_poses.end())
							rows.insert(make_pair(make_pair(pos_str, lemma), i));
					}
					if(config_.column_wordpos.size() == 0
					   || config_.column_wordpos.find(ls) != config_.column_wordpos.end())
					{
						if(config_.column_poses.size() == 0 ||
						   config_.column_poses.find(pos_str) != config_.column_poses.end())
							cols.insert(make_pair(make_pair(pos_str, lemma), i));
					}
				} // index sentence

				BOOST_FOREACH (index_type::value_type &r2i, rows)
				{
					//writer->write_sentence(*pSentence);
					BOOST_FOREACH (index_type::value_type & c2i, cols)
					{
						if(r2i.second == c2i.second)
							continue;

						if( abs(long(r2i.second - c2i.second)) > config_.max_window)
							continue;

						std::string entry =  r2i.first.first + ":" + r2i.first.second + ";";

						if(config_.simple)
							entry+= "SNTWND";
						else
							entry += "SNTWND:" + boost::lexical_cast<std::string>(-long(r2i.second - c2i.second));

						entry += ";" + c2i.first.first + ":" + c2i.first.second;
						str2st::iterator find = data.find(entry);
						if(find == data.end())
							data.insert( make_pair(entry, 1) );
						else
							find->second  += 1;


						//std::cout << entry << std::endl;

					}
				}

				if(++sent_no % config_.dump_interval == 0)
				{
					cerr << "Sentence no: " << human_readable(sent_no) << ". Dumping data... " ;
					BOOST_FOREACH (str2st::value_type &entry2count, data)
						out << entry2count.first << ";" << entry2count.second
							<< std::endl;
					cerr << " Done." << std::endl;
					data.clear();
				}
			}// while sentence

			cerr << "Sentence no: " << human_readable(sent_no) << " (last sentence of corpus). Dumping data... " ;
			BOOST_FOREACH (str2st::value_type &entry2count, data)
				out << entry2count.first << ";" << entry2count.second
					<< std::endl;
			cerr << " Done." << std::endl;
			data.clear();

		}


	} // run

	std::string WindowMatrixBuilder::human_readable(double input, size_t level)
	{
		static const char* suffixes = " KMGT";
		if(input < 1000 || level >= 4 )
		{
			std::stringstream ss;
			ss.setf(std::ios::fixed,std::ios::floatfield);
			ss.precision(2);
			ss << input << suffixes[level];
			return ss.str();
		}
		return human_readable(input/1000, level+1);
	}

} // namespace windowbuilder
