/** 
* @file CodebookPrototypes.cpp
* @brief feature CodebookPrototypes
* @author Erik Rodner, Alexander Freytag
* @date 05-06-2013 (dd-mm-yyyy ) (original: 02/15/2008)
*/
#include <iostream>
#include <assert.h>

#include <core/image/Convert.h>

#include "vislearning/math/distances/genericDistance.h"

#include "CodebookPrototypes.h"

using namespace OBJREC;

using namespace std;

using namespace NICE;

CodebookPrototypes::CodebookPrototypes()
{
  this->clear();
  this->distanceType = "euclidean";
  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);   
}

CodebookPrototypes::CodebookPrototypes( const std::string & filename )
{
    Codebook::read(filename);
}

CodebookPrototypes::CodebookPrototypes( const NICE::VVector & vv )
{
  this->append ( vv, true /* _copyData*/ ); 

  reinit ( vv.size() );
  
  this->distanceType = "euclidean";
  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);     
}

CodebookPrototypes::CodebookPrototypes( NICE::Config * _conf, const std::string & _section) : Codebook ( _conf, _section )
{    
  this->distanceType = _conf->gS( _section, "distanceType", "euclidean" );
  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);  
  
  this->clear();
}

CodebookPrototypes::~CodebookPrototypes()
{
  //NOTE the config does not need to be deleted, it is just a pointer to an external data structure
}

void CodebookPrototypes::copy ( const Codebook *codebook )
{
    Codebook::copy ( codebook );
    VVector::clear();
    const CodebookPrototypes *codebookp = dynamic_cast<const CodebookPrototypes *> ( codebook );
    assert ( codebookp != NULL );
    insert ( begin(), codebookp->begin(), codebookp->end() );
    
  this->distanceType = this->p_conf->gS( this->s_section, "distanceType", "euclidean" );
  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);      
}

void CodebookPrototypes::voteVQ ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const
{
    const double *xp = feature.getDataPointer();
    size_t k = 0;
    double mindist = numeric_limits<double>::max();
    size_t my_cluster = 0;
    
    int len = feature.size();
    // iterate over all cluster centers
    for ( std::vector<NICE::Vector>::const_iterator i = this->begin();
            i != this->end();
            i++,k++ )
    {

      const NICE::Vector & cluster = *i;
      //slow ICE variant
      //double distance = (x - cluster).Length();
      
//       const double *clusterp = cluster.getDataPointer();
      double distance = this->distancefunction->calculate(*i, feature);
      
      //old version without distance function
//       for ( int i = 0 ; i < len ; i++ )
//       {
//           double h = clusterp[i] - xp[i];
//           distance += h*h;
//       }

      if ( distance < mindist )
      {
          my_cluster = k;
          mindist = distance;
      }
    }

    codebookEntry = my_cluster;
    weight = 1.0;
    distance = mindist;
}

void CodebookPrototypes::voteVA ( const NICE::Vector & feature, NICE::Vector & votes ) const
{
  votes.set( 0.0 );
  //TODO
}

void CodebookPrototypes::voteVA ( const NICE::Vector & feature, NICE::SparseVector & votes ) const
{
  votes.clear(); 
  //TODO
}

void CodebookPrototypes::add ( const Codebook *codebook )
{
    informativeMeasure.append ( codebook->getInformativeMeasure() );
    thresholds.append ( codebook->getThresholds() );
    classnos.append ( codebook->getClassNos() );

    const CodebookPrototypes *codebookp = dynamic_cast< const CodebookPrototypes *> ( codebook );
    assert ( codebookp != NULL );
    insert ( begin(), codebookp->begin(), codebookp->end() );
}

Codebook *CodebookPrototypes::clone () const
{
  //TODO !
    return (new CodebookPrototypes());
}

void CodebookPrototypes::clear ()
{
    VVector::clear();
    Codebook::clear();
}

void CodebookPrototypes::restore ( istream & is, int format )
{
    Codebook::restore ( is, format );
    VVector::restore ( is, format );
    
    std::cerr << "initial Codebook : " << std::endl; VVector::print ( std::cerr );
}

void CodebookPrototypes::store ( ostream & os, int format ) const
{
    Codebook::store ( os, format );
    VVector::store ( os, format );
}

void CodebookPrototypes::displayCodebook ( int xsize, int ysize ) const
{
    NICE::Image bigimg ( xsize * 5 , ysize * this->size() );
    bigimg.set(0);

    NICE::Image img_s (xsize, ysize);
    for ( int k = 0 ; k < (int)this->size() ; k++ )
    {
	NICE::Vector vimg = *(this->begin() + k);
	NICE::Image img ((int)sqrt((double)vimg.size()), (int)sqrt((double)vimg.size()));
	int i = 0;
	double max = - numeric_limits<double>::max();
	double min = numeric_limits<double>::min();
	for ( int y = 0 ; y < img.height() ; y++ )
	{
	    for ( int x = 0 ; x < img.width() ; x++,i++ )
	    {
		if ( max < vimg[i] ) 
		  max = vimg[i];
		if ( min > vimg[i] ) 
		  min = vimg[i];
	    }
	}

      i = 0;
      for ( int y = 0 ; y < img.height() ; y++ )
      {
          for ( int x = 0 ; x < img.width() ; x++,i++ )
          {
            img.setPixel( x, y, (int)((vimg[i] - min)*255/(max-min)) );
          }
      }

      
      NICE::scale ( img, &img_s );

      for ( int y = 0 ; y < ysize ; y++ )
      {
          for ( int x = 0 ; x < xsize ; x++ )
          {
            bigimg.setPixel(x, y+k*ysize, img_s.getPixel(x,y) );
          }
      }

      {
          std::ostringstream os;
          os << "no: " << k;
          if ( (int)informativeMeasure.size() > k ) 
          os << " " << informativeMeasure[k];
          // refactor-nice.pl: check this substitution
          // old: Text ( os.str().c_str(), xsize+1, k*ysize + 1, 255, 0, bigimg );
          // REFACTOR-FIXME Unable to map this statement
      }
    }

    bigimg.write ("/tmp/display.bmp");
}