/**
* @file LFReadCache.cpp
* @brief read local features from file
* @author Erik Rodner, Alexander Freytag
* @date 02/14/2008
*/

// STL includes
#include <iostream>
#include <fstream>

// nice-core includes
#include <core/basics/StringTools.h>
#include <core/basics/FileMgt.h>

// nice-vislearning includes
#include <vislearning/baselib/Globals.h>
// 
#include "vislearning/features/localfeatures/GenericLFSelection.h"
#include "vislearning/features/localfeatures/LFReadCache.h"

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

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

void LFReadCache::setDescFormat ( const std::string & _descFormat_s )
{
  if ( _descFormat_s == "binary_double" )
    this->descFormat = VVector::FILEFORMAT_BINARY_DOUBLE;
  else if ( _descFormat_s == "binary_uchar" )
    this->descFormat = VVector::FILEFORMAT_BINARY_CHAR;
  else if ( _descFormat_s == "text_line" )
    this->descFormat = VVector::FILEFORMAT_LINE;
  else if ( ( _descFormat_s == "text_nice" ) || ( _descFormat_s == "text_ice" ) )
    this->descFormat = VVector::FILEFORMAT_NICE;
  else
    fthrow ( Exception, "Format " << _descFormat_s << " is unknown." );  
}

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

LFReadCache::LFReadCache ()
{
  this->numFeatures        = -1;
  this->cachedir           = "";
  this->cachemode          = Globals::getCacheMode ( "cat" );
  std::string descFormat_s = "binary_double";
  this->setDescFormat ( descFormat_s );
}

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

LFReadCache::LFReadCache ( const Config *_conf,
                           LocalFeatureRepresentation *_lfrep,
                           int _numFeatures,
                           const std::string & _confSection ) : lfrep ( _lfrep )
{
  //srand(time(NULL));
  this->numFeatures = _numFeatures;
  this->cachedir = _conf->gS ( _confSection, "root" );
  this->cachemode = Globals::getCacheMode ( _conf->gS ( _confSection, "mode", "cat" ) );

  std::string descFormat_s = _conf->gS ( _confSection, "descriptor_format", "binary_double" );
  this->setDescFormat ( descFormat_s );
}

LFReadCache::~LFReadCache()
{
  if ( this->lfrep != NULL )
  {
    delete this->lfrep;
    this->lfrep = NULL;
  }
}

void LFReadCache::initFromConfig(const NICE::Config* _conf, const std::string& _confSection)
{
  //srand(time(NULL));
  this->numFeatures = _conf->gI ( _confSection, "numFeatures", -1 );
  this->cachedir = _conf->gS ( _confSection, "root" );
  this->cachemode = Globals::getCacheMode ( _conf->gS ( _confSection, "mode", "cat" ) );

  std::string descFormat_s = _conf->gS ( _confSection, "descriptor_format", "binary_double" );
  this->setDescFormat ( descFormat_s );
  
  if ( this->lfrep != NULL )
  {
    delete this->lfrep;
    this->lfrep = NULL;
  }  
  this->lfrep = GenericLFSelection::selectLocalFeatureRep (  _conf, _confSection );  
}


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

int LFReadCache::getDescSize () const
{
  if ( this->lfrep == NULL )
  {
    fthrow ( Exception, "Unable to get descriptor size! (cache mode)" );
    return -1;
  }
  else {
    return lfrep->getDescSize();
  }
}


int LFReadCache::extractFeatures ( const NICE::ColorImage & img,
                                   VVector & features,
                                   VVector & positions ) const
{
  return extractFeaturesTemplate<NICE::ColorImage> ( img, features, positions );
}

int LFReadCache::extractFeatures ( const NICE::Image & img,
                                   VVector & features,
                                   VVector & positions ) const
{
  return extractFeaturesTemplate<NICE::Image> ( img, features, positions );
}

void LFReadCache::visualize ( NICE::Image & img,
                              const NICE::Vector & feature ) const
{
  lfrep->visualize ( img, feature );
}

void LFReadCache::visualizeFeatures ( NICE::Image & mark,
                                      const VVector & positions,
                                      size_t color ) const
{
  lfrep->visualizeFeatures ( mark, positions, color );
}

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

void LFReadCache::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, "LFReadCache" ) )
    {
      std::cerr << " WARNING - attempt to restore LFReadCache, 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, "LFReadCache" ) )
      {
        b_endOfBlock = true;
        continue;
      }      
      
      tmp = this->removeStartTag ( tmp );   

      
      if ( tmp.compare("numFeatures") == 0 )
      {
        is >> this->numFeatures;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("lfrep") == 0 )
      {
        //TODO generic restore function        
        this->lfrep->restore ( is );
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }       
      else if ( tmp.compare("cachedir") == 0 )
      {
        is >> this->cachedir;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }      
      else if ( tmp.compare("descFormat") == 0 )
      {
        is >> this->descFormat;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("cachemode") == 0 )
      {
        is >> this->cachemode;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }   
      else
      {
      std::cerr << "WARNING -- unexpected LFReadCache object -- " << tmp << " -- for restoration... aborting" << std::endl;
      throw;
      }
    }
  }
  else
  {
    std::cerr << "LFReadCache::restore -- InStream not initialized - restoring not possible!" << std::endl;
    throw;
  }
}

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

    os << this->createStartTag( "cachedir" ) << std::endl;
    os << this->cachedir << std::endl;
    os << this->createEndTag( "cachedir" ) << std::endl;
    
    os << this->createStartTag( "descFormat" ) << std::endl;
    os << this->descFormat << std::endl;
    os << this->createEndTag( "descFormat" ) << std::endl;
    
    os << this->createStartTag( "cachemode" ) << std::endl;
    os << this->cachemode << std::endl;
    os << this->createEndTag( "cachemode" ) << std::endl;
    
    // done
    os << this->createEndTag( "LFReadCache" ) << std::endl;    
  }
  else
  {
    std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  }
}

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

