// STL includes
#include <fstream>
#include <iostream>
#include <exception>
#include <stdlib.h>     /* used for  getenv */

// nice-vislearning includes
#include "vislearning/baselib/ColorSpace.h"
// 
#include "vislearning/features/localfeatures/LocalFeatureColorWeijer.h"


using namespace OBJREC;

using namespace std;

using namespace NICE;

//! color representation for visualization
const int colors[11][3] =
{
  {0,     0,   0}, // black
  {0,     0, 255}, // blue
  {165,  42,  42}, // brown
  {190, 190, 190}, // grey
  {  0, 255,   0}, // green
  {255, 165,   0}, // orange
  {255, 192, 203}, // pink
  {160,  32, 240}, // purple
  {255,   0,   0}, // red
  {255, 255, 255}, // white
  {255, 255,   0}, // yellow
};

///////////////////// ///////////////////// /////////////////////
//                   CONSTRUCTORS / DESTRUCTORS
///////////////////// ///////////////////// /////////////////////

OBJREC::LocalFeatureColorWeijer::LocalFeatureColorWeijer() : LocalFeature () 
{
  this->tfile = "";
}


LocalFeatureColorWeijer::LocalFeatureColorWeijer( const NICE::Config *_conf )
{
  this->initFromConfig ( _conf );
}

LocalFeatureColorWeijer::~LocalFeatureColorWeijer()
{
}

void LocalFeatureColorWeijer::initFromConfig ( const NICE::Config * _conf, const std::string & _confSection )
{

  this->tfile = _conf->gS( "LocalFeatureColorWeijer", "table", "");
  this->tfile = _conf->getAbsoluteFilenameRelativeToThisConfig( tfile );
  
  // If no destination  to the LUT was given, we try to use the file shipped with this library
  // Therefore, we try to catch the NICEHOME and if possible, append the location where the LUT file is stored
  // NOTE This will only work if NICEHOME is set in your environment (e.g., by exporting it in your bashrc or sourcing the setenv.sh)
  if ( this->tfile.compare("") == 0 )
  {
    char* pNICEHOME;
    pNICEHOME = getenv ("NICEHOME"); 
    
    if ( pNICEHOME != NULL )
    {
      this->tfile = std::string ( pNICEHOME );
      this->tfile += "/vislearning/features/localfeatures/input/colorWeijer/w2c.txt"; 
    }
  }
  
  this->restoreLUT();  
}

///////////////////// ///////////////////// /////////////////////
//                      FEATURE STUFF
///////////////////// ///////////////////// ////////////////// 

int LocalFeatureColorWeijer::getDescSize() const
{
  return LASTCOLOR;
}

void LocalFeatureColorWeijer::restoreLUT()
{
  ifstream fin( this->tfile.c_str() );
  if( !fin.is_open() )
  {
    fthrow(Exception,"ColorWeijer: could not find lookup table file. Specify the path in the config, e.g., YOURNICEHOME/vislearning/features/localfeatures/input/colorWeijer/w2c.txt");
  }
    
  while( !fin.eof() )
  {
    double rd,gd,bd;
    int r,g,b;
    fin >> rd;
    fin >> gd;
    fin >> bd;
        
    r = rd/8;
    g = gd/8;
    b = bd/8;

    // read LUT values for all 11 colornames 
    for( int i = 0; i < 11; i++ )
    {
      fin >> hist[r][g][b][i];
    }
  }
    /*
  for(int r = 0; r < 32; r++)
  {
    for(int g = 0; g < 32; g++)
    {
      for(int b = 0; b < 32; b++)
      {
        for(int i = 0; i < 11; i++)
        {
          fin >> hist[r][g][b][i];
        }
      }
    }
  }  */
}

int LocalFeatureColorWeijer::getDescriptors( const NICE::Image & img, VVector & positions, VVector & features ) const
{
  throw NICE::Exception ( "LocalFeatureColorWeijer extracts COLOR Features, it won't work on gray value images");
}

