/*
    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.
*/

#ifndef SUM_FLEX_CPP
#define SUM_FLEX_CPP
#include "matrices_all.h"
#include <cmath>
#include <boost/regex.hpp>
#include "sum_flex.h"

namespace sum_flex
{
	typedef std::vector<size_t> size_t_vector;
	typedef std::map<std::string, size_t_vector> str2vecmap;
	void helpMsg(char** av)
	{
		std::cout<<"\nProgram summing matrix rows by flex \n"
				<<"Runtime format: "<< av[0] <<" in_matrix_dir in_matrix_name out_matrix_dir out_matrix_name\n\n";
	}

	void setMatrixRowsAndRowFeatures(std::map<std::string,std::vector<size_t> > word_rows, Matrices::SuperMatrixPtr outMatrix,  Matrices::SuperMatrixPtr inMatrix)
	{
		Matrices::FeatureVectorPtr rowF = outMatrix->GetRowFeatures();
		size_t currentOutRow = 0;
		Matrices::FeatureVector inRowF = (*inMatrix->GetRowFeatures());
		for (str2vecmap::iterator rowIter = word_rows.begin(); rowIter != word_rows.end(); ++rowIter)
		{
			std::string word = (*rowIter).first;
			size_t TF = 0;

			Matrices::CompressedVectorPtr rowContents = outMatrix->GetRow(currentOutRow);
			for (size_t i = 0; i<(*rowIter).second.size(); i++)
			{
				(*rowContents) += (*inMatrix->GetRow((*rowIter).second[i]));
				TF += inRowF[(*rowIter).second[i]].GetGlobalTermFrequency();
			}
			rowF->push_back(Matrices::Feature(word,rowContents->GetValues()->size(),Measures::CountVectorSum(rowContents),TF,Measures::CountVectorEntropy(rowContents)));
			currentOutRow ++;
		}
	}
	void setColumnFeatures(Matrices::SuperMatrixPtr outMatrix,  Matrices::SuperMatrixPtr inMatrix)
	{
		Matrices::FeatureVectorPtr columnF = outMatrix->GetColumnFeatures();
		Matrices::FeatureVector inColf = (*inMatrix->GetColumnFeatures());
		Matrices::FeatureVector inRowF = (*inMatrix->GetRowFeatures());
		for (size_t i = 0; i<outMatrix->NumberOfColumns(); i++)
		{
			Matrices::CompressedVectorPtr columnContents = outMatrix->GetColumn(i);
			std::string featurename = inColf[i].GetLabel();
			size_t TF = inColf[i].GetGlobalTermFrequency();
			size_t tf = inColf[i].GetTermFrequency();
			columnF->push_back(Matrices::Feature(featurename,columnContents->GetValues()->size(),tf,TF,Measures::CountVectorEntropy(columnContents)));
		}
	}

	Matrices::SuperMatrixPtr getOutputMatrix(int ac, char** av)
	{
		Matrices::SuperMatrixPtr inMatrix = Matrices::IO::LoadSuperMatrix(av[1],av[2],false,true);
		std::string matrixName(av[4]);
		return getOutputMatrix(inMatrix,matrixName);
	}

	Matrices::SuperMatrixPtr getOutputMatrix(Matrices::SuperMatrixPtr inMatrix, std::string matrixName)
	{
		Matrices::FeatureVectorPtr features = inMatrix->GetRowFeatures();
		std::map<std::string,std::vector<size_t> > word_rows;
		for (size_t i = 0; i<inMatrix->NumberOfRows(); i++)
		{
			std::string label = (*features)[i].GetLabel();
			boost::regex re("([\\S]+?):([\\S\\s]+?)");
			boost::smatch match;
			if(boost::regex_match(label,match,re))
			{
				if (word_rows.find(match[2]) == word_rows.end())
				{
					word_rows[match[2]] == std::vector<size_t>();
				}
				word_rows[match[2]].push_back(i);
			}
			else
			{
				std::cout<<label<<" appears to be invalid. Does it have a specified flex class?\n";
			}
		}
		Matrices::CRSMatrixPtr outCRS(new Matrices::CRSMatrix(matrixName, word_rows.size(), inMatrix->NumberOfColumns()));
		Matrices::CCSMatrixPtr outCCS(new Matrices::CCSMatrix(matrixName, word_rows.size(), inMatrix->NumberOfColumns()));
		Matrices::FeatureVectorPtr columnF(new Matrices::FeatureVector());
		Matrices::FeatureVectorPtr rowF(new Matrices::FeatureVector());
		size_t tokens = inMatrix->GetTokenCount();
		std::string corpus_info = inMatrix->GetCorpusInfo();
		std::map<size_t, std::string> boundaries = inMatrix->Boundaries();
		Matrices::SuperMatrixPtr outMatrix(new Matrices::SuperMatrix(matrixName,outCCS,outCRS,rowF,columnF,tokens,corpus_info,boundaries));
		setMatrixRowsAndRowFeatures(word_rows, outMatrix, inMatrix);
		outMatrix->SyncCRSToCCS();
		setColumnFeatures(outMatrix, inMatrix);
		return outMatrix;
	}



	int tool_main(int ac, char**av)
	{
		if(ac<5)
		{
			helpMsg(av);
			throw std::runtime_error("Invalid number of parameters!\n");
			return -1;
		}
		Matrices::SuperMatrixPtr outMatrix = getOutputMatrix(ac,av);
		Matrices::IO::WriteSuperMatrix(outMatrix,av[3]);

		return 0;
	}
}
#endif // MAIN_CPP
