/*
    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_headers.h"
#include "CRSMatrixReader.h"
#include <boost/algorithm/string.hpp>

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.matrices.io.CRSMatrixReader"));

namespace Matrices
{
	namespace IO
	{
		CRSMatrixReader::CRSMatrixReader(const std::string &dir, const std::string &name,
			std::string s , bool showMsg, std::ostream &log)
			: CCSMultiFileReader(dir, name, s , showMsg,log, "crs")
		{
			pointers_file_suffix = "_row_";
			indices_file_suffix = "_col_";
		}

		void CRSMatrixReader::ReadMatrix(CRSMatrix &matrix)
		{
			boost::filesystem::path dir = boost::filesystem::path(dirpath);
				dir.normalize();
			if( ! boost::algorithm::ends_with(dir.string(), name))
			{
				dir /= name;
				if(! boost::filesystem::exists(dir) )
					throw filesystem_error("CCSMultiFileReader::Read", std::string("Directory with matrix dosn't exist: ")
					+ dir.string());
				dirpath = dir.string();
			}

			LoadToMemory();

			matrix.SetDescriptor( matrix.GetName(),  rows, cols);

			for(size_t row = 0; row < rows; row++)
			{
				CompressedVector* v = GetRow(row);
				matrix.SetCompressedRow(row, *v);
				delete v;
			}

			rowind->clear();
			colptrs->clear();
			vals->clear();
			rows = cols = nonzeros = 0;
		}

		CompressedVector* CRSMatrixReader::GetRow(size_t rowNumber)
		{
			std::vector<double> values;
			std::vector<size_t> indices;
			std::map<size_t, double> map;

			// colptrs tutaj sa wskaznikami na wiersze
			size_t endIndex =  (rowNumber != (*colptrs).size()-1) ? (*colptrs)[rowNumber+1]: (*colptrs).size() ;
			size_t startIndex = (*colptrs)[rowNumber];

			if( endIndex > startIndex)
			{
				values.insert( values.begin(), vals->begin()+ startIndex, vals->begin()+endIndex);
				indices.insert( indices.begin(), rowind->begin()+ startIndex, rowind->begin()+endIndex);

			}

			for(size_t i = 0; i < indices.size(); i++)
				map[ indices[i] ] = values[i];

			CompressedVector *v = new CompressedVector(map,cols);
			return v;
		}

		CRSMatrixPtr ReadCRSFromSingleFile(const std::string &filename, const std::string &matrixname)
		{
			using namespace boost::posix_time;
			std::ifstream file( filename.c_str() );
			if(!file)
				throw filesystem_error( "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 )
				LOG4CXX_WARN(logger,"WARNING: nz <= 0... Was it intentional?");

			CRSMatrixPtr pMatrix( new CRSMatrix( matrixname, rows, cols) );
			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;
			typedef boost::split_iterator<std::string::iterator> split_iterator;
			while( std::getline( file, line ) )
			{
				row_no ++;
				boost::trim(line);
				if( line.size() == 0 )
					continue;
				 split_iterator itr =
					boost::algorithm::make_split_iterator(line,
						boost::token_finder(boost::is_any_of( " \t" )));

				while(itr != split_iterator())
				{
					boost::iterator_range<std::string::iterator> r = *itr++;

					if (itr == split_iterator()) {
						std::stringstream err;
						err << "Wrong line " << row_no + 1 << " in file " << filename << ". Number of items is not even.";
						throw std::runtime_error( err.str() );
					}

					size_t colNo  = 0;
					colNo = atoi(&r.front());

					r = *itr++;
					double value = 0;
					value = atof(&r.front());

					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() );
					}
					if(value!=0)
						pMatrix->SetCell(value, row_no, colNo);
					else
						LOG4CXX_WARN(logger,"Warning, skipped 0 value read from file");
				}

				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);
					LOG4CXX_INFO(logger,"Reading CRS, row " << row_no << " / " << rows << " ETA: " << to_simple_string( milliseconds(eta_ms) ));
					start = microsec_clock::local_time();
				}

			}
			LOG4CXX_INFO(logger,"Reading CRS, done...");

			return pMatrix;
		}
	}
}

