/**
* @file LFonHSG.cpp
* @brief Implementation of the LocalFeatureHSG.h. See description there.
* @author Eric Bach, Alexander Freytag
* @date 26.10.2011
*/

/* LocalFeatureHSG Include */
#include "vislearning/features/localfeatures/LFonHSG.h"

using namespace std;
using namespace NICE;
using namespace OBJREC;

#ifdef NICE_USELIB_BOOST
using namespace boost;
#endif

///////////////////// ///////////////////// /////////////////////
//                   PROTECTED METHODS
///////////////////// ///////////////////// /////////////////

void LFonHSG::convertScalesStringToScaleList ( )
{
  if ( this->debug )
  {
    clog << "[log] LocalFeatureHSG::LocalFeatureHSG" << std::endl;
    clog << "[log] try to parse the 'scales-string': " << this->scales << " -> ";
  }  
  
  // the scales are seperated by '+', like in the Van de Sande implementation
  char separator = '+';
#ifdef NICE_USELIB_BOOST
  typedef tokenizer<boost::char_separator<char> > tokenizer;
  char_separator<char> sep (&separator);
  tokenizer tokens (scales, sep);		// parse the string into tokens
  for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter)
  {
    if ( this->debug )
      clog << *tok_iter << " ";
    
    this->scalesV.push_back(StringTools::convert<float>(*tok_iter));
  }
#else // standard
  std::vector<std::string> temp;
  StringTools::split (scales, separator, temp);
  for ( std::vector<std::string>::const_iterator it = temp.begin(); it != temp.end(); ++it)
  {
    if ( this->debug )
      clog << *it << " ";
    
    this->scalesV.push_back(StringTools::convert<float>(*it));
  }
#endif
  if ( this->debug )
  {
    clog << std::endl;
  }
}


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

LFonHSG::LFonHSG () : LocalFeatureRepresentation ()
{
  this->debug = false;

  this->sampleScaling = 50;
  this->scales = "1";
  /** parse scales string **/
  this->convertScalesStringToScaleList();
  
  this->lf = NULL;  
}

LFonHSG::LFonHSG ( const NICE::Config * _conf, const std::string _confSection)
{
  this->initFromConfig( _conf, _confSection );
}

LFonHSG::~LFonHSG()
{
  /** free memory of descriptors **/
  
  // don't waste memory
  if ( this->lf != NULL )
  {
    delete this->lf;
    this->lf = NULL;
  }
}

void OBJREC::LFonHSG::initFromConfig(const NICE::Config * _conf, const std::string & _confSection)
{
  this->debug         = _conf->gB (_confSection, "debug",          false);

  this->sampleScaling = _conf->gI (_confSection, "sample_scaling", 50);
  this->scales        = _conf->gS (_confSection, "scales"        , "1");
  
  this->lf            = NULL;
  this->lf            = OBJREC::GenericLocalFeatureSelection::selectLocalFeature (_conf, _confSection /*TODO check whether it is useful to hand over the confSection string here*/);

  /** parse scales string **/
  this->convertScalesStringToScaleList();
}


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


int LFonHSG::getDescSize() const
{
  return lf->getDescSize();
}

void LFonHSG::getPositionsOnHSG (const unsigned int imageWidth, const unsigned int imageHeight, VVector& positions) const
{
  if (sampleScaling < 1)
  {
    std::cerr << "[err] sample-scaling (" << sampleScaling << ") musst be larger the 0!" << std::endl;
    return;
  }

  if ( this->debug )
    clog << "[log] LocalFeatureHSG::getPositionsOnHSG calculate ";

  bool oddRow = true;
  NICE::Vector pos (4);

  /** we have to calculate the koo. for every different scale **/
  for ( std::vector<float>::const_iterator it = this->scalesV.begin(); it != this->scalesV.end(); ++it)
  {
    oddRow = true;

    for (unsigned int j = sampleScaling; j <= (imageHeight - sampleScaling); j += sampleScaling)
    {
      for ( unsigned int i = (oddRow ? sampleScaling + sampleScaling / 2 : sampleScaling);
            i <= (imageWidth - sampleScaling);
            i += sampleScaling
          )
      {
        pos[ 0 ] = i;
        pos[ 1 ] = j;
        pos[ 2 ] = *it;
        pos[ 3 ] = 0;
        positions.push_back (pos);
      }
      oddRow = !oddRow;
    }
  }
}

