#ifdef NICE_USELIB_ICE

#include <core/imagedisplay/ImageDisplay.h>
#include <core/iceconversion/convertice.h>

#include <iostream>

#include "vislearning/classifier/vclassifier/VCSimpleGaussian.h"
#include "vislearning/math/pdf/PDFGaussian.h"

using namespace OBJREC;

using namespace std;

using namespace NICE;

VCSimpleGaussian::VCSimpleGaussian( const Config *conf )
{
}

VCSimpleGaussian::~VCSimpleGaussian()
{
  clear();
}

double VCSimpleGaussian::calcNLogDensity ( int classno, const NICE::Vector & x ) const
{
  std::map<int, PDF *>::const_iterator im = pdfs.find(classno);
  if ( im == pdfs.end() ) {
    fprintf (stderr, "VCSimpleGaussian: classno %d not trained !!\n", classno);
    exit(-1);
  }
  PDF *pdf = im->second;
  double result = pdf->getNLogDensity ( x );

  return result;
}

/** classify using simple vector */

ClassificationResult VCSimpleGaussian::classify ( const NICE::Vector & x ) const
{
  double min_nlogdensity = std::numeric_limits<double>::max();
  int min_class = -1;
  FullVector scores ( maxClassNo + 1 );

  for ( map<int, ice::Statistics *>::const_iterator i  = statistics.begin();
        i != statistics.end();
        i++ )
  {
    double nlogdensity = calcNLogDensity ( i->first, x );
    scores[i->first] = - nlogdensity;
    if ( nlogdensity < min_nlogdensity )
    {
      min_nlogdensity = nlogdensity;
      min_class = i->first;
    }
  }

  return ClassificationResult ( min_class, scores );
}

void VCSimpleGaussian::getVotings ( const NICE::Vector & x,
                                    std::map<int, double> & votings ) const
{
  for ( map<int, ice::Statistics *>::const_iterator i  = statistics.begin();
        i != statistics.end();
        i++ )
  {
    double nlogdensity = calcNLogDensity ( i->first, x );
    votings[ i->first ] = nlogdensity;
  }
}

PDF *VCSimpleGaussian::getPDF(int classno) const
{
  std::map<int, PDF*>::const_iterator im = pdfs.find(classno);
  if ( im == pdfs.end() ) {
    fprintf (stderr, "VCSimpleGaussian: classno %d not trained !!\n", classno);
    exit(-1);
  }
  PDF *pdf = im->second;
  return pdf;
}

/** classify using a simple vector */
void VCSimpleGaussian::teach ( const LabeledSetVector & _teachSet )
{
  maxClassNo = _teachSet.getMaxClassno();
  LOOP_ALL(_teachSet)
  {
    EACH(classno, x);
    teach ( classno, x );
  }
}

void VCSimpleGaussian::teach ( int classno, const NICE::Vector & x )
{
  if ( classno > maxClassNo ) maxClassNo = classno;

  std::map<int, ice::Statistics *>::iterator i = statistics.find(classno);

  if ( i == statistics.end() )
  {
    statistics[classno] = new ice::Statistics(x.size());
    i = statistics.find(classno); // FIXME
  }

  ice::Statistics *s = i->second;

  Put( *s, NICE::makeIceVectorT(x) );
}

void VCSimpleGaussian::finishTeaching()
{
  for ( map<int, ice::Statistics *>::iterator i  = statistics.begin();
        i != statistics.end();
        i++ )
  {
    NICE::Matrix covariance;
    NICE::Vector mean;

    ice::Statistics *s = i->second;

    mean = NICE::makeEVector<double>(Mean(*s));
    covariance = NICE::makeDoubleMatrix( Covariance(*s) );

    PDF *pdf = new PDFGaussian ( covariance, mean );

    pdfs[i->first] = pdf;
  }
}

void VCSimpleGaussian::clear ()
{
  for ( map<int, ice::Statistics *>::iterator i  = statistics.begin();
        i != statistics.end();
        i++ )
  {
    ice::Statistics *s = i->second;
    delete s;
  }

  for ( map<int, PDF *>::iterator i  = pdfs.begin();
        i != pdfs.end();
        i++ )
  {
    PDF *p = i->second;
    delete p;
  }

  pdfs.clear();
  statistics.clear();
}

void VCSimpleGaussian::store ( std::ostream & os, int format ) const
{
  fprintf (stderr, "NOT YET IMPLEMENTED\n");
  exit(-1);
}

void VCSimpleGaussian::restore ( std::istream & is, int format )
{
  fprintf (stderr, "NOT YET IMPLEMENTED\n");
  exit(-1);
}

#endif
