// Beispielhafter Aufruf: BUILD_x86_64/progs/testActiveSemanticSegmentationBinary -config <CONFIGFILE>

/**
* @file testActiveSemanticSegmentationBinary.cpp
* @brief test semantic segmentation routines with actively selecting regions for labeling
* @author Alexander Freytag
* @date 27-02-2013
*/

#ifdef NICE_USELIB_OPENMP
#include <omp.h>
#endif

#include "core/basics/Config.h"
#include "core/basics/StringTools.h"
#include <vislearning/baselib/ICETools.h>

#include <semseg/semseg/SemanticSegmentation.h>
#include <semseg/semseg/SemSegLocal.h>
#include <semseg/semseg/SemSegCsurka.h>
#include <semseg/semseg/SemSegNovelty.h>
#include <semseg/semseg/SemSegNoveltyBinary.h>
#include <semseg/semseg/SemSegContextTree.h>

#include "core/image/FilterT.h"

#include <core/basics/ResourceStatistics.h>

#include <fstream>

using namespace OBJREC;

using namespace NICE;

using namespace std;

/**
 test semantic segmentation routines
*/
int main( int argc, char **argv )
{
  std::set_terminate( __gnu_cxx::__verbose_terminate_handler );

  Config conf( argc, argv );
  
  ResourceStatistics rs;
  
  NICE::MatrixT<double> matTemp;
  std::cerr << "foo " << std::endl;
  std::cerr << matTemp << std::endl;
  
  bool show_result = conf.gB( "debug", "show_results", false );

  bool write_results = conf.gB( "debug", "write_results", false );

  bool write_results_pascal = conf.gB( "debug", "write_results_pascal", false );

  std::string resultdir = conf.gS( "debug", "resultdir", "." );
  
  //how often do we want to iterate between sem-seg and active query?
  int activeIterations = conf.gI("main", "activeIterations", 1 );
    
  if ( write_results )
  {
    cerr << "Writing Results to " << resultdir << endl;
  }

  MultiDataset md( &conf );

  const ClassNames & classNames = md.getClassNames( "train" );

  string method = conf.gS( "main", "method", "SSCsurka" );

  //currently, we only allow SemSegNoveltyBinary, because it implements addNovelExamples()
  SemSegNoveltyBinary *semseg = NULL;
  
  Timer timer;
  timer.start();

  semseg = new SemSegNoveltyBinary( &conf, &md );

  timer.stop();
  
  std::cerr << "AL time for training: " << timer.getLast() << std::endl;

  const LabeledSet *testFiles = md["test"];

  std::set<int> forbidden_classes;
  std::string forbidden_classes_s = conf.gS( "analysis", "forbidden_classesTrain", "" );
  classNames.getSelection( forbidden_classes_s, forbidden_classes );
  
  std::set<int> forbidden_classesForActiveLearning;
  std::string forbidden_classesForActiveLearning_s = conf.gS( "analysis", "forbidden_classesForActiveLearning", "" );
  classNames.getSelection( forbidden_classesForActiveLearning_s, forbidden_classesForActiveLearning );
  
  
  int positiveClass;
  
  //check whether we have a single positive class
  std::string positiveClass_s = conf.gS ( "SemSegNoveltyBinary", "positiveClass", "" );
  std::set<int> positiveClassNumberTmp;
  classNames.getSelection ( positiveClass_s, positiveClassNumberTmp );  

  switch ( positiveClassNumberTmp.size() )
  {
    case 0:
    {
      positiveClass = 0;
//       std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
      break;
    }
    case 1:
    {
      positiveClass = *(positiveClassNumberTmp.begin());
//       std::cerr << "positive class will be number" << positiveClass << " with the name: " << positiveClass_s << std::endl;
      break;
    }
    default:
    {
      //we specified more than a single positive class. right now, this is not what we are interested in, but 
      //in theory we could also accept this and convert positiveClass into a set of ints of possible positive classes
      positiveClass = 0;
//       std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
      break;
    }
  }  
 
  
  std::cerr << "number of AL iterations: " << activeIterations << std::endl;
  for (int iterationCount = 0; iterationCount < activeIterations; iterationCount++)
  {    
    std::cerr << "SemSeg AL Iteration: " << iterationCount << std::endl;
    semseg->setIterationCountSuffix(iterationCount);

    int fileno = 0;

    std::cerr << "start looping over all files" << std::endl;
    LOOP_ALL_S( *testFiles )
    {
      EACH_INFO( classno, info );
      std::string file = info.img();

      NICE::ImageT<int> lm;
      NICE::MultiChannelImageT<double> probabilities;

      if ( info.hasLocalizationInfo() )
      {
        const LocalizationResult *l_gt = info.localization();

        lm.resize( l_gt->xsize, l_gt->ysize );
        //lm.set( 0 );
        l_gt->calcLabeledImage( lm, classNames.getBackgroundClass() );
      }

      ((SemanticSegmentation*)semseg)->semanticseg( file, lm, probabilities );

      fprintf( stderr, "testSemanticSegmentation: Segmentation finished !\n" );

      //ground truth image, needed for updating the confusion matrix
      //TODO check whether this is really needed, since we computed such a label image already within SemSegNovelty
      NICE::ImageT<int> lm_gt;

      if ( info.hasLocalizationInfo() )
      {
        const LocalizationResult *l_gt = info.localization();

        lm_gt.resize( l_gt->xsize, l_gt->ysize );
        lm_gt.set( 0 );

        fprintf( stderr, "testSemanticSegmentation: Generating Labeled NICE::Image (Ground-Truth)\n" );
        l_gt->calcLabeledImage( lm_gt, classNames.getBackgroundClass() );
      }

      if ( show_result || write_results )
      {
        NICE::ColorImage orig( file );
        NICE::ColorImage rgb;
        NICE::ColorImage rgb_gt;

        classNames.labelToRGB( lm, rgb );

        classNames.labelToRGB( lm_gt, rgb_gt );

        if ( write_results )
        {
          
          std::stringstream out;       
          std::vector< std::string > myList;
          StringTools::split ( Globals::getCurrentImgFN (), '/', myList );
          out << resultdir << "/" << myList.back();
          cerr << "Writing to file " << resultdir << "/"<< myList.back() << endl;
          
          std::string noveltyMethodString = conf.gS( "SemSegNoveltyBinary",  "noveltyMethod", "gp-variance");
          orig.write ( out.str() + "_orig.ppm" );
          rgb.write ( out.str() + "_" + noveltyMethodString + "_result_run_" + NICE::intToString(iterationCount) + ".ppm" );
          rgb_gt.write ( out.str() + "_groundtruth.ppm" );
        }

        if ( show_result )
        {
  #ifndef NOVISUAL
          showImage( rgb, "Result" );
          showImage( rgb_gt, "Groundtruth" );
          showImage( orig, "Input" );
  #endif
        }
      }


      fileno++;

    } //Loop over all test images


    //**********************************************
    //                  EVALUATION 
    //   COMPUTE CONFUSION MAT AND FINAL SCORES
    //**********************************************
    timer.start();
    
    double score = semseg->getAUCPerformance();
    std::cerr << "auc scores of run : " << iterationCount << " : " << score << std::endl;
    
    long maxMemory;
    rs.getMaximumMemory(maxMemory);
    cerr << "Maximum memory used: " << maxMemory << " KB" << endl;

    
    timer.stop();
    std::cout << "AL time for evaluation: " << timer.getLastAbsolute() << std::endl;
    

    
    //**********************************************
    //          INCLUDE THE NEW INFORMATION
    //           AND UPDATE THE CLASSIFIER
    //**********************************************    
      
     timer.start();
     semseg->addNovelExamples(); 
     
     timer.stop();
     std::cout << "AL time for incremental update: " << timer.getLastAbsolute() << std::endl;
     //alternatively, we could call the destructor of semseg, and create it again, which does the same thing 
     // (add new features, save the classifier, re-read it after initialization)
     //BUT this would not setup the forbidden and known classes properly!!! We should fix that!
     
     const Examples * novelExamples = semseg->getNovelExamples(); 
//      std::cerr << " ==================================== " << std::endl;
//      std::cerr << "new examples to be added: " << std::endl;
//      for ( uint i = 0 ; i < novelExamples->size() ; i++ )
//      {
//         std::cerr << (*novelExamples)[i].first << " "; (*novelExamples)[i].second.store(std::cerr);
//      }
//      std::cerr << " ==================================== " << std::endl;
     
    //check which classes will be added using the features from the novel region
    std::set<int> newClassNumbers;
    newClassNumbers.clear(); //just to be sure  
    for ( uint i = 0 ; i < novelExamples->size() ; i++ )
    {
      if (newClassNumbers.find( (*novelExamples)[i].first /* classNumber*/) == newClassNumbers.end() )
      {
        newClassNumbers.insert( (*novelExamples)[i].first );
      }
    }

    //accept the new classes as valid information
    for (std::set<int>::const_iterator clNoIt = newClassNumbers.begin(); clNoIt != newClassNumbers.end(); clNoIt++)
    {
      if ( forbidden_classes.find ( *clNoIt ) != forbidden_classes.end() )
      {
        forbidden_classes.erase(*clNoIt);
      }
    }       

    std::cerr << "iteration finished - start the next round" << std::endl;
    
  } //iterationCount

  delete semseg;

  return 0;
}