int LocalFeatureColorWeijer::getDescriptors( const NICE::ColorImage & img, VVector & positions, VVector & features ) const
{
  for ( int j = 0; j < ( int )positions.size(); j++ )
  {
    int x = positions[j][0];
    int y = positions[j][1];
    int r = img(x,y,0)/8;
    int g = img(x,y,1)/8;
    int b = img(x,y,2)/8;
      
    NICE::Vector feat( 11 );
    for ( uint i = 0; i < 11; i++ )
    {
      feat[i] = hist[r][g][b][i];
    }
    features.push_back( feat );
  }
 
  return 1;
}

void LocalFeatureColorWeijer::visualizeFeatures( NICE::Image & mark, const VVector & positions, size_t color ) const
{

}

int LocalFeatureColorWeijer::findColor( string &fn )
{
  if ( fn.find( "black" ) != string::npos )
    return BLACK;
  if ( fn.find( "blue" ) != string::npos )
    return BLUE;
  if ( fn.find( "brown" ) != string::npos )
    return BROWN;
  if ( fn.find( "grey" ) != string::npos )
    return GREY;
  if ( fn.find( "green" ) != string::npos )
    return GREEN;
  if ( fn.find( "orange" ) != string::npos )
    return ORANGE;
  if ( fn.find( "pink" ) != string::npos )
    return PINK;
  if ( fn.find( "purple" ) != string::npos )
    return PURPLE;
  if ( fn.find( "red" ) != string::npos )
    return RED;
  if ( fn.find( "white" ) != string::npos )
    return WHITE;
  if ( fn.find( "yellow" ) != string::npos )
    return YELLOW;
  
  return -1;
}

void LocalFeatureColorWeijer::visualizeFeatures( NICE::ColorImage & out, const VVector & features, const VVector & position ) const
{
  for ( int i = 0; i < ( int )position.size(); i++ )
  {
    int maxpos = 0;
    double maxval = 0.0;
    for ( int j = 0; j < ( int )features[i].size(); j++ )
    {
      if ( maxval < features[i][j] )
      {
        maxval = features[i][j];
        maxpos = j;
      }
    }

    out.setPixel( position[i][0], position[i][1], colors[maxpos][0], colors[maxpos][1], colors[maxpos][2] );
  }
}

void LocalFeatureColorWeijer::visualizeFeatures( const NICE::ColorImage & cimg ) const
{
  NICE::ColorImage out;
  this->visualizeFeatures( cimg, out );
}

void LocalFeatureColorWeijer::visualizeFeatures( const NICE::ColorImage & cimg, NICE::ColorImage &out ) const
{
  NICE::VVector pos, feats;
  for ( int y = 0; y < cimg.height(); y++ )
  {
    for ( int x = 0; x < cimg.width(); x++ )
    {
      Vector vec( 2 );
      vec[0] = x;
      vec[1] = y;
      pos.push_back( vec );
    }
  }

  this->getDescriptors( cimg, pos, feats );
  
  //heatmap for a special class
  /*ofstream fout("out.txt");
  
  int counter = 0;
  for ( int y = 0; y < cimg.height(); y++ )
  {
    for ( int x = 0; x < cimg.width(); x++, counter++ )
    {
      fout << feats[counter][8] << " ";
    }
  }
  cout << "counter: " << counter << " feats.size(): " << feats.size() << endl;
  
  fout.close();*/

  out.resize( cimg.width(), cimg.height() );
  out.set( 0, 0, 0 );
  this->visualizeFeatures( out, feats, pos );
  NICE::ColorImage combinedout( cimg.width()*2, cimg.height() );

  int width = ( int )cimg.width();

  for ( int y = 0; y < ( int )cimg.height(); y++ )
  {
    for ( int x = 0; x < width; x++ )
    {
      combinedout.setPixel( x, y, cimg.getPixel( x, y, 0 ), cimg.getPixel( x, y, 1 ), cimg.getPixel( x, y, 2 ) );
      combinedout.setPixel( x + width, y, out.getPixel( x, y, 0 ), out.getPixel( x, y, 1 ), out.getPixel( x, y, 2 ) );
    }
  }

  showImage( combinedout, "result" );
}

