/*
    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 "GenericTransformer.h"
#include <boost/date_time/posix_time/posix_time.hpp>

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.comparator.transformations.GenericTransformer"));

namespace smartcomparator
{
	namespace transformations
	{
		GenericTransformer::GenericTransformer(Matrices::SuperMatrixPtr matrix,
							weights::WeightFeaturePtr weight,
							filters::FilterFeaturePtr filter,
							selectors::SelectorPtr selector,
							transformations::TransformationPtr transformation,
							bool showTime
							):Transformation(matrix),
							m_pMatrix(matrix),
							m_pFilter(filter),
							m_pSelector(selector),
							m_pWeight(weight),
							m_pTransformation(transformation),
							m_showTime(showTime)

		{
			//if(m_pWeight.use_count()==0 && m_pFilter.use_count()==0 && m_pSelector.use_count()==0&& m_pTransformation.use_count()==0)
			//	throw std::runtime_error("GenericTransformer: At least two arguments must be defined");
		}


		void GenericTransformer::TransformMatrix()
		{

			// transform each row of a matrix
			boost::posix_time::time_duration total_time;
			LOG4CXX_INFO(logger, "Transforming matrix..");

			for(size_t row_no = 0; row_no <m_pMatrix->NumberOfRows();++row_no)
			{
				boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
				Matrices::CompressedVectorPtr rowN1 = m_pMatrix->GetCRS()->GetCompressedRow(row_no);
				filterN1.clear();
				for(std::map<size_t, double>::const_iterator iter = rowN1->GetValues()->begin(); iter != rowN1->GetValues()->end(); iter++)
					filterN1.insert( iter->first );

				Filtering(m_pFilter, rowN1);
				Selection(m_pSelector, rowN1, row_no);
				Weighting(m_pWeight, rowN1, row_no);
				RemoveIndices(rowN1,row_no);

				boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
				boost::posix_time::time_duration len = end - start;
				if(len.total_milliseconds()> 5000 && m_showTime)
				{
					total_time += len;
					long total_time_ms = total_time.total_milliseconds();
					long eta_ms = (total_time_ms * ( m_pMatrix->NumberOfRows() - row_no) ) / row_no;
					LOG4CXX_INFO(logger, "Transformed "<< row_no << " of " <<m_pMatrix->NumberOfRows()<<" ETA: " << boost::posix_time::to_simple_string( boost::posix_time::milliseconds(eta_ms) ));
				}
			}


			if(m_pMatrix->GetCCS() != NULL)
				m_pMatrix->SyncCRSToCCS();
			Transforming(m_pTransformation);
			LOG4CXX_INFO(logger, "Transforming matrix... done...");

		}

		void GenericTransformer::Filtering(filters::FilterFeaturePtr filter, Matrices::CompressedVectorPtr row)
		{
			if(filter)
			{
				std::set<size_t> out1;
				filter->Filter(out1, &filterN1);

				for(std::set<size_t>::iterator iter = filterN1.begin(); iter != filterN1.end(); iter++)
					if( out1.find(*iter) == out1.end())
						row->SetElement(*iter, 0);
				filterN1.swap(out1);
			}

		}

		void GenericTransformer::Selection(selectors::SelectorPtr selector, Matrices::CompressedVectorPtr row,size_t rowNo)
		{
			if(selector)
			{
				// copy selected indcies
				std::set<size_t> in1;
				// clear them
				filterN1.swap(in1);
				// fill them up again
				selector->SelectBestFeatures(rowNo,in1,filterN1);
				for(std::set<size_t>::iterator iter = in1.begin(); iter != in1.end(); iter++)
					if( filterN1.find(*iter) == filterN1.end())
						row->SetElement(*iter, 0);
				filterN1.swap(in1);
			}

		}

		void GenericTransformer::Weighting(weights::WeightFeaturePtr weight, Matrices::CompressedVectorPtr row,size_t rowNo)
		{
			if(weight)
			{
				weight->Weight(rowNo, row.get(), filterN1);
			}
		}

		void GenericTransformer::RemoveIndices(Matrices::CompressedVectorPtr row, size_t rowNo)
		{
			std::set<size_t> remove;
			for(Matrices::STDoubleMapIter iter = row->GetValues()->begin(); iter != row->GetValues()->end(); ++iter)
				if( filterN1.find(iter->first) == filterN1.end())
					remove.insert(iter->first);

			for(std::set<size_t>::iterator iter = remove.begin(); iter != remove.end(); iter++)
				row->SetElement(*iter, 0);
		}

		void GenericTransformer::Transforming(transformations::TransformationPtr transformation)
		{
			if(transformation)
			{
				transformation->TransformMatrix();
			}
		}
	}
}




