/*
    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 "../comparator_stdheaders.h"
#include "MatrixTransformer.h"

#include "boost/date_time/posix_time/posix_time.hpp"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.comparator.weights.MatrixTransformer"));

namespace smartcomparator
{
	namespace weights
	{
		MatrixTransformer::MatrixTransformer(boost::shared_ptr<WeightFeature> weight) 
		{
			m_pWeight = new boost::shared_ptr<WeightFeature> (weight);
			m_dLogInterval = 10;
		}

		MatrixTransformer::~MatrixTransformer(void)
		{
			delete m_pWeight;
		}

		void MatrixTransformer::Transform()
		{
			Matrices::SuperMatrixPtr m = (*m_pWeight)->GetMatrix();
			LOG4CXX_INFO(logger, "Transforming matrix " << m->GetName());

			if(m->GetCRS() != NULL)
				CRSBasedTransform();
			else if(m->GetCCS() != NULL)
				CCSBasedTransform();
			LOG4CXX_INFO(logger,"Transforming matrix " << m->GetName() << "... Done" );
			
		}

		void MatrixTransformer::CCSBasedTransform()
		{
			Matrices::CCSMatrixPtr ccs = (*m_pWeight)->GetMatrix()->GetCCS();
			size_t noOfRows = ccs->NumberOfRows();
			size_t noOfColumns = ccs->NumberOfColumns();

			for(size_t rowNo = 0; rowNo < noOfRows; rowNo++)
			{
				double *row = ccs->GetRow(rowNo);
				Matrices::CompressedVector v(noOfColumns, row);
				
				std::set<size_t> nz_indices;//(v.GetNonZeroIndices()->begin(), v.GetNonZeroIndices()->end());
				for(std::map<size_t, double>::const_iterator i = v.GetValues()->begin(); i != v.GetValues()->end(); i++)
					nz_indices.insert(i->first);

				(*m_pWeight)->Weight(rowNo, &v, nz_indices);

				if(rowNo % 100 == 0)
					LOG4CXX_INFO(logger,"Transformed " << rowNo << " out of " << noOfRows << " rows...  ") ;
				ccs->FreeMem(row);
			}

			LOG4CXX_INFO(logger, "Syncing...");
			(*m_pWeight)->GetMatrix()->SyncCCSToCRS();
			LOG4CXX_INFO(logger, "Syncing done..." );
		}


		void MatrixTransformer::CRSBasedTransform()
		{
			using namespace boost::posix_time;
			Matrices::CRSMatrixPtr crs = (*m_pWeight)->GetMatrix()->GetCRS();
			size_t noOfRows = crs->NumberOfRows();

			time_duration total_time;
			ptime start = microsec_clock::local_time();
			for(size_t rowNo = 0; rowNo < noOfRows; rowNo++)
			{
				Matrices::CompressedVector &v =(* crs->GetCompressedRow(rowNo) );
				std::set<size_t> nz_indices;
				for(std::map<size_t, double>::const_iterator i = v.GetValues()->begin(); 
					i != v.GetValues()->end(); i++)
					nz_indices.insert(i->first);

				(*m_pWeight)->Weight(rowNo, &v, nz_indices);

				ptime end = microsec_clock::local_time();
				time_duration len = end - start;
				if(len.total_milliseconds() > 5000)
				{
					total_time += len;
					long total_time_ms = total_time.total_milliseconds();
					double rows_per_sec = double( (rowNo+1)*1000)  / total_time_ms;
					long eta_ms = (noOfRows*total_time_ms) / (rowNo+1) - total_time_ms;
					
					LOG4CXX_INFO(logger, "Transformed " << rowNo << " out of " << noOfRows << " rows...  | row/sec = " << rows_per_sec << " ETA: "\
								 << to_simple_string( milliseconds(eta_ms) ) );
					start = microsec_clock::local_time();
				}
			}

			LOG4CXX_INFO(logger,"Syncing...");
			(*m_pWeight)->GetMatrix()->SyncCRSToCCS();
			LOG4CXX_INFO(logger, "Syncing done...");
		}

		void MatrixTransformer::SetWeightFunction(boost::shared_ptr<WeightFeature> weight)
		{
			delete m_pWeight;
			m_pWeight = new boost::shared_ptr<WeightFeature> (weight);
		}

		void WeightCRS(Matrices::CRSMatrix* crs, smartcomparator::weights::WeightFeaturePtr m_pWeight)
		{
			size_t noOfRows = crs->NumberOfRows();
			for(size_t rowNo = 0; rowNo < noOfRows; rowNo++)
			{
				Matrices::CompressedVector &v =(* crs->GetCompressedRow(rowNo) );
				std::set<size_t> nz_indices;
				for(std::map<size_t, double>::const_iterator i = v.GetValues()->begin(); 
					i != v.GetValues()->end(); i++)
					nz_indices.insert(i->first);

				m_pWeight->Weight(rowNo, &v, nz_indices);
			}
		}
	}

	
}
