/** 
* @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

KMeansMatlab::KMeansMatlab( const Config *conf, 
			    int _noClasses ) : noClasses(_noClasses)
{
    kmeansDir = conf->gS("matlab", "source_root", "/home/rodner/osl/labor/cvs/osl/") + "/kernel/";
    inputFN   = conf->gS("KMeansMatlab", "tmpInput", "/tmp/KMeansMatlab.input" );
    outputFN   = conf->gS("KMeansMatlab", "tmpoutput", "/tmp/KMeansMatlab.output" );

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

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


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 noClasses=%d\n", noClasses);
    for ( int k = 0 ; k < noClasses ; 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 < noClasses ; 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 ( noClasses, 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() >= noClasses )
	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 (&noClasses, 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 < noClasses ; 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);
    }
}