/*
    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 "matrices_all.h"
#include "config.h"

/**
  * Each type of storage type has own class for reading results.
  * Main function concentrates on preparing files and arguments.
  */

int partition(double* array,int*indexArray, int left, int right, int pivotIndex)
{
	double pivotValue = array[pivotIndex];
	std::swap(array[pivotIndex],array[right]);
	std::swap(indexArray[pivotIndex],indexArray[right]);
	int storeIndex=left;
	for(int i=left;i<right;i++)
	{
		if(array[i] >=pivotValue)
		{
			std::swap(array[i],array[storeIndex]);
			std::swap(indexArray[i],indexArray[storeIndex]);
			storeIndex++;
		}
	}
	std::swap(array[storeIndex],array[right]);
	std::swap(indexArray[storeIndex],indexArray[right]);
	return storeIndex;
}

void quicksort(double* array,int* indexArray, int left, int right)
{
	if(right>left)
	{
		int pivotIndex=(left+right)/2;
		pivotIndex=partition(array,indexArray,left,right,pivotIndex);
		quicksort(array,indexArray,left,pivotIndex-1);
		quicksort(array,indexArray,pivotIndex+1,right);
	}
}

void runForDenseMatrix(std::ifstream* inputFile,std::vector<std::string> matrixLabels,int self)
{
	std::cout<<"Started run for dense matrix\n";
	Config* cfg = Config::getInstance();
	std::string line;
	std::ofstream output(cfg->outputFile.c_str());
	std::vector<std::string> tokens;
	int counter=0;
	int indexTable[matrixLabels.size()];
	double valueTable[matrixLabels.size()];
	int lineNumber=0;

	while(std::getline((*inputFile),line))
	{

		boost::split(tokens, line, boost::is_any_of(" ") );
		//        std::cout<<"Tokens in read line: "<<tokens.size()<<std::endl;
		for(int i=0;i<(int)tokens.size();i++)
		{
			if(atof(tokens[i].c_str()) > cfg->minimalSimilarity && i != lineNumber)
			{
				indexTable[counter]=i;
				valueTable[counter]=atof(tokens[i].c_str());
				counter++;
			}

		}
		output<<matrixLabels[lineNumber]<<std::endl;
		if(counter>0)
		{
			quicksort(valueTable,indexTable,0,counter-1);
			if(counter>cfg->kBestResults)
				counter=cfg->kBestResults;
			for(int i=0;i<counter;i++)
			{
				if(matrixLabels[lineNumber] == matrixLabels[indexTable[i]]&&(!self))
					continue;
				output<<"\t"<<matrixLabels[indexTable[i]];
				output<<";"<<valueTable[i]<<std::endl;
			}
			counter=0;

		}
		lineNumber++;
	}
	output.close();

}

void runForSuperMatrix(std::ifstream* inputFile,std::vector<std::string> matrixLabels,int self)
{
	std::cout<<"Started run for super matrix\n";
	Config* cfg = Config::getInstance();
	std::string line;
	std::ofstream output(cfg->outputFile.c_str());
	std::vector<std::string> tokens;
	int counter=0;
	int indexTable[matrixLabels.size()];
	double valueTable[matrixLabels.size()];
	//Reading header line
	std::getline((*inputFile),line);
	int lineNumber=0;

	while(std::getline((*inputFile),line))
	{
		boost::split(tokens, line, boost::is_any_of(" ") );
		//        std::cout<<"Read "<<tokens.size()<<" tokens in line "<<lineNumber<<std::endl;
		//        std::cout<<"Line: "<<line<<std::endl;
		//        for(int x=0;x<tokens.size();x++)
		//            std::cout<<"token "<<x<<": "<<tokens[x]<<std::endl;

		for(int i=0;i<(int)tokens.size()-1;i+=2)
		{

			if(atof(tokens[i+1].c_str()) > cfg->minimalSimilarity && atoi(tokens[i].c_str()) != lineNumber)
			{
				indexTable[counter]=atoi(tokens[i].c_str());
				valueTable[counter]=atof(tokens[i+1].c_str());
				counter++;
			}
		}


		if(counter>0)
		{
			quicksort(valueTable,indexTable,0,counter-1);
			if(counter>cfg->kBestResults)
				counter=cfg->kBestResults;
			output<<matrixLabels[lineNumber]<<std::endl;
			for(int i=0;i<counter;i++)
			{
				if(matrixLabels[lineNumber] == matrixLabels[indexTable[i]]&&(!self))
					continue;
				output<<"\t"<<matrixLabels[indexTable[i]];
				output<<";"<<valueTable[i]<<std::endl;
			}
			counter=0;

		}
		lineNumber++;

	}
	output.close();

}

