/*
    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 "SuperMatrixReader.h"
#include "CCSMatrixReader.h"
#include "CRSMatrixReader.h"
#include "SuperMatrixWriter.h"
#include "../SuperMatrix.h"
#include "defines.h"

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

namespace Matrices
{
	namespace IO
	{
		std::string NormalizeDir(const std::string &s)
		{
			char last_char = s[s.length() - 1];
			std::string ret (s);
			if(last_char == '\\' || last_char == '/')
				ret = ret.substr(0, s.length() - 1);
			return ret;

		}

		SuperMatrixPtr SuperMatrixReader::Read(const std::string &dir,const std::string &matrix_name, bool ccs, bool crs)
		{
			namespace fs = boost::filesystem ;
			LOG4CXX_INFO(logger, "Reading SuperMatrix matrix " << matrix_name << "...");
			// sprawdz czy wszystkie pliki istnieja w katalogu

			CheckDir(dir, matrix_name);

			// wczytaj cechy wierszy i kolumn
			fs::path rowfeatures (dir);
			rowfeatures /= matrix_name;
			rowfeatures /= ROW_FEATURE_FILENAME;
			FeatureVectorPtr row_features = LoadFeatures(rowfeatures.string());
			fs::path columnfeatures (dir);
			columnfeatures /= matrix_name;
			columnfeatures /= COLUMN_FEATURE_FILENAME;
			FeatureVectorPtr column_features = LoadFeatures(columnfeatures.string());

			// wczytaj macierze

			SuperMatrixPtr pMatrix = LoadMatrix(dir, matrix_name, ccs, crs, row_features, column_features);
			LOG4CXX_INFO(logger,"Reading SuperMatrix  matrix " << pMatrix->GetName() << "... done");

			return pMatrix;
		}


		SuperMatrixPtr SuperMatrixReader::LoadMatrix(const std::string &dir, const std::string &matrix_name, bool ccs, bool crs,
				FeatureVectorPtr row_features, FeatureVectorPtr column_features)
		{
			namespace fs = boost::filesystem ;
			// read INFO file

			fs::path info_filename (dir);
			info_filename /= matrix_name;
			info_filename /= INFO_FILENAME;
			std::string corpus_info, token_count_str;
			std::ifstream file(info_filename.string().c_str() );
			if(!file)
				throw filesystem_error( "Cannot open file " + info_filename.string() );
			std::getline(file, corpus_info);
			std::getline(file, token_count_str);
			size_t token_count = boost::lexical_cast<size_t>(token_count_str);
			// read crs
			CRSMatrixPtr pCRS;
			if(crs)
			{
				fs::path crs_filename(dir);
				crs_filename /= matrix_name;
				crs_filename /= CRS_PART_NAME;
				LOG4CXX_INFO(logger, "Reading CRS matrix from " << crs_filename.string());
				pCRS = ReadCRSFromSingleFile( crs_filename.string(), matrix_name);
				LOG4CXX_INFO(logger,"CRS matrix read..");
			}
			// read ccs
			CCSMatrixPtr pCCS;
			if(ccs)
			{
				fs::path ccs_filename(dir);
				ccs_filename /= matrix_name;
				ccs_filename /= CCS_PART_NAME;
				LOG4CXX_INFO(logger,  "Reading CCS matrix from " << ccs_filename.string());
				pCCS = ReadCCSFromSingleFile( ccs_filename.string(), matrix_name);
				LOG4CXX_INFO(logger,"CCS matrix read..\n");
			}
			std::map<size_t, std::string> boundaries = TryLoadingBoundaries(dir, matrix_name);
			if(boundaries.size() == 0)
				boundaries.insert( std::make_pair( column_features->size(), matrix_name) );

			LOG4CXX_INFO(logger,"Creating supermatrix");
			// create supermatrix
			return SuperMatrixPtr( new SuperMatrix(matrix_name, pCCS, pCRS, row_features, column_features,
												   token_count, corpus_info, boundaries) );
		}

		std::map<size_t, std::string> TryLoadingBoundaries(const std::string &mdir, const std::string &mname)
		{
			std::map<size_t, std::string> indices;
			boost::regex pattern( "(.*):=(.*)", boost::regex::icase );
			std::string dir = NormalizeDir(mdir);
			std::string fname;
			if(dir=="")
				fname = (mname + "/"+ "boundary.txt" );
			else
				fname = ( dir + "/" + mname + "/"+ "boundary.txt" );
			std::ifstream file( fname.c_str() );
			if(!file)
			{
				LOG4CXX_ERROR(logger,"Could not load " << fname);
				return indices;
			}

			std::string line;

			while( (std::getline(file,line) ) )
			{
				boost::smatch match;
				boost::trim(line);
				if( boost::regex_match(line, match, pattern) )
				{
					std::string name = match[1];
					std::string tmp = match[2];
					boost::trim(name);
					boost::trim(tmp);
					LOG4CXX_INFO(logger, name << " -> " << tmp);
                                        // -1 was a bug!// -1, beacause MIWeight uses <= for stop criterion during iterating through columns
                                        size_t id = boost::lexical_cast<size_t> ( tmp );
					indices[id] = name;
				}
			}//for each line

			return indices;
		}

		FeatureVectorPtr SuperMatrixReader::LoadFeatures(std::string filename)
		{
			std::ifstream file(filename.c_str());
			if(!file)
				throw filesystem_error( "Cannot open file for writing: " + filename);

			std::string line;
			std::getline(file, line);
			boost::trim(line);

			if(line != "label;nz;tf;TF;entropy" )
				throw std::runtime_error( "File " + filename + " has wrong header: " + line);

			FeatureVectorPtr features = FeatureVectorPtr( new FeatureVector() );

			int lineNo = 1;
			while( getline(file,line ) )
			{
				boost::trim(line);
				if( line.size() > 0)
				{
					std::vector<std::string> tokens;
					boost::split(tokens, line, boost::is_any_of(";") );
					if(tokens.size() != 5 )
					{
						std::stringstream err;
						err << "File " << filename << " contains wrong line ("
							<< lineNo << "): " << line;
						throw std::runtime_error( err.str() );
					}
					std::string label = tokens[0];
					size_t nz = boost::lexical_cast<size_t>( tokens[1] );
					size_t tf = boost::lexical_cast<size_t>( tokens[2] );
					size_t TF = boost::lexical_cast<size_t>( tokens[3] );
					double ent = boost::lexical_cast<double>( tokens[4] );
					Feature f(label, nz, tf, TF, ent);
					features->push_back(f);
				}
				lineNo++;
			}

			return features;
		}

		void SuperMatrixReader::CheckDir(const std::string &dir, const std::string &matrix_name)
		{
			namespace fs = boost::filesystem;
			fs::path dir_path(dir);
			if ( !boost::algorithm::ends_with(dir,matrix_name) )
			{
				dir_path /= matrix_name;
			}

			dir_path.normalize();

			if( ! fs::exists(dir_path) || ! fs::is_directory(dir_path) )
				throw std::runtime_error( (dir_path.string() +" is not a directory...").c_str() );

			fs::path info = dir_path / INFO_FILENAME;
			if( ! fs::exists(info))
				throw std::runtime_error( (info.string() +" does not exists...").c_str() );
			if( fs::is_directory(info) )
				throw std::runtime_error( (info.string() +" is a directory...").c_str() );

		}

		void CheckSuperMatrixFiles(const std::string &dir, const std::string &matrix_name, bool ccs, bool crs)
		{
			namespace fs = boost::filesystem;
			fs::path base(dir);
			base /= matrix_name;

			if(ccs)
				CheckFile(base, CCS_PART_NAME);
			if(crs)
				CheckFile(base, CRS_PART_NAME);
			CheckFile(base, INFO_FILENAME);
			CheckFile(base, ROW_FEATURE_FILENAME);
			CheckFile(base, COLUMN_FEATURE_FILENAME);
			CheckFile(base, (matrix_name + ROW_LABELS_SUFFIX).c_str());
			CheckFile(base, (matrix_name + COLUMN_LABELS_SUFFIX).c_str());
		}

		SuperMatrixPtr LoadSuperMatrix(const std::string &dir,const  std::string &name, bool ccs, bool crs)
		{
			SuperMatrixReader reader;
			return reader.Read(dir, name, ccs, crs);
		}

	}//namespace IO
}
