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

#include "boost/date_time/posix_time/posix_time.hpp"
#include <boost/regex.hpp>

#include <fstream>
#include <iostream>

namespace clustering
{
	namespace cbc
	{
		KBest::KBest()
		{
		}
		KBest::~KBest()
		{
		}
		void KBest::ReadFromXmlFile(const std::string &filename)
		{
			std::cout << "Reading k-best from " << filename << std::endl;
			boost::regex synset_regex(".*<synset lexicalunit=\"([^\"]*)\".*>.*", boost::regex::icase);
			boost::regex lu_regex(".*<lu\\s*[^>]*>(.*)</lu>.*", boost::regex::icase);

			std::ifstream file(filename.c_str() );
			if(!file)
				throw std::runtime_error("Cannot open file: " + filename);
			std::string line, curr_unit;
			std::vector<std::string> all;
			while( std::getline(file,line) )
			{
				boost::smatch m;
				if(boost::regex_match(line, m, synset_regex))
				{
					if(all.size() > 0 && curr_unit.length() > 0)
					{
						m_mKBest[curr_unit] = all;
						all.clear();
					}
					curr_unit = m[1];
					//std::cout << curr_unit << std::endl;
					boost::trim(curr_unit);
					continue;
				}

				if(boost::regex_match(line,m,lu_regex) )
				{
					std::string w = m[1];
					boost::trim(w);
					if(w.length())
						all.push_back(w);
					//std::cout << "\t"<< w << std::endl;
				}
			}
		}

		void KBest::GenerateFromMatrix(const std::string &filename_out, size_t k, double sim_min,
				Matrices::SuperMatrixPtr pMatrix, smartcomparator::compare::CompareFunctionPtr pCmp)
		{
			size_t size_rows = pMatrix->NumberOfRows();
			size_t nz = 0;
			std::vector< std::vector< std::pair< size_t, double> > > simmatrix;
			std::cout << "Generating similarity matrix..." << std::endl;
			for(size_t row1 = 0; row1 < size_rows; row1++)
			{
				size_t added = 0;
				simmatrix.push_back(std::vector< std::pair<size_t, double> >());
				std::vector< std::pair<size_t, double> > &row = simmatrix[row1];
				for(size_t row2 = 0; row2 < size_rows && added < k; row2++)
				{
					if(row1 == row2)
						continue;

					double sim = pCmp->Compare(row1,row2);
					if(sim >= sim_min)
					{
						row.push_back( std::pair<size_t,double>(row2, sim) );
						added++;
						nz++;
					}
				}//for 2nd row
				if( row1 % 10 == 0 )
					std::cout << "Generating similarity matrix... Row "<< row1 << " / " << size_rows << std::endl;
			}//for each row

			std::cout << "Saving similarity matrix to file: "<< filename_out << std::endl;
			std::ofstream file(filename_out.c_str() );
			if(!file)
				throw std::runtime_error("Cannot open file: " + filename_out);

			file << size_rows << " " << size_rows <<" " <<nz << std::endl;
			for(size_t row1 = 0; row1 < size_rows; row1++)
			{
				std::vector< std::pair<size_t, double> > &row = simmatrix[row1];
				for(std::vector<std::pair<size_t,double> >::iterator elem = row.begin(); elem != row.end(); elem++)
					file << elem->first << " " << elem->second << " ";

				file << std::endl;
			}//for each row
			file.close();

			std::string labelfile = filename_out + ".rlabel";
			std::cout << "Saving labels to file: "<< labelfile << std::endl;
			file.open( labelfile.c_str() );
			if(!file)
				throw std::runtime_error("Cannot open file: " + labelfile);
			for(size_t row1 = 0; row1 < size_rows; row1++)
				file << pMatrix->GetRowFeatures()->operator[](row1).GetLabel() << std::endl;
			file.close();
		}

		Matrices::SymmetricSimMatrixPtr KBest::ReadFromGeneratedFiles(const std::string &filename, const std::string &labelfile,
				size_t k, double min_sim)
		{
			using namespace boost::posix_time;
			std::string lfilename = labelfile;
			if(lfilename.length() == 0)
				lfilename = filename + ".rlabel";
			std::vector<std::string> labels;
			std::cout << "Reading labels from file " << lfilename << std::endl;
			std::ifstream file( lfilename.c_str() );
			if(!file)
				throw std::runtime_error("Cannot open file: "+ lfilename);
			std::string line;
			Matrices::LabelIndex matrixlabels;
			size_t id = 0;
			while( std::getline(file,line) )
			{
				boost::trim(line);
				if(line.length())
					labels.push_back(line);
				else
					std::cerr << "Warning - empty line in file " << lfilename.c_str() << ". Line no = " << id << std::endl;
				matrixlabels[line] = id++;
			}//while file
			file.close();

			std::cout << "Reading similarity db from file " << filename << std::endl;
			//size_t rows, nz;
			file.open( filename.c_str() );
			if(!file)
				throw std::runtime_error("Cannot open file: "+ filename);
			std::getline(file,line) ;
			std::vector<std::string> first_line_tokens;
			boost::split(first_line_tokens, line, boost::is_any_of(" ") );
			size_t items = boost::lexical_cast<size_t>(first_line_tokens[0]);
			Matrices::SymmetricSimMatrixPtr pMatrix( new Matrices::SymmetricSimMatrix(filename, items) );
			size_t row_no = 0;
			ptime start = microsec_clock::local_time();
			time_duration total_time;
			while( std::getline(file,line) )
			{
				if(line.length() == 0)
				{
					row_no++;
					continue;
				}
				std::string &row_label = labels[row_no];
				std::vector<std::string> &sim_list = m_mKBest[row_label];
				std::multimap<double,size_t> sims;
				std::vector<std::string> tokens;
				boost::split(tokens, line, boost::is_any_of(" \t") );
				for(size_t i = 0; i < tokens.size()-1; i+=2)
				{
					size_t index = boost::lexical_cast<size_t>(tokens[i] );
					double sim = boost::lexical_cast<double>(tokens[i+1] );
					if(sim < min_sim)
						continue;
					sims.insert( std::pair<double, size_t>(sim,index) );
				}
				size_t added = 0;
				for(std::multimap<double,size_t>::reverse_iterator iter = sims.rbegin(); iter != sims.rend() && added < k; iter ++ )
				{
					sim_list.push_back( labels[ iter->second ] );
					pMatrix->SetCell(iter->first, row_no, iter->second);
					added++;
				}
				row_no++;

				ptime end = microsec_clock::local_time();
				time_duration len = end-start;
				if( len.total_milliseconds() > 10000)
				{
					total_time += len;
					long total_time_ms = total_time.total_milliseconds();
					double item_per_sec = double( (row_no+1)*1000)  / total_time_ms;
					long eta_ms = (total_time_ms*(items - row_no - 1)) / (row_no+1);

					std::cout << "Reading similarity db, " << row_no << " / " << items  << " | row/sec = "
						<< item_per_sec << " ETA: " << to_simple_string(milliseconds(eta_ms) )
						<< std::endl;
					start = microsec_clock::local_time();
				}
			}
			file.close();
			if( matrixlabels.size() != pMatrix->NumberOfRows() )
				std::cerr << "Warning: number of labels != number of rows!";

			pMatrix->SetLabelIndex(matrixlabels);

			return pMatrix;
		}

		std::vector<std::string>& KBest::GetKBest(const std::string &w)
		{
			return m_mKBest[w];
		}
	}//cbc
}//clustering

