#include <iostream>

#include "vislearning/features/fpfeatures/PixelPairFeature.h"
#include "vislearning/cbaselib/FeaturePool.h"

using namespace OBJREC;

using namespace std;
using namespace NICE;

#define BOUND(x,min,max) (((x)<(min))?(min):((x)>(max)?(max):(x)))
const int firstColorchannel = 0;
const int lastColorchannel = 2;

void PixelPairFeature::explode ( FeaturePool & featurePool, bool variableWindow ) const
{
	PixelPairFeature *f = new PixelPairFeature ( *this );
	int firstchannel = ( imagetype == CachedExample::I_COLOR ) ? firstColorchannel : 0;
	int lastchannel = ( imagetype == CachedExample::I_COLOR ) ? lastColorchannel : 0;

	int wsx = window_size_x / 2;
	int wsy = window_size_y / 2;
	
	int numberOfPairFeatures = (lastchannel - firstchannel + 1) * 
		(2*wsx / step_x) * (2*wsy / step_y);

	numberOfPairFeatures *= numberOfPairFeatures;

	for ( int _type = PPTYPE_DIFF ; _type < PPTYPE_VALUE ; _type++ )
	for ( int _x1 = -wsx ; _x1 < wsx ; _x1 += step_x )
	for ( int _y1 = -wsy ; _y1 < wsy ; _y1 += step_y )
	for ( int _b1 = firstchannel ; _b1 <= lastchannel ; _b1++ ) 
	for ( int _x2 = -wsx ; _x2 < wsx ; _x2 += step_x )
	for ( int _y2 = -wsy ; _y2 < wsy ; _y2 += step_y )
	for ( int _b2 = firstchannel ; _b2 <= lastchannel ; _b2++ ) 
	{
		if ( (_b1 == _b2) && (_x1 == _x2) && (_y1 == _y2) ) continue;
		f->x1 = _x1; f->y1 = _y1; f->b1 = _b1;
		f->x2 = _x2; f->y2 = _y2; f->b2 = _b2;
		f->type = _type;
		featurePool.addFeature ( f->clone(), 1.0 / ((PPTYPE_VALUE - PPTYPE_DIFF) * numberOfPairFeatures) );
	}

	f->type = PPTYPE_VALUE;
	for ( int _x1 = -wsx ; _x1 < wsx ; _x1 += step_x )
	for ( int _y1 = -wsy ; _y1 < wsy ; _y1 += step_y )
	for ( int _b1 = firstchannel ; _b1 <= lastchannel ; _b1++ ) 
	{
		f->x1 = _x1; f->y1 = _y1; f->b1 = _b1;
		featurePool.addFeature ( f->clone(), 1.0 / numberOfPairFeatures );
	}

	delete f;
}


Feature *PixelPairFeature::clone() const
{
    PixelPairFeature *fp =  new PixelPairFeature(*this);
    return fp;
}


/************* PixelPairFeature **************/
PixelPairFeature::PixelPairFeature( const Config *conf )
{
    window_size_x = conf->gI("PixelPairFeatures", "window_size_x", 24 );
    window_size_y = conf->gI("PixelPairFeatures", "window_size_y", 24 );
    step_x = conf->gI("PixelPairFeatures", "step_x", 1 );
    step_y = conf->gI("PixelPairFeatures", "step_y", 1 );
    bool use_color = conf->gB("PixelPairFeatures", "use_color", true );

    if ( use_color ) {
	imagetype = CachedExample::I_COLOR;
    } else {
	imagetype = CachedExample::I_GRAYVALUES;
    }
}

PixelPairFeature::PixelPairFeature ( int _window_size_x,	
			   int _window_size_y,
			   int _step_x,
			   int _step_y,
			   int _imagetype )
{
    window_size_x = _window_size_x;
    window_size_y = _window_size_y;
    x1 = 0; y1 = 0; b1 = firstColorchannel;
    x2 = 1; y1 = 0; b2 = firstColorchannel;
    step_x = _step_x;
    step_y = _step_y;
    type = PixelPairFeature::PPTYPE_DIFF;
    imagetype = _imagetype;
}

