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

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

namespace Matrices
{
	CCSMatrix::CCSMatrix(const std::string &name, size_t rows, size_t columns)
		: Matrix(name, rows, columns), m_vColumns(NULL)
	{
		AllocateMemory();
	}

	CCSMatrix::~CCSMatrix(void)
	{
		ClearMemory();
	}

	void CCSMatrix::AllocateMemory()
	{
		if( m_vColumns != NULL)
			ClearMemory();

		m_vColumns = new std::vector<CompressedVectorPtr>();
		for(size_t i = 0; i < m_stColumns; i++)
			m_vColumns->push_back(CompressedVectorPtr(new CompressedVector(m_stRows, NULL)));
	}

	void CCSMatrix::ClearMemory()
	{
		if(m_vColumns)
		{
			delete m_vColumns;
			m_vColumns = NULL;
		}
	}

	double* CCSMatrix::GetColumn(size_t colNo)
	{
		size_t size = NumberOfRows();
		double *column = new double[size];
		double * data = (*m_vColumns)[colNo]->DecompressData();
		memcpy( column, data, sizeof(double) * size );
		(*m_vColumns)[colNo]->FreeBuffer();
		return column;
	}

	double* CCSMatrix::GetRow(size_t rowNo)
	{
		size_t size = NumberOfColumns();
		double *row = new double[size];
		for(size_t i = 0; i < size; i++)
			row[i] = GetCell(rowNo, i);
		return row;
	}

	double* CCSMatrix::operator[](size_t rowNo)
	{
		return GetRow(rowNo);
	}

	void CCSMatrix::SetRow(std::vector<double> &row, size_t rowIndex)
	{
		for(size_t c = 0; c < m_stColumns; c++)
			SetCell(row[c],rowIndex,c);
	}

	void CCSMatrix::SetRow(double* row, size_t rowIndex)
	{
		for(size_t c = 0; c < m_stColumns ; c++)
			SetCell(row[c],rowIndex,c);
	}

	void CCSMatrix::SetColumn(double* col, size_t colIndex)
	{
		(*m_vColumns)[colIndex] = CompressedVectorPtr(new CompressedVector(NumberOfRows(), col) );
	}

	void CCSMatrix::SumRows(double *row, size_t rowIndex)
	{
		double *rowToSum = GetRow(rowIndex);
		size_t columns = m_stColumns;
		for(size_t c = 0; c < columns ; c++)
			rowToSum[c] += row[c];
		SetRow(rowToSum,rowIndex);
	}

	Matrix*& CCSMatrix::Transpose(Matrix*&  returnMatrix)
	{
		if(returnMatrix != NULL)
			throw std::runtime_error(std::string("CCSMatrix::Transpose : returnMatrix != NULL"));

		CCSMatrix* mat = new CCSMatrix(m_sName+"_T", m_stColumns, m_stRows);
		returnMatrix = (Matrix*) mat;

		size_t columns = NumberOfColumns();
		for(size_t oldMCol = 0; oldMCol < columns ; oldMCol++)//dla kazdej kolumny w starej macierzy
		{
			double* col = GetColumn(oldMCol);
			mat->SetRow(col, oldMCol);
			delete[] col;
			if(oldMCol%20 == 0)
				LOG4CXX_INFO(logger,"Transposing matrix... " << oldMCol<< " / " << columns);
		}

		return returnMatrix;
	}

	void CCSMatrix::SetCell(double val, size_t rowInd, size_t colInd)
	{
		CompressedVectorPtr column = (*m_vColumns)[colInd];
		column->SetElement(rowInd, val);
	}

	double CCSMatrix::GetCell(size_t rowInd, size_t colInd)
	{
		CompressedVectorPtr column = (*m_vColumns)[colInd];
		return (*column) [rowInd];
	}

	void CCSMatrix::Clear()
	{
		ClearMemory();
		AllocateMemory();
	}

	void CCSMatrix::AddToCell(double value, size_t rowIndex, size_t columnIndex)
	{
		SetCell(GetCell(rowIndex,columnIndex)+value, rowIndex, columnIndex);
	}

