/*
    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 "SuperMatrix.h"

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

namespace Matrices
{
	SuperMatrix::SuperMatrix(const std::string &name, CCSMatrixPtr ccs, CRSMatrixPtr crs,
							 FeatureVectorPtr rowFeatures, FeatureVectorPtr columnFeatures,
							 size_t tokens, const std::string &corpus_desc, std::map<size_t, std::string> boundaries)
								 :

								 m_mapOperatorBoundaries(boundaries),
								 m_cTokens(tokens),
								 m_sName(name),
								 m_pCCSMatrix(ccs),
								 m_pCRSMatrix(crs),
								 m_pRowFeatures(rowFeatures),
								 m_pColumnFeatures(columnFeatures),
								 m_sCorpusDescription(corpus_desc)
	{
		BuildLabelsIndex(*m_pRowFeatures, m_RowLabelIndex);
		BuildLabelsIndex(*m_pColumnFeatures, m_ColumnLabelIndex);
	}

	SuperMatrix::SuperMatrix(const std::string &name, CCSMatrixPtr ccs, CRSMatrixPtr crs,
							 FeatureVectorPtr rowFeatures, FeatureVectorPtr columnFeatures,
							 size_t tokens, const std::string &corpus_desc)
								 :
								 m_cTokens(tokens),
								 m_sName(name),
								 m_pCCSMatrix(ccs),
								 m_pCRSMatrix(crs),
								 m_pRowFeatures(rowFeatures),
								 m_pColumnFeatures(columnFeatures),
								 m_sCorpusDescription(corpus_desc)
	{
		BuildLabelsIndex(*m_pRowFeatures, m_RowLabelIndex);
		BuildLabelsIndex(*m_pColumnFeatures, m_ColumnLabelIndex);
	}


	SuperMatrix::~SuperMatrix()
	{
	}

	void SuperMatrix::SetBoundaries(std::map<size_t, std::string > newBoundaries)
	{
		m_mapOperatorBoundaries = newBoundaries;
	}

	std::map<size_t, std::string >& SuperMatrix::Boundaries()
	{
		return m_mapOperatorBoundaries;
	}

	const std::string& SuperMatrix::GetName() const
	{
		return m_sName;
	}

	CompressedVectorPtr SuperMatrix::GetRow(size_t rowNo)
	{
		if(m_pCRSMatrix != NULL)
			return m_pCRSMatrix->GetCompressedRow(rowNo);
		return CompressedVectorPtr();
	}

	CompressedVectorPtr SuperMatrix::GetColumn(size_t rowNo)
	{
		if( m_pCCSMatrix != NULL)
			return m_pCCSMatrix->GetCompressedColumn(rowNo);
		return CompressedVectorPtr();
	}
	void SuperMatrix::Clone(SuperMatrixPtr &matrix)
	{
		Matrices::CCSMatrixPtr ccsCopy;( new Matrices::CCSMatrix("ccs",1,1));
		Matrices::CRSMatrixPtr crsCopy; (new Matrices::CRSMatrix("crs",1,1));
		if(GetCCS()!=NULL)
		{
			ccsCopy = CCSMatrixPtr(new CCSMatrix("ccs",1,1));
			GetCCS()->Clone(ccsCopy.get());
		}
		if(GetCRS()!=NULL)
		{
			crsCopy = CRSMatrixPtr(new CRSMatrix("crs",1,1));
			GetCRS()->Clone(crsCopy.get());
		}
		matrix = SuperMatrixPtr( new SuperMatrix(GetName(),ccsCopy,crsCopy,GetRowFeatures(),GetColumnFeatures(),
								 GetTokenCount(),GetCorpusInfo(),Boundaries()));
	}

	boost::shared_ptr<Matrices::CCSMatrix> SuperMatrix::GetCCS()
	{
		return m_pCCSMatrix;
	}

	boost::shared_ptr<Matrices::CRSMatrix> SuperMatrix::GetCRS()
	{
		return m_pCRSMatrix;
	}

	void SuperMatrix::SyncCRSToCCS()
	{
		using namespace boost::posix_time;
		if(m_pCRSMatrix == NULL)
			return;

		size_t total_rows = NumberOfRows();
		size_t total_cols = NumberOfColumns();

		if(m_pCCSMatrix == NULL)
			m_pCCSMatrix = CCSMatrixPtr( new CCSMatrix("matrix_ccs", total_rows, total_cols ) );
		else
		{
			m_pCCSMatrix->Clear();
			m_pCCSMatrix->SetDescriptor("matrix_ccs", total_rows, total_cols);
		}

		ptime start = microsec_clock::local_time();
		time_duration total_time;

		for(size_t row= 0; row < total_rows; row++)
		{
			CompressedVectorPtr pRow = m_pCRSMatrix->GetCompressedRow(row);
			std::map<size_t, double> *row_elems = pRow->GetValues();
			for(std::map<size_t, double>::iterator iter = row_elems->begin(); iter != row_elems->end(); iter++)
			{
				size_t column = iter->first;
				double value = iter->second;
				m_pCCSMatrix->SetCell(value, row, column);
			}

			// progress...
			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*(total_rows-row-1)) / (row+1);
				LOG4CXX_INFO(logger, "Cloning row " << row << " / " << total_rows
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ));
				start = microsec_clock::local_time();
			}
		}


	}

	void SuperMatrix::SyncCCSToCRS()
	{
		using namespace boost::posix_time;
		if(m_pCCSMatrix == NULL )
			return;

		size_t total_rows = NumberOfRows();
		size_t total_cols = NumberOfColumns();

		if(m_pCRSMatrix == NULL)
			m_pCRSMatrix = CRSMatrixPtr( new CRSMatrix("matrix_crs", total_rows,total_cols ) );
		else
		{
			m_pCRSMatrix->Clear();
			m_pCRSMatrix->SetDescriptor("matrix_crs", total_rows,total_cols);
		}

		ptime start = microsec_clock::local_time();
		time_duration total_time;

		for(size_t column = 0; column < total_cols; column++)
		{
			CompressedVectorPtr pColumn = m_pCCSMatrix->GetCompressedColumn(column);
			std::map<size_t, double> *col_elems = pColumn->GetValues();
			for(std::map<size_t, double>::iterator iter = col_elems->begin(); iter != col_elems->end(); iter++)
			{
				size_t row = iter->first;
				double value = iter->second;
				m_pCRSMatrix->SetCell(value, row, column);
			}

			// progress...
			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*(total_cols-column-1)) / (column+1);
				LOG4CXX_INFO(logger, "Cloning column " << column << " / " << total_cols
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ));
				start = microsec_clock::local_time();
			}
		}
	}

	/**
	Zmiana nazwy nie powinna pociagac za soba konsekwncji (przynajmniej nie dopoki uzywane macierze
	mieszcza sie w pamieci... )
	*/
	void SuperMatrix::SetNewName(std::string newName)
	{
		m_sName = newName;
	}

	void SuperMatrix::SetTokenCount(size_t newCount)
	{
		m_cTokens = newCount;
	}

	size_t SuperMatrix::GetTokenCount() const
	{
		return m_cTokens;
	}

	size_t SuperMatrix::NumberOfRows() const
	{
		if(m_pCRSMatrix != NULL)
			return m_pCRSMatrix->NumberOfRows();

		if(m_pCCSMatrix != NULL)
			return m_pCCSMatrix->NumberOfRows();

		return 0;
	}
	size_t SuperMatrix::NumberOfColumns() const
	{
		if(m_pCRSMatrix != NULL)
			return m_pCRSMatrix->NumberOfColumns();

		if(m_pCCSMatrix != NULL)
			return m_pCCSMatrix->NumberOfColumns();

		return 0;
	}

	size_t SuperMatrix::GetRowIndex(const std::string & rowlabel)
	{
		LabelIndex::iterator find = m_RowLabelIndex.find(rowlabel);
		if(find == m_RowLabelIndex.end() )
			return (size_t) -1;//cast to size_t max
		return find->second;
	}
	CompressedVectorPtr SuperMatrix::GetRow(const std::string & rowlabel)
	{
		if(m_pCRSMatrix == NULL)
			return CompressedVectorPtr();

		LabelIndex::iterator find = m_RowLabelIndex.find(rowlabel);
		if(find == m_RowLabelIndex.end() )
			return CompressedVectorPtr();

		return GetRow( find->second);
	}

	size_t SuperMatrix::GetColumnIndex(const std::string & columnlabel)
	{
		LabelIndex::iterator find = m_ColumnLabelIndex.find(columnlabel);
		if(find == m_ColumnLabelIndex.end() )
			return (size_t) -1;//cast to size_t max
		return find->second;
	}
	CompressedVectorPtr SuperMatrix::GetColumn(const std::string &columnLabel)
	{
		if(m_pCCSMatrix == NULL)
			return CompressedVectorPtr();

		LabelIndex::iterator find = m_ColumnLabelIndex.find(columnLabel);
		if(find == m_ColumnLabelIndex.end())
			return CompressedVectorPtr();

		return GetColumn( find->second );
	}

	double SuperMatrix::GetCell(size_t rowInd, size_t colInd)
	{
		boost::shared_ptr<Matrix> matrix;
		if(m_pCRSMatrix == NULL)
			if(m_pCCSMatrix == NULL)
				return -1;
			else
				matrix = m_pCCSMatrix;
		else
			matrix = m_pCRSMatrix;

		return matrix->GetCell(rowInd, colInd);
	}

	double SuperMatrix::GetCell(const std::string &rowLabel, const std::string &columnLabel)
	{
		size_t row_idx = GetRowIndex(rowLabel);
		size_t col_idx = GetColumnIndex(columnLabel);
		if (row_idx == (size_t) -1 || col_idx == (size_t) -1) {
			return -1;
		}

		return GetCell(row_idx, col_idx);
	}

	void SuperMatrix::SetCorpusInfo(std::string newInfo)
	{
		m_sCorpusDescription = newInfo;
	}

	const std::string& SuperMatrix::GetCorpusInfo() const
	{
		return m_sCorpusDescription;
	}
	FeatureVectorPtr SuperMatrix::GetRowFeatures()
	{
		return m_pRowFeatures;
	}
	FeatureVectorPtr SuperMatrix::GetColumnFeatures()
	{
		return m_pColumnFeatures;
	}

	void SuperMatrix::SetRowFeatures(FeatureVectorPtr fp)
	{
		m_pRowFeatures = fp;
		BuildLabelsIndex(*m_pRowFeatures, m_RowLabelIndex);
	}
	void SuperMatrix::SetColumnFeatures(FeatureVectorPtr fp)
	{
		m_pColumnFeatures = fp;
		BuildLabelsIndex(*m_pColumnFeatures, m_ColumnLabelIndex);
	}

	void SuperMatrix::SetCCS(CCSMatrixPtr  ccs)
	{
		m_pCCSMatrix = ccs;
	}
	void SuperMatrix::SetCRS(CRSMatrixPtr crs)
	{
		m_pCRSMatrix = crs;
	}
}

