/*
    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 <boost/date_time.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include <iostream>
#include <fstream>
void ConvertToCLUTO(const std::string &filename, const std::string &out_filename);

int main(int ac, char **av)
{
	try
	{
		if( ac != 3)
			std::cerr << "ClutoConv, Bartosz Broda\nUsage: ClutoConv input_matrix_file output_matrix_file\n";
		else
		{
			std::string infile = av[1];
			std::string outfile = av[2];
			ConvertToCLUTO(infile, outfile);
		}
	}
	catch(const std::exception &e)
	{
		std::cerr << "Cought exception: " << e.what() << std::endl;
		return 1;
	}

	return 0;
}

size_t GetNewNNZ(const std::string &filename)
{
	std::cout << "Geting new nnz..." << std::endl;
	std::ifstream file( filename.c_str() );
	std::string line;
	std::getline( file, line ) ;//skip header line
	size_t new_nnz = 0, row_no = 0;
	while( std::getline( file, line ) )
	{
		row_no ++;
		boost::trim(line);
		if( line.size() == 0 )
			continue;
		std::vector<std::string> tokens;
		boost::split(tokens, line, boost::is_any_of( " \t" ) );
		if(tokens.size() % 2 != 0)
		{
			std::stringstream err;
			err << "Wrong line " << row_no + 1 << " in file " << filename << ". Number of items is not even.";
			throw std::runtime_error( err.str() );
		}
		for(size_t i = 0; i < tokens.size() - 1; i+=2)
			new_nnz++;
	}

	return new_nnz;
}

void ConvertToCLUTO(const std::string &filename, const std::string &out_filename)
{
	using namespace boost::posix_time;

	size_t new_nnz = GetNewNNZ(filename);

	std::ifstream file( filename.c_str() );
	std::ofstream out_file( out_filename.c_str() );
	if(!file)
		throw std::runtime_error( std::string("Cannot open file: ") + filename);
	size_t rows, cols, nz;
	file >> rows >> cols >> nz;

	if(rows <= 0 || cols <= 0 )
		throw std::runtime_error( "Wrong dimensions of a matrix in file: " + filename);
	if( nz <= 0 )
		std::cerr << "WARNING: nz <= 0... Was it intentional?\n";


	if(new_nnz != nz)
		std::cerr << "NNZ in file is different then the actual value - corrcting." << std::endl;

	boost::regex ccs_pattern(".*matrix_ccs.mat");

	boost::smatch match;

	if (boost::regex_match(filename, match, ccs_pattern) )
	{
		out_file<< cols << " " << rows << " " << new_nnz << std::endl;
	}
	else
	{
		out_file<< rows << " " << cols << " " << new_nnz << std::endl;
	}

	std::string line;
	size_t row_no = -1;
	std::getline( file, line ) ;//skip header line
	ptime start = microsec_clock::local_time();
	time_duration total_time;

	while( std::getline( file, line ) )
	{
		row_no ++;
		boost::trim(line);
		if( line.size() == 0 )
		{
			out_file << std::endl;
			continue;
		}
		std::vector<std::string> tokens;
		boost::split(tokens, line, boost::is_any_of( " \t" ) );
		if(tokens.size() % 2 != 0)
		{
			std::stringstream err;
			err << "Wrong line " << row_no + 1 << " in file " << filename << ". Number of items is not even.";
			throw std::runtime_error( err.str() );
		}
		std::stringstream ooo;
		for(size_t i = 0; i < tokens.size() - 1; i+=2)
		{
			size_t colNo = boost::lexical_cast<size_t> ( tokens[i] );
			double value = boost::lexical_cast<double> ( tokens[i+1] );
			if(colNo > cols)
			{
				std::stringstream err;
				err << "Wrong entry in "<< filename << ", line " << row_no+1 << ". Column number "
					<< colNo << " is grater then number of columns: " << cols ;
				throw std::runtime_error( err.str() );
			}

			ooo << colNo+1 << " " << value << " ";
		}
		std::string s = ooo.str();
		boost::trim(s);
		out_file << s << std::endl;
		ptime end = microsec_clock::local_time();
		time_duration len = end-start;
		if(len.total_milliseconds() > 5000)
		{
			total_time += len;
			long total_ms = total_time.total_milliseconds();
			long eta_ms =  (total_ms*(rows-row_no-1)) / (row_no+1);
			std::cout << "Converting matrix, row " << row_no << " / " << rows << " ETA: " << to_simple_string( milliseconds(eta_ms) )  << std::endl;
			start = microsec_clock::local_time();
		}

	}

	std::cout << "Convering done..." << std::endl;

}
