/*
    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 "CCSMultiFileReader.h"
#include "CCSConverter.h"

namespace Matrices
{
	namespace IO
	{
		CCSMultiFileReader::CCSMultiFileReader(const std::string &dirname, const std::string &ccsmatrixname,
			const std::string &transformation_suffix, bool showMsg, std::ostream &log,const std::string &ct) :
			m_bShowMsg(showMsg),
			logout(log),
			compression_type(ct),
			pointers_file_suffix("_col_"),
			indices_file_suffix("_row_")
		{
			dirpath = dirname;
			this->transformation_suffix = transformation_suffix;
			name = ccsmatrixname;

			rowind = new std::vector<size_t> ();
			colptrs = new std::vector<size_t>();
			vals = new std::vector<double> ();
		}

		CCSMultiFileReader::~CCSMultiFileReader()
		{
			if(rowind)
			{
				delete rowind;
				rowind = NULL;
			}

			if(colptrs)
			{
				delete colptrs;
				colptrs = NULL;
			}

			if(vals)
			{
				delete vals;
				vals = NULL;
			}
		}

		void CCSMultiFileReader::Read(Matrices::Matrix &matrix) throw (filesystem_error)
		{
			try
			{
				boost::filesystem::path dir = boost::filesystem::path(dirpath);
				dir.normalize();

				CCSConverter converer(m_bShowMsg, logout);

				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();
				}

				LoadDim();
				LoadToMemory();

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


				converer.CCSToMatrix(matrix,*rowind,*colptrs, *vals);

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

			}
			catch(boost::filesystem::filesystem_error e)
			{

				throw filesystem_error("CCSMultiFileReader::Read", e.what());
			}

		}

		void CCSMultiFileReader::SetTransformationSuffix(const std::string &transformation_suffix)
		{
			this->transformation_suffix = transformation_suffix;
		}

		bool CCSMultiFileReader::FindFile(const std::string& name, const std::string &path_str,
			const std::string& sufix, std::string& ret)
		{

			using namespace boost::filesystem;

			path p(path_str);

			if( !exists(p) )
				return false;
			directory_iterator end;
			for(directory_iterator i(p); i != end; i++)
			{
				std::string ss = i->path().filename().c_str();
				if(!is_directory(*i) &&  boost::algorithm::starts_with(ss, name)
					&& boost::algorithm::ends_with(ss, sufix) )
				{
					ret = i->path().string();
					return true;
				}
			}

			return false;
		}



		void CCSMultiFileReader::LoadDim() throw(filesystem_error)
		{
			try
			{
				boost::filesystem::path dir = boost::filesystem::path(dirpath);

				std::string filename;
				if( FindFile(name,dir.string(), "_dim", filename) )
				{
					std::ifstream dimFile(filename.c_str());
					if(! dimFile )
					{
						std::string err = std::string("File not found: ") + filename;
						throw filesystem_error(err);
					}
					dimFile >> rows >> cols >> nonzeros;
				}
				else
					throw filesystem_error("CCSMultiFileReader::LoadDim",
					std::string("Dim file (*_dim) dosn't exist in dir:") + dirpath);
			}
			catch(boost::filesystem::filesystem_error e)
			{
				throw filesystem_error("CCSMultiFileReader::LoadDim", e.what());
			}
		}

		void CCSMultiFileReader::LoadToMemory() throw(filesystem_error)
		{
			try
			{
				using namespace std;
				int i;
				double d;

				if(m_bShowMsg)
					logout << "Reading " << compression_type << " files..." << std::endl;

				LoadDim();

				string rowFileName;

				if(!FindFile(name, dirpath, indices_file_suffix+compression_type, rowFileName))
					throw filesystem_error(std::string("Row file (*_row_XXX) dosn't exist in dir:") + dirpath);

				std::ifstream rowFile(rowFileName.c_str());

				if(!rowFile)
					throw filesystem_error(std::string("Error while opening row file: ") + rowFileName);
				if(m_bShowMsg)
					logout << "Reading row file..." << std::endl;

				while(rowFile)
				{
					rowFile >> i;
					if(rowFile)
						rowind->push_back(i);

				}
				rowFile.close();

				string valFileName;

				if(!FindFile(name,dirpath, transformation_suffix+"_nz_"+compression_type, valFileName))
					throw filesystem_error(std::string("Nz file (*_nz_ccs_XXX) dosn't exist in dir:") + dirpath);

				std::ifstream valFile(valFileName.c_str());
				if(!valFile)
					throw filesystem_error(std::string("Error while opening nz file: ") + valFileName);

				if(m_bShowMsg)
					logout << "Reading values files..." << std::endl;

				while(valFile)
				{
					valFile >> d;
					if(valFile)
						vals->push_back(d);
				}
				valFile.close();

				string colFileName;
				if(!FindFile(name,dirpath, pointers_file_suffix+compression_type, colFileName))
					throw filesystem_error(std::string("Colums file (*_col_XXX) dosn't exist in dir:") + dirpath);

				std::ifstream colFile(colFileName.c_str());
				if(!colFile)
					throw filesystem_error(std::string("Error while opening nz file: ") + colFileName);

				while(colFile)
				{
					colFile >> i;
					if(colFile)
						colptrs->push_back(i);
				}
				colFile.close();


				if(m_bShowMsg)
					logout << "Reading " << compression_type << " files... Done" << std::endl;

			}
			catch(boost::filesystem::filesystem_error e)
			{
				throw filesystem_error("CCSMultiFileReader::LoadToMemory", e.what());
			}
		}
	}
}

