
#include "NoveltyDetectorCodebookLevel.h"

//STL
#include <iostream>

//core
#include <core/vector/VectorT.h>

//vislearning
#include <vislearning/features/localfeatures/LFonHSG.h>
#include <vislearning/features/localfeatures/LFColorSande.h>
#include <vislearning/features/localfeatures/LocalFeatureColorWeijer.h>
#include <vislearning/features/localfeatures/LFReadCache.h>
#include <vislearning/features/localfeatures/LFWriteCache.h>

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

  //**********************************************
  //
  //                 PROTECTED METHODS
  //
  //********************************************** 


void NoveltyDetectorCodebookLevel::setFeatureExtractor( const bool & _setForTraining )
{  
  //be careful with previously allocated memory
  if (this->featureExtractor != NULL)
    delete featureExtractor;  
  
    //feature stuff
  // which OpponentSIFT implementation to use {NICE, VANDESANDE}
  std::string opSiftImpl;  
  opSiftImpl = this->conf->gS ( "Descriptor", "implementation", "VANDESANDE" );
  // read features?
  bool readfeat;
  readfeat = this->conf->gB ( "Descriptor", "read", true );
  // write features?
  bool writefeat;  
  writefeat = this->conf->gB ( "Descriptor", "write", true );   
  
  // Welche Opponentsift Implementierung soll genutzt werden ?
  LocalFeatureRepresentation *cSIFT = NULL;
  LocalFeatureRepresentation *writeFeats = NULL;
  LocalFeatureRepresentation *readFeats = NULL;
  this->featureExtractor = NULL;
  if ( opSiftImpl == "NICE" )
  {
    if ( _setForTraining )
      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGtrain" );
    else
      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGtest" );
  }
  else if ( opSiftImpl == "VANDESANDE" )
  {
    if ( _setForTraining )
      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTrain" );
    else
      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTest" );
  }
  else
  {
    fthrow ( Exception, "feattype: %s not yet supported" << opSiftImpl );
  }

  this->featureExtractor = cSIFT;
  
  if ( writefeat )
  {
    // write the features to a file, if there isn't any to read
    writeFeats = new LFWriteCache ( this->conf, cSIFT );
    this->featureExtractor = writeFeats;
  }

  if ( readfeat )
  {
    // read the features from a file
    if ( writefeat )
    {
      readFeats = new LFReadCache ( this->conf, writeFeats, -1 );
    }
    else
    {
      readFeats = new LFReadCache ( this->conf, cSIFT, -1 );
    }
    this->featureExtractor = readFeats; 
  }  
  
  //only set feature stuff to NULL, deletion of the underlying object is done in the destructor
  if ( cSIFT != NULL )
    cSIFT = NULL;
  if ( writeFeats != NULL )
    writeFeats = NULL;
  if ( readFeats != NULL )
    readFeats  = NULL ;   
}



bool NoveltyDetectorCodebookLevel::loadInitialCodebook ( )
{
  if ( b_loadInitialCodebook  )
  {
    std::cerr << " INITIAL CODEBOOK ALREADY COMPUTED - RE-USE IT" << std::endl;    
    std::cerr << " // WARNING - WE DO NOT VERIFY WHETHER THIS IS THE CORRECT CODEBOOK FOR THIS TRAINING SET!!!!" << std::endl;    
    
    prototypes->clear();
    
    try
    {
      prototypes->read(cacheInitialCodebook);
    }
    catch (...)
    {
      std::cerr << "Error while loading initial codebook" << std::endl;
      return false;
    }  
    return true;
  }
  else
    return false;
}

bool NoveltyDetectorCodebookLevel::writeInitialCodebook ( )
{      
  if (  b_saveInitialCodebook )
  {
    std::cerr << " SAVE INITIAL CODEBOOK " << std::endl;
    
    try 
    {
      prototypes->write( cacheInitialCodebook );
    }
    catch (...)
    {
      std::cerr << "Error while saving initial codebook" << std::endl;
      return false;
    }    
    return true;
  } 
  else
    return false;
}


  //**********************************************
  //
  //                 PUBLIC METHODS
  //
  //********************************************** 


NoveltyDetectorCodebookLevel::NoveltyDetectorCodebookLevel ( const Config *_conf,
                               const MultiDataset *_md, const std::string & _section )
    : NoveltyDetector ( _conf, _section )
{ 
  // define the distance function to be used
  std::string distFunctionString = conf->gS(section, "distFunction", "euclidian");    
  
  
  //**********************************************
  //
  //      SET UP VARIABLES AND METHODS
  //             - FEATURE TYPE
  //             - DISTANCE FUNCTION
  //             - ...
  //
  //**********************************************  
  
  
  if (distFunctionString.compare("euclidian") == 0)
  {
    distFunction = new NICE::EuclidianDistance<double>();
  }
  else
  {
    std::cerr << "Unknown vector distance selected, use euclidian instead" << std::endl;
    distFunction = new NICE::EuclidianDistance<double>();
  }    

  //feature extraction for comparison against codebook
  this->featureExtractor = NULL;
  //feature extraction for unseen images
  this->setFeatureExtractor( false /* set for training */ );    
  
  this->prototypes = NULL;  
}

NoveltyDetectorCodebookLevel::~NoveltyDetectorCodebookLevel()
{
  // clean-up
  if ( distFunction != NULL )
    delete distFunction;
  if ( featureExtractor != NULL )
    delete featureExtractor; 
  if ( ( ! externalCodebook ) && (prototypes != NULL) )
    delete prototypes;
}

void NoveltyDetectorCodebookLevel::setCodebook( NICE::VVector * _prototypes)
{
  std::cerr << "aim to set codebook" << std::endl;
  // if we had no codebook so far, we simply store the pointer and remember, that we use the external one
  // such that we do not have to delete it lateron
  if ( this->prototypes == NULL)
  {
    std::cerr << "previous codebook is null -- set it as external codebook" << std::endl;
    this->prototypes = _prototypes;
    this->externalCodebook = true;
  }
  else
  {
    std::cerr << "previous codebook is not null - do we want to treat the new one as external?" << std::endl;
    //in case of an internal codebook, we need to copy the whole thing
    if ( ! externalCodebook )
    {
      *(this->prototypes) = *_prototypes;
      std::cerr << "new codebook treated as internal codebook" << std::endl;
    }
    //if we use a pointer to an external codebook, we simply renew the pointer
    else
    {
      this->prototypes = _prototypes;
      std::cerr << "new codebook treated as external codebook " << std::endl;
    }
  }
}