	void CCSMatrix::Clone(Matrix* matrix)
	{
		using namespace boost::posix_time;
		if (matrix == NULL)
			throw std::runtime_error( std::string("CCSMatrix::Clone: input matrix cannot be null") );

		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t columnIndex = 0; columnIndex < NumberOfColumns(); columnIndex++)
		{
			double * column = GetColumn(columnIndex);
			matrix->SetColumn(column, columnIndex);
			FreeMem(column);
			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*(NumberOfColumns()-columnIndex-1)) / (columnIndex+1);
				LOG4CXX_INFO(logger,"Cloning column " << columnIndex << " / " << NumberOfColumns()
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ));
				start = microsec_clock::local_time();
			}
		}

	}
	void CCSMatrix::Clone(CRSMatrixPtr matrix, std::ostream &out)
	{
		using namespace boost::posix_time;
		if (matrix == NULL)
			throw std::runtime_error( std::string("CCSMatrix::Clone: input matrix cannot be null") );
		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t columnIndex = 0; columnIndex < NumberOfColumns(); columnIndex++)
		{
			CompressedVectorPtr  column = GetCompressedColumn(columnIndex);
			matrix->SetCompressedColumn(columnIndex, *column );

			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*(NumberOfColumns()-columnIndex-1)) / (columnIndex+1);
				out << "Cloning column " << columnIndex << " / " << NumberOfColumns()
						<< " ETA: " << to_simple_string( milliseconds(eta_ms) )  << "          \r";
				out.flush();
				start = microsec_clock::local_time();
			}

		}

	}

	void CCSMatrix::Clone(CCSMatrix* matrix, std::ostream &out)
	{
		using namespace boost::posix_time;
		if (matrix == NULL)
			throw std::runtime_error( std::string("CCSMatrix::Clone: input matrix cannot be null") );
		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t columnIndex = 0; columnIndex < NumberOfColumns(); columnIndex++)
		{
			CompressedVectorPtr  column = GetCompressedColumn(columnIndex);
			matrix->SetCompressedColumn(columnIndex, *column );

			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*(NumberOfColumns()-columnIndex-1)) / (columnIndex+1);
				out << "Cloning column " << columnIndex << " / " << NumberOfColumns()
						<< " ETA: " << to_simple_string( milliseconds(eta_ms) )  << "          \r";
				out.flush();
				start = microsec_clock::local_time();
			}

		}

	}

	CompressedVectorPtr CCSMatrix::GetCompressedColumn(size_t columnNo)
	{
		return (*m_vColumns)[columnNo];
	}

	void CCSMatrix::SetCompressedColumn(size_t columnno, const CompressedVector & v)
	{
		(*m_vColumns)[columnno] = CompressedVectorPtr( new CompressedVector(v) );
	}

	void CCSMatrix::SetCompressedColumn(size_t columnno, const CompressedVectorPtr  pV)
	{
		(*m_vColumns)[columnno] = pV;
	}

	void CCSMatrix::SetCompressedRow(size_t rowNo, CompressedVector v)
	{
		STDoubleMapPtr values = v.GetValues();
		for(STDoubleMapIter iter=values->begin();iter!=values->end();++iter)
		{
			SetCell(iter->second,rowNo,iter->first);
		}
	}

	void CCSMatrix::SetCompressedRow(size_t rowNo, const CompressedVectorPtr pV)
	{
		STDoubleMapPtr values = pV->GetValues();
		for(STDoubleMapIter iter=values->begin();iter!=values->end();++iter)
		{
			SetCell(iter->second,rowNo,iter->first);
		}
	}

	void CCSMatrix::SetDescriptor(const std::string &name, size_t rows, size_t columns)
	{
		ClearMemory();
		Matrix::SetDescriptor(name, rows, columns);
		AllocateMemory();
	}
	CCSMatrixPtr GetNewCCSMatrix(const std::string &name, size_t rows, size_t columns)
	{
		return CCSMatrixPtr( new CCSMatrix(name,rows,columns) );
	}
}

