#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::MultiChannelImage3DT<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.width();
  int ysize = img.height();

  int p1x = BOUND ( xl + xx1, 0, xsize - 1 );
  int p1y = BOUND ( yl + yy1, 0, ysize - 1 );

  int v1 = img.get(p1x,p1y,b1);

  if ( type != PPTYPE_VALUE )
  {
    int p2x = BOUND ( xl + xx2, 0, xsize - 1 );
    int p2y = BOUND ( yl + yy2, 0, ysize - 1 );

    int v2 = img.get(p2x,p2y,b2);

    if ( type == PPTYPE_DIFF )
      return v1 - v2;
    else if ( type == PPTYPE_ABSDIFF )
      return fabs ( (double)(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