/** 
* @file toyExample.cpp
* @brief just a toy tool
* @author Erik Rodner
* @date 04/07/2009

*/


#include <iomanip>
#include <core/imagedisplay/SimpleSelector.h>
#include <core/image/CrossT.h>

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

#include "vislearning/regression/gpregression/RegGaussianProcess.h" 
#include "vislearning/math/kernels/KernelRBF.h"

#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"
#include "core/image/ImageT.h"
#include "core/imagedisplay/ImageDisplay.h"

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

#ifndef NOVISUAL
void selectTrainingSet ( VVector & train, Vector & labels, NICE::Image & img)
{
    vector<int> colors;
    vector<CoordT<double> > points;
    NICE::selectColoredPoints ( img, points, colors, "Select some points!", 3 );
    
    int k = 0;
    for ( vector<CoordT<double> >::const_iterator i = points.begin();
	    i != points.end(); i++,k++ )
    {
		NICE::Vector feature ( 2 );
		feature[0] = i->x / img.width();
		feature[1] = i->y / img.height();

		train.push_back (  feature );

		if ( colors[k] == 1 ) 
			colors[k] = -1;
		else if ( colors[k] == 2 )
			colors[k] = 1;
		else if ( colors[k] == 3 )
			colors[k] = 0;

		labels.append ( colors[k] );
    }
}
#endif

void markBoundary ( const NICE::Image & imgclassno, NICE::Image & mark )
{
    for ( int y = 0 ; y < imgclassno.height(); y++ )
		for ( int x = 0 ; x < imgclassno.width(); x++ )
		{
			int val = imgclassno.getPixel(x,y);
			bool boundary = false;
			for ( int i = -1 ; (i <= 1) && (!boundary) ; i++ )
			for ( int j = -1 ; (j <= 1) && (!boundary) ; j++ )
			{
				int xn = x + i;
				int yn = y + j;
				if ( (xn<0) || (yn<0) || (xn>=imgclassno.width()) || (yn>=imgclassno.height()) )
				continue;
				int valn = imgclassno.getPixel(xn,yn);
				if ( valn != val )
					boundary = true;
			}
			if ( boundary )
			mark.setPixel(x,y,1);
		}
}

/** 
    just a toy tool 
*/
int main (int argc, char **argv)
{   
#ifndef __clang__
#ifndef __llvm__
    std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
#endif
#endif


    Config conf ( argc, argv );
    conf.store(cout);
 
    int xsize = conf.gI("main", "xsize", 300 );
    int ysize = conf.gI("main", "ysize", 300 );
    
    NICE::Image img (xsize, ysize);
    img.set(255);
	NICE::Image mark (img);
    mark.set(0);

    VVector train;
	Vector labels;

    std::string trainsetcache = conf.gS("main", "trainset", "");
    bool readtrainset = conf.gB("main", "readtrainset", false);
    if ( readtrainset && (trainsetcache.size() > 0 ) )
    {
		ifstream ifs ( trainsetcache.c_str(), ios::in );
		if ( !ifs.good() )
			fthrow(IOException, "Unable to read training data from " << trainsetcache << "." );

		ifs >> labels;
		train.restore ( ifs, VVector::FILEFORMAT_LINE );
		ifs.close ();
		cerr << "Labels: " << labels.size() << " // Examples " << train.size() << endl;
    }
    
	int k = 0;
    for ( VVector::const_iterator i = train.begin();
		i != train.end(); i++,k++ )
    {
		double classno = labels[k];
		const Vector & x = *i;
		Cross cross ( Coord( (int)(x[0]*mark.width()), (int)(x[1]*mark.height()) ), 10 );
		if ( classno < 0 ) 
			mark.draw ( cross, 1 );
		else if ( classno > 0 )
			mark.draw ( cross, 2 );
		else
			mark.draw ( cross, 3 );
    }

	bool selectManually = conf.gB("main", "select", true);
    if ( selectManually )
    {
#ifdef NOVISUAL
		fprintf (stderr, "toyExample: visual manual selection needs ICE visualization\n");
#else
		selectTrainingSet ( train, labels, img );
#endif
    }


    bool writetrainset = conf.gB("main", "writetrainset", false);
    if ( writetrainset && (trainsetcache.size() > 0) )
	{
		ofstream ofs ( trainsetcache.c_str(), ios::out );
		if ( !ofs.good() )
			fthrow(IOException, "Unable to write training data to " << trainsetcache << "." );

		ofs << labels;
		ofs << endl;
		train.store ( ofs, VVector::FILEFORMAT_LINE );
		ofs.close ();
	}

    if ( train.size() <= 0 )
    {
		fthrow(Exception, "Size of the training set is zero!");
    }

	// do something
	KernelRBF kernelFunction ( conf.gD("main", "loggamma", 0.0) ); 
	RegressionAlgorithm *regression = new RegGaussianProcess ( &conf, &kernelFunction );
	cerr << labels << endl;
	regression->teach ( train, labels );

    NICE::FloatImage imgd (img.width(), img.height());
    NICE::Image imgclassno (img);

    for ( int y = 0 ; y < img.height(); y++ )
		for ( int x = 0 ; x < img.width(); x++ )
		{
			NICE::Vector example ( 2 );
			example[0] = x / (double)img.width();
			example[1] = y / (double)img.height();
			
			double value = regression->predict ( example );

			imgd.setPixel(x,y, 1.0 / ( 1.0 + exp(-value) ));

			imgclassno.setPixel(x,y, ( value < 0 ) ? 1 : 2);
		}
	

    markBoundary ( imgclassno, mark );
	Image imgScore ( img.width(), img.height() );
    floatToGrayScaled ( imgd, &imgScore );

#ifndef NOVISUAL
    showImageOverlay ( img, mark );
    showImageOverlay ( imgScore, mark );
    showImageOverlay ( imgclassno, imgclassno );
#endif
    
    return 0;
}