int LFonHSG::extractFeatures (const NICE::ColorImage & cimg, VVector & features, VVector & positions) const
{
  /** To get the keypoint descriptor, we need the positions of the keypoints. **/
  this->getPositionsOnHSG (cimg.width(), cimg.height(), positions);

  /** calulate the descriptor-values **/
  return lf->getDescriptors (cimg, positions, features);
}


int LFonHSG::extractFeatures (const NICE::Image & img, VVector & features, VVector & positions) const
{
  /** To get the keypoint descriptor, we need the positions of the keypoints. **/
  this->getPositionsOnHSG (img.width(), img.height(), positions);

  /** calculate the descriptor-values **/
  return lf->getDescriptors (img, positions, features);
}

void LFonHSG::visualizeFeatures (NICE::Image & mark, const VVector & positions, size_t color) const
{
  // TODO: Implementierung des gewaehlten Descriptortyps aufrufen.
  throw NICE::Exception( "LFonHSG::visualizeFeatures currently not implemented." );
}


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

void LFonHSG::restore ( std::istream & is, int format )
{
  //delete everything we knew so far...
  this->clear();
  
  
  if ( is.good() )
  {
    
    std::string tmp;
    is >> tmp; //class name 
        
    if ( ! this->isStartTag( tmp, "LFonHSG" ) )
    {
      std::cerr << " WARNING - attempt to restore LFonHSG, 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, "LFonHSG" ) )
      {
        b_endOfBlock = true;
        continue;
      }      
      
      tmp = this->removeStartTag ( tmp );   
      
      if ( tmp.compare("debug") == 0 )
      {
        is >> this->debug;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("sampleScaling") == 0 )
      {
        is >> this->sampleScaling;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }      
      else if ( tmp.compare("scales") == 0 )
      {
        is >> this->scales;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("scalesV") == 0 )
      {
        is >> tmp; //size:
        unsigned int ui_scalesVSize;
        is >> ui_scalesVSize;
        this->scalesV.clear();
        //allocate enough memory
        this->scalesV.resize( ui_scalesVSize );
        
        std::vector< float >::iterator itScalesV = this->scalesV.begin();
        for ( uint tmpIdx = 0; tmpIdx < ui_scalesVSize; tmpIdx++, itScalesV++ )
        {
          is >> *itScalesV;
        }

        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      } 
      else if ( tmp.compare("lf") == 0 )
      {
        OBJREC::GenericLocalFeatureSelection::restoreLocalFeature( this->lf, is );
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }    
      else
      {
      std::cerr << "WARNING -- unexpected LFonHSG object -- " << tmp << " -- for restoration... aborting" << std::endl;
      throw;
      }
    }
  }
  else
  {
    std::cerr << "LFonHSG::restore -- InStream not initialized - restoring not possible!" << std::endl;
    throw;
  }
}

void LFonHSG::store ( std::ostream & os, int format ) const
{ 
  if (os.good())
  {
    // show starting point
    os << this->createStartTag( "LFonHSG" ) << std::endl; 
    
    os << this->createStartTag( "debug" ) << std::endl;
    os << this->debug << std::endl;
    os << this->createEndTag( "debug" ) << std::endl;  

    os << this->createStartTag( "sampleScaling" ) << std::endl;
    os << this->sampleScaling << std::endl;
    os << this->createEndTag( "sampleScaling" ) << std::endl;
    
    os << this->createStartTag( "scalesV" ) << std::endl;
    os << "size: " << this->scalesV.size() << std::endl;
    for ( std::vector< float >::const_iterator itScalesV = this->scalesV.begin();
          itScalesV != this->scalesV.end();
          itScalesV++ 
         )
    {
      os << *itScalesV << " ";
    }            
    os << std::endl;
    os << this->createEndTag( "scalesV" ) << std::endl;  

    os << this->createStartTag( "lf" ) << std::endl;
    if ( this->lf != NULL )
    {
      //TODO this might be tricky if lf was not intantiated properly.      
      this->lf->store ( os );
    }
    os << this->createEndTag( "lf" ) << std::endl; 
    
    // done
    os << this->createEndTag( "LFonHSG" ) << std::endl;    
  }
  else
  {
    std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  }
}

void LFonHSG::clear ()
{
  if ( this->lf != NULL )
  {
    delete this->lf;
    this->lf = NULL;
  }  
}
