
#include <queue>
#include <sstream>

#include "SynsetClass.h"

namespace smartcomparator
{
	namespace simfunctions
	{


SynsetClass::SynsetClass( size_t pId ) :
	mId( pId ),
	mSize( 0UL ),
	mLemmasCount( 0UL )
{

}

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


SynsetClassPtrS SynsetClass::split(
	size_t	pNewClassId,
	float 	pMinSizeSimilarity )
{
	SynsetClassPtrS 	newClass( new SynsetClass( pNewClassId ) );
	SynsetByIdMap& 		trees = getTreesEditable();

	std::priority_queue<SynsetPtrS, std::vector<SynsetPtrS>, SynsetComparator> queue;

	for( SynsetByIdMap::iterator iter = trees.begin(); iter != trees.end(); ++iter ){
		queue.push( iter->second );
	}

	//std::cout << "\n" << getSize() << " " << getTreesNumber() << std::endl;

	// split trees in class to two classes without cutting tree
	SynsetPtrS innerBigestTree;
	SynsetPtrS newBigestTree;

	mSize 			= 0UL;
	newClass->mSize = 0UL;
	while( !queue.empty() ){
		SynsetPtrS synset = queue.top();
		if( mSize > newClass->mSize ){
			newClass->mTrees.insert( std::pair<size_t, SynsetPtrS>(
				synset->getSynsetId(), synset ) );
			mTrees.erase( synset->getSynsetId() );
			newClass->mSize += synset->getNodesCount();
			if( newBigestTree == 0 )
				newBigestTree = synset;
		} else {
			mSize += synset->getNodesCount();
			if( innerBigestTree == 0 )
				innerBigestTree = synset;
		}
		queue.pop();
	}

	// determine which class is bigger
	SynsetClass* 	biggerClass;
	SynsetClass* 	smallerClass;
	SynsetPtrS 		treeToCut;
	if( newClass->mSize > mSize ){
		biggerClass 	= newClass.get();
		smallerClass 	= this;
		treeToCut 		= newBigestTree;
	} else {
		biggerClass 	= this;
		smallerClass 	= newClass.get();
		treeToCut 		= innerBigestTree;
	}

	// cut the bigest tree in bigger class if needed
	if( pMinSizeSimilarity > static_cast<float>( smallerClass->mSize ) / biggerClass->mSize )
	{
		size_t 		desiredTreeSize = ( biggerClass->mSize - smallerClass->mSize ) >> 1;
		SynsetPtrS 	cuttedTreeO, newTreeO;
		Synset::cutSingleTree( treeToCut, cuttedTreeO, newTreeO,
			desiredTreeSize, pMinSizeSimilarity );
		biggerClass->getTreesEditable().erase( treeToCut->getSynsetId() );
		biggerClass->getTreesEditable().insert( std::pair<size_t, SynsetPtrS>(
			cuttedTreeO->getSynsetId(), cuttedTreeO ) );
		smallerClass->mTrees.insert( std::pair<size_t, SynsetPtrS>(
			newTreeO->getSynsetId(), newTreeO ) );
	}

	recomputeSize();
	newClass->recomputeSize();

	//std::cout << getSize() << " " << getTreesNumber() << " " << "ncid: " << mId << std::endl;
	//std::cout << newClass->getSize() << " " << newClass->getTreesNumber() << " " << "ncid: " <<  newClass->mId << std::endl;

	return newClass;
}


SynsetByIdMap& SynsetClass::getTreesEditable(){
	return mTrees;
}


size_t SynsetClass::getTreesNumber() const {
	return mTrees.size();
}


size_t SynsetClass::getSize() const {
	return mSize;
}


size_t SynsetClass::getId() const {
	return mId;
}


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


void SynsetClass::recomputeSize(){
	mSize 			= 0UL;
	mLemmasCount	= 0UL;
	for( SynsetByIdMap::iterator iter = mTrees.begin(); iter != mTrees.end(); ++iter ){
		mSize 			+= iter->second->getNodesCount();
		mLemmasCount	+= iter->second->getLemmasCount();
	}
}


void SynsetClass::recomputeSizeAndNodesCount(){
	mSize 			= 0UL;
	mLemmasCount	= 0UL;
	for( SynsetByIdMap::iterator iter = mTrees.begin(); iter != mTrees.end(); ++iter ){
		iter->second->recomputeSizes();
		mSize 			+= iter->second->getNodesCount();
		mLemmasCount	+= iter->second->getLemmasCount();
	}
}


void SynsetClass::print() const {
	std::cout << mId << "(T=" << mTrees.size() << ",N=" << mSize << ")" << "=[";
	for( SynsetByIdMap::const_iterator iter = mTrees.begin(); iter != mTrees.end(); ++iter )
		iter->second->print();
	std::cout << "]" << std::endl;


}
std::string	SynsetClass::dumb() const {
	std::stringstream str;

	str << "Class ID:	" << mId << "\n"
		<< "Trees:  	" << mTrees.size() << "\n"
		<< "Nodes:  	" << mSize << "\n"
		<< "Lemmas: 	" << mLemmasCount << "\n\n"
		<< "Synsets:	" << "\n\n";

	SynsetByIdMap::const_iterator iter 	= mTrees.begin();
	SynsetByIdMap::const_iterator end 	= mTrees.end();
	for(; iter != end; ++iter ){
		str << iter->second->dumb( 0UL );
	}

	return str.str();
}


	}
}
