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

#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"
#include "core/image/ImageT.h"

// STL includes
#include <iostream>
#include <fstream>
// 
#include <sys/stat.h>
#include <sys/types.h>

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

// nice-vislearning includes
#include <vislearning/baselib/Globals.h>

// 
#include "vislearning/features/localfeatures/GenericLFSelection.h"
#include "vislearning/features/localfeatures/LFWriteCache.h"

using namespace OBJREC;

using namespace std;
using namespace NICE;

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

void LFWriteCache::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
///////////////////// ///////////////////// /////////////////

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

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

LFWriteCache::LFWriteCache ( const Config *conf,
                             LocalFeatureRepresentation *_lfrep,
                             const std::string & _section ) : lfrep ( _lfrep )
{
  this->cachedir = conf->gS ( _section, "root" );
  this->cachemode = Globals::getCacheMode ( conf->gS ( _section, "mode", "cat" ) );

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

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

void LFWriteCache::initFromConfig(const NICE::Config* _conf, const std::string& _confSection)
{
  //srand(time(NULL));
  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 LFWriteCache::getDescSize () const
{
  return lfrep->getDescSize();
}

int LFWriteCache::extractFeatures ( const NICE::Image & img,
                                    VVector & features,
                                    VVector & positions ) const
{
  std::string filename = Globals::getCacheFilename ( cachedir, cachemode );

  int ret = lfrep->extractFeatures ( img, features, positions );
  std::string filename_desc = filename + ".desc";
  std::string filename_pos = filename + ".key";

  struct stat dummy;
  if ( stat ( filename_desc.c_str(), &dummy ) < 0 )
  {
    fprintf ( stderr, "features: %d, positions: %d\n", ( int ) features.size(),
              ( int ) positions.size() );
    if ( features.size() > 0 )
      fprintf ( stderr, "feature dimension: %d %d\n", features[0].size(), lfrep->getDescSize() );
    features.save ( filename_desc, descFormat );
    positions.save ( filename_pos, VVector::FILEFORMAT_LINE );
  } else {
    fprintf ( stderr, "description file %s exists !\n", filename_desc.c_str() );
  }


  return ret;
}

int LFWriteCache::extractFeatures ( const NICE::ColorImage & img,
                                    VVector & features,
                                    VVector & positions ) const
{
  std::string filename = Globals::getCacheFilename ( cachedir, cachemode );

  int ret = lfrep->extractFeatures ( img, features, positions );
  std::string filename_desc = filename + ".desc";
  std::string filename_pos = filename + ".key";

  struct stat dummy;

  if ( stat ( filename_desc.c_str(), &dummy ) < 0 )
  {
    fprintf ( stderr, "features: %d, positions: %d\n", ( int ) features.size(),
              ( int ) positions.size() );
    if ( features.size() > 0 )
      fprintf ( stderr, "feature dimension: %d %d\n", features[0].size(), lfrep->getDescSize() );
    features.save ( filename_desc, descFormat );
    positions.save ( filename_pos, VVector::FILEFORMAT_LINE );
  }
  else
  {
    fprintf ( stderr, "description file %s exists !\n", filename_desc.c_str() );
  }


  return ret;
}

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

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

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

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

      
      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 LFWriteCache object -- " << tmp << " -- for restoration... aborting" << std::endl;
      throw;
      }
    }
  }
  else
  {
    std::cerr << "LFWriteCache::restore -- InStream not initialized - restoring not possible!" << std::endl;
    throw;
  }
}

void LFWriteCache::store ( std::ostream & os, int format ) const
{ 
  if (os.good())
  {
    // show starting point
    os << this->createStartTag( "LFWriteCache" ) << 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( "LFWriteCache" ) << std::endl;    
  }
  else
  {
    std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  }
}

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