void LocalFeatureColorWeijer::getFeats( const NICE::ColorImage &img, MultiChannelImageT<double> &feats )
{
  int width = ( int )img.width();
  int height = ( int )img.height();
  feats.reInit( width, height, 11);

  for ( int y = 0; y < height; y++ )
  {
    for ( int x = 0; x < width; x++ )
    {
      int r = img(x,y,0)/8;
      int g = img(x,y,1)/8;
      int b = img(x,y,2)/8;
      
      for ( uint i = 0; i < 11; i++ )
      {
        feats.set( x, y, hist[r][g][b][i], i );
      }
    }
  }

}

void LocalFeatureColorWeijer::getFeats( const ColorImage &img, NICE::ImageT<double> &feats, int color )
{
  assert ( feats.width() > 0 );
  assert ( feats.height() > 0 );

  int width = ( int )img.width();
  int height = ( int )img.height();

  for ( int y = 0; y < height; y++ )
    for ( int x = 0; x < width; x++ )
    {
      int r = img(x,y,0)/8;
      int g = img(x,y,1)/8;
      int b = img(x,y,2)/8;

      feats.setPixel( x, y, hist[r][g][b][color] );
    }

}

///////////////////// INTERFACE PERSISTENT /////////////////////
// interface specific methods for store and restore
///////////////////// INTERFACE PERSISTENT ///////////////////// 

void LocalFeatureColorWeijer::restore ( std::istream & is, int format )
{
  //delete everything we knew so far...
  this->clear();
  
  bool b_restoreVerbose ( false );
#ifdef B_RESTOREVERBOSE
  b_restoreVerbose = true;
#endif  
  
  if ( is.good() )
  {
    if ( b_restoreVerbose ) 
      std::cerr << " restore LocalFeatureColorWeijer" << std::endl;
    
    std::string tmp;
    is >> tmp; //class name 
    
    if ( ! this->isStartTag( tmp, "LocalFeatureColorWeijer" ) )
    {
      std::cerr << " WARNING - attempt to restore LocalFeatureColorWeijer, but start flag " << tmp << " does not match! Aborting... " << std::endl;
      throw;
    }   
    
    bool b_endOfBlock ( false ) ;
    
    while ( !b_endOfBlock )
    {
      is >> tmp; // start of block 
      
      if ( this->isEndTag( tmp, "LocalFeatureColorWeijer" ) )
      {
        b_endOfBlock = true;
        continue;
      }      
      
      tmp = this->removeStartTag ( tmp );
      
      if ( b_restoreVerbose )
        std::cerr << " currently restore section " << tmp << " in LocalFeatureColorWeijer" << std::endl;
           
      if ( tmp.compare("tfile") == 0 )
      {
        is >> this->tfile;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      } 
      else
      {
      std::cerr << "WARNING -- unexpected LocalFeatureColorWeijer object -- " << tmp << " -- for restoration... aborting" << std::endl;
      throw;
      }
    }
  }
  else
  {
    std::cerr << "LocalFeatureColorWeijer::restore -- InStream not initialized - restoring not possible!" << std::endl;
    throw;
  }
  
  // try to load the LUT from a separate external file
  //NOTE we could also think about adding the LUT into the store/restore process. However, the table is BIG, so it would
  // blow up whole outfile
  this->restoreLUT ( ) ;
}

void LocalFeatureColorWeijer::store ( std::ostream & os, int format ) const
{ 
  if (os.good())
  {
    // show starting point
    os << this->createStartTag( "LocalFeatureColorWeijer" ) << std::endl;    
       
    os << this->createStartTag( "tfile" ) << std::endl;
    os << this->tfile << std::endl;
    os << this->createEndTag( "tfile" ) << std::endl;     
    
    // done
    os << this->createEndTag( "LocalFeatureColorWeijer" ) << std::endl;    
  }
  else
  {
    std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  }
}

void LocalFeatureColorWeijer::clear ()
{
}
