/*
    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 <iostream>
#include <fstream>
#include <stdexcept>

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

#include "matrices_all.h"

size_t get_dim(const std::string &filename)
{
	std::ifstream file(filename.c_str());
	if(!file)
		throw std::runtime_error( std::string("Cannot open file: ") + filename);
	std::string line;
	size_t words = 0;
	while( std::getline(file, line) )
	{

		if( line.size() ==0 )
			continue;

		if(isdigit(line[0]))
			words++;
	}
	return words;
}

Matrices::CRSMatrixPtr  load_to_crs(const std::string &filename, size_t k, double t)
{
	std::cout << "Reading no. of dimensions..." << std::endl;
	size_t dim = get_dim(filename);
	std::cout << "Dimensions: " << dim << std::endl;

	std::ifstream file(filename.c_str());
	if(!file)
		throw std::runtime_error( std::string("Cannot open file: ") + filename);

	std::string line;
	std::multimap<double, size_t> row;
	size_t line_no = 0;

	Matrices::CRSMatrixPtr pMatrix(new Matrices::CRSMatrix("simmatrix", dim, dim));

	size_t row_index = 0;

	int rows_processed = -1; // skipping first entrance to first row
	int values_skipped=0;

	while( std::getline(file, line) )
	{
		boost::trim_right(line);
		if( line.size() ==0 )
			continue;

		if(isdigit(line[0]))
		{// new row
			rows_processed++;
			if(row.size())
			{
				size_t inserted = 0;
				for(std::multimap<double,size_t>::reverse_iterator riter = row.rbegin();
					riter != row.rend() && inserted < k; ++riter, ++inserted)
					pMatrix->SetCell(riter->first, row_index, riter->second);
				row.clear();
			}
			row_index = boost::lexical_cast<size_t>(line);
		}
		else
		{// in the middle of a row
			std::vector<std::string> tokens;
			boost::split(tokens, line, boost::is_any_of(" \t"));
			if(tokens.size() != 3)
			{
				std::cerr << "Error in line " << line_no << std::endl;
				continue;
			}
			size_t col = boost::lexical_cast<size_t>(tokens[1]);


			if(col == row_index)
				continue; // skipping sim(x,x)

			double val = boost::lexical_cast<double>(tokens[2]);
			if(val < t)
			{
				values_skipped++;
				continue;
			}
			row.insert( std::make_pair(val, col));
		}
		++line_no;

		if(line_no % 1000000 == 0)
			std::cout << "Line " << line_no / 1000000 << "M Rows processed: " << rows_processed << "/" << dim <<std::endl;
	}
	std::cout<<"Skipped "<<values_skipped<<" too low values during reading matrix\n";
	return pMatrix;
}

int main(int ac, char**av)
{
	Matrices::init_logging();
	namespace po = boost::program_options;
	po::options_description basic("Basic options", 100);

	size_t k;
	double t;
	std::string filename;
	std::string outpath;

	basic.add_options()
			("help", "Prints this message")
			("kbest", po::value<size_t>(&k)->default_value(100000), "Number of most similar elements to retain" )
			("threshold", po::value<double>(&t)->default_value(0.01), "Similarity threshold")
			("input", po::value<std::string>(&filename), "Input path")
			("output", po::value<std::string>(&outpath), "Output path")
			;
	boost::program_options::variables_map varmap;
	po::store(po::parse_command_line(ac, av, basic), varmap);
	po::notify(varmap);
	if( varmap.count("help") )
	{
		std::cout << basic << std::endl;
		exit(1);
	}

	std::cout << "k = " << k << "\nt = " << t << "\nin = " << filename << "\nout = " << outpath << std::endl;


	Matrices::CRSMatrixPtr  pMatrix = load_to_crs(filename, k, t);
	std::cout << "Writing results to: " << outpath << std::endl;
	Matrices::IO::WriteToSingleFile(pMatrix, outpath);
	std::cout << "Done." << std::endl;
	return 0;
}