void runForTextFile(std::ifstream* inputFile,std::vector<std::string> matrixLabels, int self)
{
	std::cout<<"Started run for text file \n";
	Config* cfg =Config::getInstance();
	std::ofstream output(cfg->outputFile.c_str());
	int indexTable[matrixLabels.size()];
	double valueTable[matrixLabels.size()];

	std::string line;
	std::vector<std::string> tokens;



	bool exitFlag=false;
	if(!std::getline((*inputFile),line))
		exitFlag=true;
	int counter=0;

	while(!exitFlag)
	{
		//        std::cout<<"Putting line: "<<line<<std::endl;
		std::string curr_label = matrixLabels[atoi(line.c_str())];
		output<<matrixLabels[atoi(line.c_str())]<<std::endl;

		if(!std::getline((*inputFile),line))
		{

			exitFlag=true;
		}
		//        std::cout<<"Line read: "<<line<<"\n";
		while(!exitFlag && (line.find('\t',0)==0) )
		{
			//            std::cout<<"entered deep while loop, line read: "<<line<<"\n";
			boost::split(tokens, line, boost::is_any_of("\t") );
			//            std::cout<<tokens.size()<<"tokens created: "<<tokens[0]<<"\t"<<tokens[1]<<"\t"<<tokens[2]<<std::endl;
			//            sleep(1);
			//            if(atof(tokens[2].c_str())==0.143448)
			//                std::cout<<"Error line: "<<line<<std::endl;
			if(atof(tokens[2].c_str()) > cfg->minimalSimilarity)
			{
				//Somehow first token is "\t"
				indexTable[counter]=atoi(tokens[1].c_str());
				//                std::cout<<"Converted "<<tokens[1]<<" into "<<indexTable[counter]<<std::endl;
				valueTable[counter]=atof(tokens[2].c_str());
				counter++;
			}
			if(!std::getline((*inputFile),line))
				exitFlag=true;
		}
		if(counter>0)
		{
			quicksort(valueTable,indexTable,0,counter-1);
			if(counter>cfg->kBestResults)
				counter=cfg->kBestResults+1;
			for(int i=0;i<counter;i++)
			{
				if(curr_label == matrixLabels[indexTable[i]]&&(!self))
					continue;
				output<<"\t"<<matrixLabels[indexTable[i]];
				output<<";"<<valueTable[i]<<std::endl;
			}
			counter=0;

		}
	}
	output.close();

}

int main(int ac,char ** av)
{
	Matrices::init_logging();
	Config::initialize(ac,av);

	Config* cfg = Config::getInstance();

	std::vector<std::string> labels;
	std::cout << "Reading labels from file " << cfg->labelFile<< std::endl;
	std::ifstream file( cfg->labelFile.c_str() );
	if(!file)
		throw std::runtime_error("Cannot open file: "+ cfg->labelFile);
	std::string line;
	std::vector<std::string> 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 " << cfg->labelFile.c_str() << ". Line no = " << id << std::endl;
		matrixlabels.push_back(line);

	}//while file
	file.close();

	file.open(cfg->matrixName.c_str());
	if(!file)
		throw std::runtime_error("Cannot open file: "+ cfg->matrixName);



	std::getline(file,line);
	//    std::cout<<"Lenght of line: "<<line.length()<<std::endl;
	//    std::cout<<"Type of matrix: "<<cfg->typeOfMatrix<<std::endl;
	if(cfg->typeOfMatrix.compare("")==0)
	{

		std::vector<std::string> tokens;
		boost::split(tokens, line, boost::is_any_of(" ") );
		if(tokens.size()==1)
		{
			cfg->typeOfMatrix = "textfile";

		}
		else if (tokens.size()==3)
			cfg->typeOfMatrix = "supermatrix";
		else
		{
			cfg->typeOfMatrix = "densefile";
		}
	}
	file.seekg(0,std::ios::beg);

	std::cout<<"Running for "<<cfg->typeOfMatrix<<std::endl;

	if(cfg->typeOfMatrix.compare("textfile")==0)
		runForTextFile(&file,matrixlabels,cfg->selfSimilarity);
	else if(cfg->typeOfMatrix.compare("supermatrix")==0)
		runForSuperMatrix(&file,matrixlabels,cfg->selfSimilarity);
	else if(cfg->typeOfMatrix.compare("densefile")==0)
		runForDenseMatrix(&file,matrixlabels,cfg->selfSimilarity);
	else
		throw std::runtime_error("Invalid type of matrix\n");
	file.close();




	return 0;
}
