/*
hCRF-light Library 1.0 (full version http://hcrf.sf.net)
Copyright (C) 2012 Yale Song (yalesong@mit.edu)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "Features.h"

using namespace std;


///////////////////////////////////////////////////////////////////////////////////
// RawFeatures
//
RawFeatures::RawFeatures():FeatureType()
{
	strFeatureTypeName = "Raw Feature Type";
	featureTypeId = RAW_FEATURE_ID;
	basicFeatureType = NODE_FEATURE;
}

void RawFeatures::getFeatures(featureVector& listFeatures, DataSequence* X, Model* m, int nodeIndex, int prevNodeIndex, int seqLabel)
{
	if(X->getPrecomputedFeatures() != NULL && prevNodeIndex == -1)
	{
		dMatrix * preFeatures = X->getPrecomputedFeatures();
		int nbFeatures = preFeatures->getHeight();
		feature* pFeature;
		int idState = 0;
		int nbStateLabels = m->getNumberOfStates();

		for(int s = 0; s < nbStateLabels; s++)
		{
			for(int f = 0; f < nbFeatures; f++)
			{
				pFeature = listFeatures.addElement();
				pFeature->id = getIdOffset(seqLabel) + f + idState*nbFeatures;
				pFeature->globalId = getIdOffset() + f + idState*nbFeatures;
				pFeature->nodeIndex = nodeIndex;
				pFeature->nodeState = s;
				pFeature->prevNodeIndex = -1;
				pFeature->prevNodeState = -1;
				pFeature->sequenceLabel = seqLabel;
				pFeature->value = preFeatures->getValue(f,nodeIndex); 
			}
			idState ++;
		}
	}
}

void RawFeatures::getAllFeatures(featureVector& listFeatures, Model* m, int NbRawFeatures)
{
	int nbStateLabels = m->getNumberOfStates();
	feature* pFeature;

	for(int s = 0; s < nbStateLabels; s++)
	{
		for(int f = 0; f < NbRawFeatures; f++)
		{
			pFeature = listFeatures.addElement();
			pFeature->id = getIdOffset() + f + s*NbRawFeatures;
			pFeature->globalId = getIdOffset() + f + s*NbRawFeatures;
			pFeature->nodeIndex = featureTypeId;
			pFeature->nodeState = s;
			pFeature->prevNodeIndex = -1;
			pFeature->prevNodeState = -1;
			pFeature->sequenceLabel = -1;
			pFeature->value = f;
		}
	}
}



void RawFeatures::init(const DataSet& dataset, const Model& m)
{
	FeatureType::init(dataset,m);
	if(dataset.size() > 0)
	{
		int nbStates = m.getNumberOfStates();
		int nbSeqLabels = m.getNumberOfSequenceLabels();
		int nbFeaturesPerStates = dataset.at(0)->getPrecomputedFeatures()->getHeight();

		nbFeatures = nbStates * nbFeaturesPerStates;
		for(int i = 0; i < nbSeqLabels; i++)
			nbFeaturesPerLabel[i] = nbFeatures;
	}
}

bool RawFeatures::isEdgeFeatureType()
{
	return false;
}



///////////////////////////////////////////////////////////////////////////////////
// LabelEdgeFeatures
//
LabelEdgeFeatures::LabelEdgeFeatures():FeatureType()
{
	strFeatureTypeName = "Label Edge Feature Type";
	featureTypeId = LABEL_EDGE_FEATURE_ID;
	basicFeatureType = LABEL_EDGE_FEATURE;
}


void LabelEdgeFeatures::getFeatures(featureVector& listFeatures, DataSequence*, Model* m, int nodeIndex, int prevNodeIndex, int seqLabel)
{
	if(seqLabel != -1 && m->getNumberOfSequenceLabels() > 0 && prevNodeIndex == -1)
	{
		feature* pFeature;
		int nbStateLabels = m->getNumberOfStates();
		for(int s = 0; s < nbStateLabels; s++)
		{
			pFeature = listFeatures.addElement();
			pFeature->id = getIdOffset(seqLabel) + s;
			pFeature->globalId = getIdOffset() + s + seqLabel*nbStateLabels ;
			pFeature->nodeIndex = nodeIndex;
			pFeature->nodeState = s;
			pFeature->prevNodeIndex = -1;
			pFeature->prevNodeState = -1;
			pFeature->sequenceLabel = seqLabel;
			pFeature->value = 1.0f;
		}
	}
}

void LabelEdgeFeatures::getAllFeatures(featureVector& listFeatures, Model* m, int)
{
	if(m->getNumberOfSequenceLabels() > 0)
	{
		int nbStateLabels = m->getNumberOfStates();
		int nbSeqLabels = m->getNumberOfSequenceLabels();
		feature* pFeature;
		for(int seqLabel = 0; seqLabel < nbSeqLabels; seqLabel++)
		{
			for(int s = 0; s < nbStateLabels; s++)
			{
				pFeature = listFeatures.addElement();
				pFeature->id = getIdOffset() + s + seqLabel*nbStateLabels ;
				pFeature->globalId = getIdOffset() + s + seqLabel*nbStateLabels ;
				pFeature->nodeIndex = featureTypeId;
				pFeature->nodeState = s;
				pFeature->prevNodeIndex = -1;
				pFeature->prevNodeState = -1;
				pFeature->sequenceLabel = seqLabel;
				pFeature->value = 1.0f;
			}
		}
	}
}


void LabelEdgeFeatures::init(const DataSet& dataset, const Model& m)
{
	FeatureType::init(dataset,m);
	int nbStateLabels = m.getNumberOfStates();
	int nbSeqLabels = m.getNumberOfSequenceLabels();

	if(nbSeqLabels == 0)
		nbFeatures = 0;
	else
	{
		nbFeatures = nbStateLabels*nbSeqLabels;
		for(int i = 0; i < nbSeqLabels; i++)
			nbFeaturesPerLabel[i] = nbStateLabels;
	}
}

void LabelEdgeFeatures::computeFeatureMask(iMatrix& matFeautureMask, const Model& m)
{
	int nbLabels = m.getNumberOfSequenceLabels();
	int firstOffset = idOffset;

	for(int j = 0; j < nbLabels; j++)
	{
		int lastOffset = firstOffset + nbFeaturesPerLabel[j];
	
		for(int i = firstOffset; i < lastOffset; i++)
			matFeautureMask(i,j) = 1;

		firstOffset += nbFeaturesPerLabel[j];
	}
}

bool LabelEdgeFeatures::isEdgeFeatureType()
{
	return false;
}



///////////////////////////////////////////////////////////////////////////////////
// EdgeFeatures
//
EdgeFeatures::EdgeFeatures():FeatureType()
{
	strFeatureTypeName = "Edge Feature Type";
	featureTypeId = EDGE_FEATURE_ID;
	basicFeatureType = EDGE_FEATURE;
}

void EdgeFeatures::getFeatures(featureVector& listFeatures, DataSequence* X, 
							   Model* m, int nodeIndex, int prevNodeIndex, 
							   int seqLabel)
{
	// These features are only used for adjacent edge in the chain	
	int nbNodes = -1;
	
	if(X->getPrecomputedFeatures())
		nbNodes = X->getPrecomputedFeatures()->getWidth();
	else
		nbNodes = (int)X->getPrecomputedFeaturesSparse()->getWidth();
	
	if( ((prevNodeIndex == nodeIndex-1) || prevNodeIndex == nodeIndex-1 + nbNodes)
		&& (prevNodeIndex != -1))
	{
		feature* pFeature;
		int nbStateLabels = m->getNumberOfStates();
		for(int s1 = 0; s1 < nbStateLabels;s1++)
		{
			for(int s2 = 0; s2 < nbStateLabels;s2++)
			{
				pFeature = listFeatures.addElement();
				pFeature->id = getIdOffset(seqLabel) + s2 + s1*nbStateLabels ;
				pFeature->globalId = getIdOffset() + s2 + s1*nbStateLabels + 
					seqLabel*nbStateLabels*nbStateLabels ;
				pFeature->nodeIndex = nodeIndex;
				pFeature->nodeState = s2;
				pFeature->prevNodeIndex = prevNodeIndex;
				pFeature->prevNodeState = s1;
				pFeature->sequenceLabel = seqLabel;
				pFeature->value = 1.0f;
			}
		}
	}
}

void EdgeFeatures::getAllFeatures(featureVector& listFeatures, Model* m, int)
{
	int nbStateLabels = m->getNumberOfStates();
	int nbSeqLabels = m->getNumberOfSequenceLabels();
	feature* pFeature;
	if(nbSeqLabels == 0)
		nbSeqLabels = 1;
	for(int seqLabel = 0; seqLabel < nbSeqLabels;seqLabel++)
	{
		for(int s1 = 0; s1 < nbStateLabels;s1++)
		{
			for(int s2 = 0; s2 < nbStateLabels;s2++)
			{
				pFeature = listFeatures.addElement();
				pFeature->id = getIdOffset() + s2 + s1*nbStateLabels + seqLabel*nbStateLabels*nbStateLabels ;
				pFeature->globalId = getIdOffset() + s2 + s1*nbStateLabels + seqLabel*nbStateLabels*nbStateLabels ;
				pFeature->nodeIndex = featureTypeId;
				pFeature->nodeState = s2;
				pFeature->prevNodeIndex = -1;
				pFeature->prevNodeState = s1;
				pFeature->sequenceLabel = seqLabel;
				pFeature->value = 1.0f;
			}
		}
	}
}


void EdgeFeatures::init(const DataSet& dataset, const Model& m)
{
	FeatureType::init(dataset,m);
	int nbStateLabels = m.getNumberOfStates();
	int nbSeqLabels = m.getNumberOfSequenceLabels();

	if(nbSeqLabels == 0)
		nbFeatures = nbStateLabels*nbStateLabels;
	else
	{
		nbFeatures = nbStateLabels*nbStateLabels*nbSeqLabels;
		for(int i = 0; i < nbSeqLabels; i++)
			nbFeaturesPerLabel[i] = nbStateLabels*nbStateLabels;
	}
}

void EdgeFeatures::computeFeatureMask(iMatrix& matFeautureMask, const Model& m)
{
	int nbLabels = m.getNumberOfSequenceLabels();
	int firstOffset = idOffset;

	for(int j = 0; j < nbLabels; j++)
	{
		int lastOffset = firstOffset + nbFeaturesPerLabel[j];
	
		for(int i = firstOffset; i < lastOffset; i++)
			matFeautureMask(i,j) = 1;

		firstOffset += nbFeaturesPerLabel[j];
	}
}

bool EdgeFeatures::isEdgeFeatureType()
{
	return true;
}
