
#include <queue>
#include <vector>
#include <sstream>

#include "Synset.h"

namespace smartcomparator
{
	namespace simfunctions
	{


void Synset::createSynsetTrees(
	SynsetRelationSetPtrS& 	pRelationSet,
	SynsetByIdMapPtrS& 		pSynsetsMap )
{
	for( SynsetRelationSet::iterator relIter = pRelationSet->begin(); pRelationSet->end() != relIter; ++relIter ){
		size_t 		parentId 	= relIter->first;
		size_t 		childId		= relIter->second;
		SynsetByIdMap::iterator parentIter 	= pSynsetsMap->find( parentId );
		SynsetByIdMap::iterator childIter	= pSynsetsMap->find( childId );
		if( parentIter == pSynsetsMap->end() || childIter == pSynsetsMap->end() )
			continue;
		parentIter->second->getChildsEditable().insert(
			std::pair<size_t, SynsetPtrS>( childId, childIter->second ) );
	}

	for( SynsetByIdMap::iterator synIter = pSynsetsMap->begin(); pSynsetsMap->end() != synIter; ++synIter )
		synIter->second->recomputeSizes();
}


void Synset::cutSingleTree(
	SynsetPtrS& pTree,
	SynsetPtrS&	pOutCuttedTree,
	SynsetPtrS&	pOutNewTree,
	size_t		pDesiredNewTreeSize,
	float		pMinSizeSimilarity )
{
	long int nodesCountLB = static_cast<long int>( pMinSizeSimilarity * pDesiredNewTreeSize );
	long int nodesCountUB = static_cast<long int>( ( pDesiredNewTreeSize << 1 ) - nodesCountLB );

	pOutCuttedTree.reset( new Synset( pTree->getSynsetId(), pTree->getClassEditable() ) );
	pOutNewTree.reset( new Synset( pTree->getSynsetId(), pTree->getClassEditable() ) );

	pOutCuttedTree->getNodesCountEditable() += 1UL;
	pOutNewTree->getNodesCountEditable() 	+= 1UL;

	cutSingleTreeIR( pTree, pOutCuttedTree, pOutNewTree, nodesCountLB - 1UL, nodesCountUB - 1UL );

	pOutNewTree->recomputeSizes();
	pOutCuttedTree->recomputeSizes();
}


void Synset::cutSingleTreeIR(
	SynsetPtrS& pTree,
	SynsetPtrS&	pOutCuttedTree,
	SynsetPtrS&	pOutNewTree,
	long int	pNodesCountLB,
	long int	pNodesCountUB )
{
	pOutCuttedTree->mLemmas = pTree->getLemmasEditable();
	pOutNewTree->mLemmas 	= pTree->getLemmasEditable();

	std::priority_queue<SynsetPtrS, std::vector<SynsetPtrS>, SynsetComparatorR> queue;
	for( SynsetByIdMap::iterator iter = pTree->mChilds.begin(); iter != pTree->mChilds.end(); ++iter )
		queue.push( iter->second );

	// from the smallest tree to the biggest coz:
	// + we can stop iterating on first element which cannot be inserted coz UB
	// + if we have to split a tree then we have to split only one
	while( true ){
		SynsetPtrS synset = queue.top();
		// if there is enough space in new tree for new subtree then add subtree to new tree
		if( static_cast<long int>( synset->getNodesCount() ) <= pNodesCountUB ){
			pOutNewTree->mChilds.insert( std::pair<size_t, SynsetPtrS>(
				synset->getSynsetId(), synset ) );
			pOutNewTree->getNodesCountEditable() += synset->getNodesCount();
			pNodesCountLB -= synset->getNodesCount();
			pNodesCountUB -= synset->getNodesCount();
			queue.pop();
		} else {
			break;
		}
	}

	if( pNodesCountLB > 0L && queue.top()->getNodesCount() > 1UL ){
		// get one tree to split
		SynsetPtrS tosplit = queue.top();
		queue.pop();

		SynsetPtrS cuttedT 	= SynsetPtrS( new Synset( tosplit->getSynsetId(), tosplit->getClassEditable() ) );
		SynsetPtrS newT 	= SynsetPtrS( new Synset( tosplit->getSynsetId(), tosplit->getClassEditable() ) );
		pOutCuttedTree->mChilds.insert( std::pair<size_t, SynsetPtrS>(
			tosplit->getSynsetId(), cuttedT ) );
		pOutNewTree->mChilds.insert( std::pair<size_t, SynsetPtrS>(
			tosplit->getSynsetId(), newT ) );

		cutSingleTreeIR( tosplit, cuttedT, newT, pNodesCountLB - 1UL, pNodesCountUB - 1UL );
	}

	// add subtree which left to oldtree
	while( !queue.empty() ){
		SynsetPtrS synset = queue.top();
		pOutCuttedTree->mChilds.insert( std::pair<size_t, SynsetPtrS>(
			synset->getSynsetId(), synset ) );
		pOutCuttedTree->getNodesCountEditable() += synset->getNodesCount();
		queue.pop();
	}
}


Synset::Synset(
	size_t pId,
	size_t pClass )
:
	mId( pId ),
	mClass( pClass ),
	mNodesCount( 0UL ),
	mLemmasCount( 0UL )
{

}

Synset::~Synset() {
	// TODO Auto-generated destructor stub
}


LexicalUnitByIdMap& Synset::getLemmasEditable(){
	return mLemmas;
}


SynsetByIdMap& Synset::getChildsEditable(){
	return mChilds;
}


size_t& Synset::getClassEditable(){
	return mClass;
}


size_t& Synset::getNodesCountEditable(){
	return mNodesCount;
}


size_t& Synset::getLemmasCountEditable(){
	return mLemmasCount;
}


size_t Synset::getSynsetId() const {
	return mId;
}


size_t Synset::getChildsCount() const {
	return mChilds.size();
}


size_t Synset::getNodesCount() const {
	return mNodesCount;
}


size_t Synset::getLemmasCount() const {
	return mLemmasCount;
}


void Synset::recomputeSizes(){
	mNodesCount 	= 1UL;
	mLemmasCount 	= mLemmas.size();
	SynsetByIdMap::iterator iter 	= mChilds.begin();
	SynsetByIdMap::iterator end 	= mChilds.end();
	for(; iter != end; ++iter ){
		iter->second->recomputeSizes();
		mNodesCount 	+= iter->second->getNodesCount();
		mLemmasCount 	+= iter->second->getLemmasCount();
	}
}


void Synset::print() const {
	std::cout << mId << "@" << mClass << "(C=" << mChilds.size() << ")=[";
	SynsetByIdMap::const_iterator end = mChilds.end();
	SynsetByIdMap::const_iterator iter = mChilds.begin();
	for(; iter != end; ++iter )
		std::cout << iter->first << ",";
	std::cout << "], ";
}

std::string Synset::dumb( size_t pDeep ) const {
	std::stringstream str;

	str << "Level (deep)    " << pDeep << "\n"
		<< "Synset ID:      " << mId << "\n"
		<< "Nodes:          " << mNodesCount << "\n"
		<< "Lemmas in tree: " << mLemmasCount << "\n"
		<< "Lemmas:         " << mLemmas.size() << "\n\t";

	LexicalUnitByIdMap::const_iterator liter 	= mLemmas.begin();
	LexicalUnitByIdMap::const_iterator lend		= mLemmas.end();
	for(; liter != lend; ++liter ){
		str << liter->second->getLemma() << ", ";
	}
	str << "\n";
	if( mLemmas.size() > 0 )
		str << "\n";


	++pDeep;

	SynsetByIdMap::const_iterator iter 	= mChilds.begin();
	SynsetByIdMap::const_iterator end 	= mChilds.end();
	for(; iter != end; ++iter ){
		str << "\n" << iter->second->dumb( pDeep ) << "\n";
	}

	return str.str();
}


	}
}

