
#include <stdexcept>
#include <queue>
#include <vector>
#include "../../../matrices/utils.h"

#include "SynsetClassSet.h"

namespace smartcomparator
{
	namespace simfunctions
	{


SynsetClassSet::SynsetClassSet() {

}

SynsetClassSet::SynsetClassSet( SynsetByIdMapPtrS& pTreesRoots ){
	initClassSet( pTreesRoots );
}

SynsetClassSet::~SynsetClassSet() {

}



void SynsetClassSet::initClassSet( SynsetByIdMapPtrS& pTreesRoots )
{
	mClasses.clear();

	for( SynsetByIdMap::iterator iter = pTreesRoots->begin(); iter != pTreesRoots->end(); ++iter ){
		SynsetClassPtrS synsetClass;
		SynsetClassByIdMap::iterator classIter = mClasses.find( iter->second->getClassEditable() );
		if( classIter == mClasses.end() ){
			synsetClass.reset( new SynsetClass( iter->second->getClassEditable() ) );
			mClasses.insert( std::pair<size_t, SynsetClassPtrS>(
				iter->second->getClassEditable(), synsetClass ) );
		} else {
			synsetClass = classIter->second;
		}
		synsetClass->getTreesEditable().insert( std::pair<size_t, SynsetPtrS>(
			iter->first, iter->second ) );
	}

	for( SynsetClassByIdMap::iterator iter = mClasses.begin(); iter != mClasses.end(); ++iter )
		iter->second->recomputeSize();
}


void SynsetClassSet::splitClasses(
	size_t					pDesiredClassNumber,
	float 					pTreeCutRatio )
{
	if( mClasses.size() > pDesiredClassNumber )
		throw std::runtime_error("Desired number of classes is less than starting classes number!\n");

	std::priority_queue<SynsetClassPtrS, std::vector<SynsetClassPtrS>, SynsetClassComparator > queue;

	size_t nextClassId = 0UL;
	for( SynsetClassByIdMap::iterator iter = mClasses.begin(); iter != mClasses.end(); ++iter ){
		if( nextClassId < iter->second->getId() )
			nextClassId = iter->second->getId();
		queue.push( iter->second );
	}

	while( queue.size() < pDesiredClassNumber ){
		SynsetClassPtrS cls 	= queue.top();
		SynsetClassPtrS newCls 	= cls->split( ++nextClassId, pTreeCutRatio );
		queue.pop();
		queue.push( cls );
		queue.push( newCls );
	}

	mClasses.clear();
	while( !queue.empty() ){
		mClasses.insert( std::pair<size_t, SynsetClassPtrS>(
			queue.top()->getId(), queue.top() ) );
		queue.pop();
	}
}


ClassIdsSetByLemmasMapPtrS SynsetClassSet::mapLemmasToClasses(){
	ClassIdsSetByLemmasMapPtrS mapping( new ClassIdsSetByLemmasMap() );

	for( SynsetClassByIdMap::iterator citer = mClasses.begin(); citer != mClasses.end(); ++citer ){
		SynsetByIdMap::iterator titer 	= citer->second->getTreesEditable().begin();
		SynsetByIdMap::iterator tend 	= citer->second->getTreesEditable().end();
		for(; titer != tend; ++titer )
			mapLemmasToClassesIR( titer->second, citer->first, mapping );
	}

	return mapping;
}


void SynsetClassSet::mapLemmasToClassesIR(
	SynsetPtrS& 					pSynset,
	size_t							pClassId,
	ClassIdsSetByLemmasMapPtrS& 	pMapping )
{
	{
		LexicalUnitByIdMap::iterator iter 	= pSynset->getLemmasEditable().begin();
		LexicalUnitByIdMap::iterator end 	= pSynset->getLemmasEditable().end();
		for(; iter != end; ++iter ){
			ClassIdsSetPtrS classSet;
			ClassIdsSetByLemmasMap::iterator classtiter = pMapping->find( iter->second->getLemma() );
			if( classtiter == pMapping->end() ){
				classSet = pMapping->insert( std::pair<std::string, ClassIdsSetPtrS>(
					iter->second->getLemma(),
					ClassIdsSetPtrS( new ClassIdsSet() ) ) ).first->second;
			} else {
				classSet = classtiter->second;
			}
			classSet->insert( pClassId );
		}
	}

	{
		SynsetByIdMap::iterator iter 	= pSynset->getChildsEditable().begin();
		SynsetByIdMap::iterator end		= pSynset->getChildsEditable().end();
		for(; iter != end; ++iter ){
			mapLemmasToClassesIR( iter->second, pClassId, pMapping );
		}
	}
}

/*
 * CRS Version
 * tests may be required
 */
Matrices::SuperMatrixPtr SynsetClassSet::createFeatureMatrixFC(
	ClassIdsSetByLemmasMapPtrS&	pLemmasMapping,
	Matrices::SuperMatrixPtr& 	pFrequneciesMatrix )
{
	Matrices::FeatureVectorPtr newRowsFeatures(
		new Matrices::FeatureVector( *pFrequneciesMatrix->GetColumnFeatures() ) );

	// klasy nie musza miec kolejnych indeksow, mapowanie indeksow klas na indeks kolumny w macierzy
	boost::shared_ptr<size_t> clsIdxToMatrixIdx( new size_t[ ( mClasses.rbegin()->first ) + 1UL ] );
	Matrices::FeatureVectorPtr newColsFeatures( new Matrices::FeatureVector() );
	{
		for( SynsetClassByIdMap::iterator iter = mClasses.begin(); iter != mClasses.end(); ++iter ){
			( clsIdxToMatrixIdx.get() )[ iter->second->getId() ] = newColsFeatures->size();
			newColsFeatures->push_back( Matrices::Feature(
				boost::lexical_cast<std::string>( iter->second->getId() ), 0UL, 0UL, 0UL, 0.0 ) );
		}
	}

	size_t missingLemmas = 0UL;
	Matrices::CRSMatrixPtr crsMtx( new Matrices::CRSMatrix(
		pFrequneciesMatrix->GetName(), newRowsFeatures->size(), newColsFeatures->size() ) );
	{
		Matrices::FeatureVectorPtr	freqRows		= ( pFrequneciesMatrix->GetRowFeatures() );
		size_t 						freqRowsSize 	= freqRows->size();
		for( size_t ri=0UL; ri<freqRowsSize; ++ri ){
			std::string label = (*freqRows)[ri].GetLabel().substr( (*freqRows)[ri].GetLabel().find(':')+1UL );
			ClassIdsSetByLemmasMap::iterator lemmaIter = pLemmasMapping->find( label );
			if( lemmaIter == pLemmasMapping->end() ){
				++missingLemmas;
				continue;
			}
			Matrices::STDoubleMapPtr row = ( pFrequneciesMatrix->GetRow( ri )->GetValues() );
			for( Matrices::STDoubleMap::iterator rIter = row->begin(); rIter != row->end(); ++rIter ){
				ClassIdsSet::iterator clsIter 	= lemmaIter->second->begin();
				ClassIdsSet::iterator clsEnd	= lemmaIter->second->end();
				for( ; clsIter != clsEnd; ++clsIter ){
					crsMtx->AddToCell( rIter->second, rIter->first, clsIdxToMatrixIdx.get()[*clsIter] );
				}
			}
		}
	}

	std::stringstream ss;
	ss << "Missing lemmas in WN: " << missingLemmas << "\n" << pFrequneciesMatrix->GetCorpusInfo();

	Matrices::SuperMatrixPtr smtx(
		new Matrices::SuperMatrix(
			pFrequneciesMatrix->GetName(),
			Matrices::CCSMatrixPtr(),
			crsMtx,
			newRowsFeatures,
			newColsFeatures,
			pFrequneciesMatrix->GetTokenCount(),
			ss.str()
		)
	);
	std::map<size_t, std::string> boundaries;
	boundaries.insert( std::pair<size_t, std::string>( newColsFeatures->size(), "End_of_matrix" ) );
	smtx->SetBoundaries( boundaries );

	return smtx;
}


/*
 * CRS Version
 */
Matrices::SuperMatrixPtr SynsetClassSet::createFeatureMatrixCF(
	ClassIdsSetByLemmasMapPtrS&	pLemmasMapping,
	Matrices::SuperMatrixPtr& 	pFrequneciesMatrix )
{
	Matrices::FeatureVectorPtr newColsFeatures(
		new Matrices::FeatureVector( *pFrequneciesMatrix->GetColumnFeatures() ) );

	// klasy nie musza miec kolejnych indeksow, mapowanie indeksow klas na indeks wiersza w macierzy klas
	boost::shared_ptr<size_t> clsIdxToMatrixIdx( new size_t[ ( mClasses.rbegin()->first ) + 1UL ] );
	Matrices::FeatureVectorPtr newRowsFeatures( new Matrices::FeatureVector() );
	{
		for( SynsetClassByIdMap::iterator iter = mClasses.begin(); iter != mClasses.end(); ++iter ){
			( clsIdxToMatrixIdx.get() )[ iter->second->getId() ] = newRowsFeatures->size();
			newRowsFeatures->push_back( Matrices::Feature(
				boost::lexical_cast<std::string>( iter->second->getId() ), 0UL, 0UL, 0UL, 0.0 ) );
		}
	}

	size_t missingLemmas = 0UL;
	Matrices::CRSMatrixPtr crsMtx( new Matrices::CRSMatrix(
		pFrequneciesMatrix->GetName(), newRowsFeatures->size(), newColsFeatures->size() ) );
	{
		Matrices::FeatureVectorPtr	freqRows		= ( pFrequneciesMatrix->GetRowFeatures() );
		size_t 						freqRowsSize 	= freqRows->size();
		for( size_t ri=0UL; ri<freqRowsSize; ++ri ){
			std::string label = (*freqRows)[ri].GetLabel().substr( (*freqRows)[ri].GetLabel().find(':')+1UL );
			ClassIdsSetByLemmasMap::iterator lemmaIter = pLemmasMapping->find( label );
			if( lemmaIter == pLemmasMapping->end() ){
				++missingLemmas;
				continue;
			}
			Matrices::STDoubleMapPtr row = ( pFrequneciesMatrix->GetRow( ri )->GetValues() );
			for( Matrices::STDoubleMap::iterator rIter = row->begin(); rIter != row->end(); ++rIter ){
				ClassIdsSet::iterator clsIter 	= lemmaIter->second->begin();
				ClassIdsSet::iterator clsEnd	= lemmaIter->second->end();
				// rIter->first is valid
				for( ; clsIter != clsEnd; ++clsIter )
					crsMtx->AddToCell( rIter->second, clsIdxToMatrixIdx.get()[*clsIter], rIter->first );
			}
		}
	}

	std::stringstream ss;
	ss << pFrequneciesMatrix->GetCorpusInfo() << "Missing lemmas in WN: " << missingLemmas << "\n";

	Matrices::SuperMatrixPtr smtx(
		new Matrices::SuperMatrix(
			pFrequneciesMatrix->GetName(),
			Matrices::CCSMatrixPtr(),
			crsMtx,
			newRowsFeatures,
			newColsFeatures,
			pFrequneciesMatrix->GetTokenCount(),
			ss.str()
		)
	);
	smtx->SetBoundaries( pFrequneciesMatrix->Boundaries() );

	return smtx;
}

SynsetClassByIdMap& SynsetClassSet::getClassSet(){
	return mClasses;
}

size_t SynsetClassSet::getSize() const {
	return mClasses.size();
}

	}
}