PixelPairFeature::~PixelPairFeature()
{
}

double PixelPairFeature::val( const Example *example ) const
{
    int xl = example->x;
    int yl = example->y;
    NICE::MultiChannelImageT<int> & img = example->ce->getIChannel ( imagetype );

    int xx1 = x1;
    int yy1 = y1;
    int xx2 = x1;
    int yy2 = x2;
    int exwidth = example->width;
    if ( exwidth != 0 )
    {
	int exheight = example->height;
	xx1 = xx1 * exwidth / window_size_x;	
	yy1 = yy1 * exheight / window_size_y;	
	xx2 = xx2 * exwidth / window_size_x;	
	yy2 = yy2 * exheight / window_size_y;	
    }

    int xsize = img.xsize;
    int ysize = img.ysize;

    const int *channel1 = img.data[b1];
    int p1x = BOUND ( xl + xx1, 0, xsize-1 );
    int p1y = BOUND ( yl + yy1, 0, ysize-1 );
    long off1 = p1x + p1y*xsize;
    int v1 = channel1[off1];
    
    if ( type != PPTYPE_VALUE ) 
    {
	const int *channel2 = img.data[b2];

	int p2x = BOUND ( xl + xx2, 0, xsize-1 );
	int p2y = BOUND ( yl + yy2, 0, ysize-1 );
	long off2 = p2x + p2y*xsize;
	int v2 = channel2[off2];
	
	if ( type == PPTYPE_DIFF ) 
	    return v1 - v2;
	else if ( type == PPTYPE_ABSDIFF )
	    return fabs(v1-v2);
	else if ( type == PPTYPE_SUM )
	    return v1 + v2;
	else
	    exit(-1);

    } else {
	return v1;
    }
}

void PixelPairFeature::restore (istream & is, int format)
{
    is >> type;
    is >> imagetype;
    is >> window_size_x;
    is >> window_size_y;
    is >> x1;
    is >> y1;
    is >> b1;
    is >> x2;
    is >> y2;
    is >> b2;
}

void PixelPairFeature::store (ostream & os, int format) const
{
    os << "PIXELPAIRFEATURE" << " " << type << " " 
       << imagetype << " "
       << window_size_x << " " << window_size_y << " "
       << " " << x1 << " " << y1 << " " << b1
       << " " << x2 << " " << y2 << " " << b2;
}

void PixelPairFeature::clear ()
{
    // nothing to do in my opinion
}

#if 0
void PixelPairFeature::calcFeatureValues ( const Examples & examples,
				    vector<int> & examples_selection,
				    FeatureValuesUnsorted & values ) const
{
    for ( vector<int>::const_iterator si = examples_selection.begin();
				   si != examples_selection.end();
				   si++ )
    {
	int index = *si;
	const pair<int, Example> & p = examples[index];
	int classno = p.first;
	const Example & example = p.second;
	double value = 0.0;

	int xsize, ysize;
	int xl = example.x - window_size_x/2;
        int yl = example.y - window_size_y/2;

	const double *channel1 = example.ce->getChannel ( b1, xsize, ysize );
	int p1x = BOUND ( xl + x1, 0, xsize-1 );
	int p1y = BOUND ( yl + y1, 0, ysize-1 );
	long off1 = p1x + p1y*xsize;
	double v1 = channel1[off1];
    

	if ( type != PPTYPE_VALUE ) 
	{
	    const double *channel2 = example.ce->getChannel ( b2, xsize, ysize );

	    int p2x = BOUND ( xl + x2, 0, xsize-1 );
	    int p2y = BOUND ( yl + y2, 0, ysize-1 );
	    long off2 = p2x + p2y*xsize;
	    double v2 = channel2[off2];
	    
	    
	    if ( type == PPTYPE_DIFF ) 
		value = v1 - v2;
	    else if ( type == PPTYPE_ABSDIFF )
		value = fabs(v1-v2);
	    else if ( type == PPTYPE_SUM )
		value = v1 + v2;
	} else {
	    value = v1;
	}

	values.push_back ( quadruplet<double, int, int, double> ( 
	    value, classno, index, example.weight ) );
    }

}
#endif


#undef BOUND