/** 
* @file KMeansMatlab.cpp
* @brief K-Means
* @author Erik Rodner
* @date 02/04/2008

*/
#include <iostream>

#include "vislearning/math/cluster/KMeansMatlab.h"
#include <set>

using namespace OBJREC;

using namespace std;
// refactor-nice.pl: check this substitution
// old: using namespace ice;
using namespace NICE;

#undef DEBUG_KMeansMatlab

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

KMeansMatlab::KMeansMatlab() : ClusterAlgorithm()
{
  this->noClusters    = 20;
  
  this->kmeansDir     = "/home/rodner/osl/labor/cvs/osl/kernel/";
  this->inputFN       = "/tmp/KMeansMatlab.input";
  this->outputFN      = "/tmp/KMeansMatlab.output" ;

  this->matlabExec    = "matlab";
  this->matlabArgs    = "-nosplash -nojvm -nodesktop";   
}

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

KMeansMatlab::~KMeansMatlab()
{
  if ( matlabPipe != NULL )
    pclose (matlabPipe);
}

void KMeansMatlab::initFromConfig( const NICE::Config* _conf, const std::string& _confSection )
{
  this->noClusters   = _conf->gI( _confSection, "noClusters", 20);
  
  this->kmeansDir    = _conf->gS(_confSection, "source_root", "/home/rodner/osl/labor/cvs/osl/") + "/kernel/";
  this->inputFN      = _conf->gS(_confSection, "tmpInput", "/tmp/KMeansMatlab.input" );
  this->outputFN     = _conf->gS(_confSection, "tmpOutput", "/tmp/KMeansMatlab.output" );

  this->matlabExec   = _conf->gS(_confSection, "matlab_exec", "matlab");
  this->matlabArgs   = _conf->gS(_confSection, "matlab_args", "-nosplash -nojvm -nodesktop"); 
}

///////////////////// ///////////////////// /////////////////////
//                      CLUSTERING STUFF
///////////////////// ///////////////////// ////////////////// 


int KMeansMatlab::compute_prototypes ( const VVector & features,
				  VVector & prototypes,
				  std::vector<double> & weights,
				  const std::vector<int>    & assignment )
{
    int j = 0;
    // fprintf (stderr, "KMeansMatlab::compute_prototypes: init noClusters=%d\n", noClusters);
    for ( int k = 0 ; k < noClusters ; k++ )
    {
	prototypes[k].set(0);
	weights[k] = 0;
    }

    // fprintf (stderr, "KMeansMatlab::compute_prototypes: compute means\n");
    for ( VVector::const_iterator i  = features.begin();
                                              i != features.end();
	                                      i++, j++ )
    {
	int k = assignment[j];
	// refactor-nice.pl: check this substitution
	// old: Vector & p = prototypes[k];
	NICE::Vector & p = prototypes[k];
	// refactor-nice.pl: check this substitution
	// old: const Vector & x = *i;
	const NICE::Vector & x = *i;

#ifdef DEBUG_KMeansMatlab
	// refactor-nice.pl: check this substitution
	// old: fprintf (stderr, "KMeansMatlab::compute_prototypes: vector %d has assignment %d\n", j, k );
	fprintf (stderr, "KMeansMatlab::compute_prototypes: std::vector %d has assignment %d\n", j, k );
#endif

	p += x;

#ifdef DEBUG_KMeansMatlab
	cerr << "vector was : " << x << endl;
	cerr << "prototype for this class is now : " << p << endl;
#endif
	weights[k]++;
    }

    // fprintf (stderr, "KMeansMatlab::compute_prototypes: scaling\n");
    for ( int k = 0 ; k < noClusters ; k++ )
    {
	// refactor-nice.pl: check this substitution
	// old: Vector & p = prototypes[k];
	NICE::Vector & p = prototypes[k];

#ifdef DEBUG_KMeansMatlab
	cerr << "prototype for this class before scaling : " << p << endl;
#endif

	if ( weights[k] <= 0 ) {
	    return -1;	    
	}

	p *= ( 1.0 / weights[k] );
	
	weights[k] = weights[k] / features.size();

#ifdef DEBUG_KMeansMatlab
	cerr << "prototype for this class after scaling with " << weights[k] << " : " << p << endl;
#endif
    }

    return 0;
}



void KMeansMatlab::cluster ( const VVector & features,
		       VVector & prototypes,
		       std::vector<double> & weights,
		       std::vector<int>    & assignment )
{
    prototypes.clear();
    weights.clear();
    assignment.clear ();
    weights.resize ( noClusters, 0 );
    assignment.resize ( features.size(), 0 );

    // ----------- routine argument -------------
    // refactor-nice.pl: check this substitution
    // old: string routineCMD = "W = KMeansInterface('" + inputFN + "', '" + outputFN + "');\n";
    std::string routineCMD = "W = KMeansInterface('" + inputFN + "', '" + outputFN + "');\n";

    int dimension;

    if ( (int)features.size() >= noClusters )
	dimension = features[0].size();
    else {
	fprintf (stderr, "FATAL ERROR: Not enough feature vectors provided for KMeansMatlab\n");
	exit(-1);
    }

    FILE *fi = fopen ( inputFN.c_str(), "w" );
    if ( fi == NULL )
    {
	fprintf (stderr, "KMeansMatlab: FATAL ERROR cannot write features!\n");
	exit(-1);
    }
    fwrite (&noClusters, sizeof(int), 1, fi );
    int n = features.size();
    fwrite (&n, sizeof(int), 1, fi );
    int d = features[0].size();
    fwrite (&d, sizeof(int), 1, fi );
    for ( size_t i = 0 ; i < features.size() ; i++ )
	for ( size_t k = 0 ; k < features[i].size() ; k++ )
	    // refactor-nice.pl: check this substitution
	    fwrite ( &(features[i][k]), sizeof(double), 1, fi);

    fclose(fi);

    // refactor-nice.pl: check this substitution
    // old: string chdirCMD = "cd '" + kmeansDir + "'\n";
    std::string chdirCMD = "cd '" + kmeansDir + "'\n";
    // refactor-nice.pl: check this substitution
    // old: string execCMD = matlabExec + " " + matlabArgs;
    std::string execCMD = matlabExec + " " + matlabArgs;

    matlabPipe = popen ( execCMD.c_str(), "w");
    if ( matlabPipe == NULL )
    {
	fprintf (stderr, "KMeansMatlab: FATAL ERROR cannot execute matlab!\n");
	exit(-1);
    }
    fputs (chdirCMD.c_str(), matlabPipe);
    fputs (routineCMD.c_str(), matlabPipe);
    pclose ( matlabPipe );

    FILE *g = fopen ( outputFN.c_str(), "r" );
    if ( g == NULL )
    {
	fprintf (stderr, "KMeansMatlab::teach: FATAL ERROR cannot read matlab result!\n");
	exit(-1);
    }

    for ( size_t j = 0 ; j < features.size() ; j++ )
    {
	int val = 0;
	if ( fread ( &val, sizeof(int), 1, g) <= 0 )
	{
	    // refactor-nice.pl: check this substitution
	    // old: fprintf (stderr, "KMeansMatlab::cluster: FATAL ERROR reading vector file\n");
	    fprintf (stderr, "KMeansMatlab::cluster: FATAL ERROR reading std::vector file\n");
	    exit(-1);
	}
	assignment[j] = val-1;
    }
    fclose(g);


    for ( int k = 0 ; k < noClusters ; k++ )
    {
	// fprintf (stderr, "KMeansMatlab::cluster prototype init constructor\n");
	prototypes.push_back( Vector( dimension ) );
	prototypes[k].set(0);
    }

    if ( compute_prototypes ( features, prototypes, weights, assignment ) < 0 )
    {
	fprintf (stderr, "KMeansMatlab::cluster failure !!\n");
	exit(-1);
    }
}

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

void KMeansMatlab::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, "KMeansMatlab" ) )
    {
      std::cerr << " WARNING - attempt to restore KMeansMatlab, 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, "KMeansMatlab" ) )
      {
        b_endOfBlock = true;
        continue;
      }      
      
      tmp = this->removeStartTag ( tmp );   
      
      if ( tmp.compare("noClusters") == 0 )
      {
        is >> this->noClusters;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("kmeansDir") == 0 )
      {
        is >> this->kmeansDir;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }      
      else if ( tmp.compare("inputFN") == 0 )
      {
        is >> this->inputFN;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("outputFN") == 0 )
      {
        is >> this->outputFN;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }
      else if ( tmp.compare("matlabExec") == 0 )
      {
        is >> this->matlabExec;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }      
      else if ( tmp.compare("matlabArgs") == 0 )
      {
        is >> this->matlabArgs;
        is >> tmp; // end of block 
        tmp = this->removeEndTag ( tmp );
      }      
      else
      {
      std::cerr << "WARNING -- unexpected KMeansMatlab object -- " << tmp << " -- for restoration... aborting" << std::endl;
      throw;
      }
    }
  }
  else
  {
    std::cerr << "KMeansMatlab::restore -- InStream not initialized - restoring not possible!" << std::endl;
    throw;
  }
}

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

    os << this->createStartTag( "kmeansDir" ) << std::endl;
    os << this->kmeansDir << std::endl;
    os << this->createEndTag( "kmeansDir" ) << std::endl;
    
    os << this->createStartTag( "inputFN" ) << std::endl;
    os << this->inputFN << std::endl;
    os << this->createEndTag( "inputFN" ) << std::endl;  

    os << this->createStartTag( "outputFN" ) << std::endl;
    os << this->outputFN << std::endl;
    os << this->createEndTag( "outputFN" ) << std::endl;  

    os << this->createStartTag( "matlabExec" ) << std::endl;
    os << this->matlabExec << std::endl;
    os << this->createEndTag( "matlabExec" ) << std::endl;
    
    os << this->createStartTag( "matlabArgs" ) << std::endl;
    os << this->matlabArgs << std::endl;
    os << this->createEndTag( "matlabArgs" ) << std::endl;  
    
    // done
    os << this->createEndTag( "KMeansMatlab" ) << std::endl;    
  }
  else
  {
    std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  }
}

void KMeansMatlab::clear ()
{ 
}