/** 
* @file calcNormTrainingSet.cpp
* @brief save normalized object images
* @author Erik Rodner
* @date 07/21/2008

*/
#ifdef WIN32
#ifdef NICE_USELIB_BOOST
	#include "boost/filesystem.hpp"
#endif
#endif

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

#include <core/image/Convert.h>

#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <core/basics/Config.h>
#include <vislearning/baselib/cmdline.h>
#include <vislearning/baselib/Preprocess.h>

#include <vislearning/cbaselib/MultiDataset.h>

#include <vislearning/baselib/ProgressBar.h>
#include <vislearning/baselib/Globals.h>

using namespace OBJREC;
 



using namespace NICE;
using namespace std;


/** 
    
    save normalized object images 
    
*/
int main (int argc, char **argv)
{   
#ifndef __clang__
#ifndef __llvm__
    std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
#endif
#endif

    char configfile [300];
    char objectclass_c [1024];
    char setname_c [1024];
    bool scaleToAVG = false;

    struct CmdLineOption options[] = {
	{"config", "use config file", NULL, "%s", configfile},
	{"ds", "use data set", "train", "%s", setname_c},
	{"class", "create pictures of", "", "%s", objectclass_c},
	{"scale", "scale pictures to average sizes", NULL, NULL, &scaleToAVG},
	{NULL, NULL, NULL, NULL, NULL} 
    };
    int ret;
    char *more_options[argc];
    ret = parse_arguments( argc, (const char**)argv, options, more_options);
    fprintf (stderr, "data set name: %s\n", setname_c );

    if ( ret != 0 )
    {
	if ( ret != 1 ) fprintf (stderr, "Error parsing command line !\n");
	exit (-1);
    }

    Config conf ( configfile );
    Preprocess::Init ( &conf );
    
    MultiDataset md ( &conf );

    // refactor-nice.pl: check this substitution
    // old: string setname ( setname_c );
    std::string setname ( setname_c );
    const LabeledSet & ls = *(md[setname]);

    map<int, double> maxwidth;
    map<int, double> maxheight;
    map<int, double> minwidth;
    map<int, double> minheight;
    map<int, double> avgheight;
    map<int, double> avgwidth;
    map<int, int> count;

    const ClassNames & classNames = md.getClassNames( setname );
    int objectclassno;
    
    // refactor-nice.pl: check this substitution
    // old: string objectclass ( objectclass_c );
    std::string objectclass ( objectclass_c );
    if ( objectclass.size() > 0 ) 
    {
	cerr << "Object class " << objectclass << endl;

	objectclassno = classNames.classno(objectclass);
	if ( objectclassno < 0 ) {
	    fprintf (stderr, "Unknown object class %s\n", objectclass_c );
	    exit(-1);
	}
    } else {
	objectclassno = -1;
    }

    ProgressBar pb ("Statistics");

    pb.show();

    LOOP_ALL_S(ls)
    {
	EACH_INFO(classno,info);
	pb.update ( ls.count() );
    
	fprintf (stderr, "Filename %s\n", info.img().c_str());
	if ( ! info.hasLocalizationInfo() ) {
	    fprintf (stderr, "No localization information available !!\n");
	    exit(-1);
	}
	const LocalizationResult *l = info.localization();

	if ( l->size() <= 0 ) {
	    fprintf (stderr, "No objects found in this image !!\n");
	    exit(-1);
	}
	fprintf (stderr, "Analyzing bounding boxes\n");
	for ( LocalizationResult::const_iterator i = l->begin();
	      i != l->end(); i++ )
	{
	    SingleLocalizationResult *slr = *i;
	    fprintf (stderr, "checking classno\n");
	    assert ( slr->r != NULL );
	    int c = slr->r->classno;
	    if ( (objectclassno < 0 ) || (c == objectclassno) ) 
	    {
		fprintf (stderr, "getting bounding box\n");
		int xi, xa, yi, ya;
		slr->getBoundingBox ( xi, yi, xa, ya );

		if ( !NICE::isFinite(xi) || !NICE::isFinite(yi) || !NICE::isFinite(xa) || !NICE::isFinite(ya) )
		{
		    fprintf (stderr, "illegal bounding box information: %s\n", info.img().c_str() );
		    exit(-1);
		}

		double width = xa - xi;
		double height = ya - yi;

		if ( width <= 0 ) {
		    fprintf (stderr, "negative width: %s !\n", info.img().c_str());
		    exit(-1);
		}
		if ( height <= 0 ) {
		    fprintf (stderr, "negative height %s !\n", info.img().c_str());
		    exit(-1);
		}

		if ( (minwidth.find(c) == minwidth.end()) || (minwidth[c] > width ) )
		    minwidth[c] = width;
		if ( (maxwidth.find(c) == maxwidth.end()) || (maxwidth[c] > width ) )
		    maxwidth[c] = width;

		if ( (minheight.find(c) == minheight.end()) || (minheight[c] > height ) )
		    minheight[c] = height;
		if ( (maxheight.find(c) == maxheight.end()) || (maxheight[c] > height ) )
		    maxheight[c] = height;

		if ( avgheight.find(c) == avgheight.end() )
		    avgheight[c] = height;
		else
		    avgheight[c] += height;

		if ( avgwidth.find(c) == avgwidth.end() )
		    avgwidth[c] = width;
		else
		    avgwidth[c] += width;

		if ( count.find(c) == count.end() )
		    count[c] = 0;
		else
		    count[c] ++;


	    }
	    fprintf (stderr, "ready for the next file\n");
	}
    }

    if ( (objectclassno >= 0) && (count.find(objectclassno) == count.end() ) )
    {
	fprintf (stderr, "NO examples of class %s found !!\n", objectclass.c_str());
	exit(-1);
    }

	
    fprintf (stderr, "-- Object Statistics --\n");
    for ( map<int, int>::iterator i = count.begin();
				 i != count.end();
				 i++ )
    {
	int c = i->first;
	int count = i->second;

	avgheight[c] /= count;
	avgwidth[c] /= count;

	// refactor-nice.pl: check this substitution
	// old: string dir = classNames.text(c);
	std::string dir = classNames.text(c);

#ifdef WIN32
#ifdef NICE_USELIB_BOOST
	boost::filesystem::path t_path(dir.c_str());
	int retcode = boost::filesystem::create_directory(t_path);
#else
		 fthrow(Exception,"mkdir function not defined on system. try using boost lib and rebuild library");
#endif
#else
	int retcode = mkdir ( dir.c_str(), 0700 );
#endif
	if ( (retcode < 0) && (retcode != EEXIST ) ) {
	    fprintf (stderr, "Failed to create directory: %s\n", dir.c_str() );
	    exit(-1);
	}

	fprintf (stderr, "[%s]\n", classNames.text(c).c_str() ); 
	fprintf (stderr, "width: min %f max %f avg %f\n", minwidth[c], maxwidth[c], avgwidth[c] );
	fprintf (stderr, "height: min %f max %f avg %f\n", minheight[c], maxheight[c], avgheight[c] );
    }


    pb.reset("Crop");


    double borderx = conf.gD("crop", "borderx", 0.2);
    double bordery = conf.gD("crop", "bordery", 0.2);
    
    int counter = 0;
    
    LOOP_ALL_S(ls)
    {
	EACH_INFO(classno,info);
	pb.update ( ls.count() );
	// refactor-nice.pl: check this substitution
	// old: string filename = info.img();
	std::string filename = info.img();

	if ( ! info.hasLocalizationInfo() ) {
	    fprintf (stderr, "createNormTrainingSet: file %s has no localization information\n", 
		filename.c_str() );
	    exit(-1);
	}

	// refactor-nice.pl: check this substitution
	// old: ImageRGB img = Preprocess::ReadImgAdvRGB ( filename );
	NICE::ColorImage img = Preprocess::ReadImgAdvRGB ( filename );
	Globals::setCurrentImgFN ( filename );

	const LocalizationResult *l = info.localization();
    
    	for ( LocalizationResult::const_iterator i = l->begin();
	      i != l->end(); i++ )
	{
	    SingleLocalizationResult *slr = *i;
	    int c = slr->r->classno;
	    if ( (objectclassno < 0) || (c == objectclassno) ) 
	    {
		int xi, xa, yi, ya;
		slr->getBoundingBox ( xi, yi, xa, ya );
		double w = xa - xi;
		double h = ya - yi;

		if ( (w < 1) || (h < 1) ) {
		    fprintf (stderr, "Illegal width or height: %s\n", filename.c_str() );
		    exit(-1);
		}


		double dstwidth;
		double dstheight;

		if ( scaleToAVG )
		{
		    double normwidth  = avgwidth[c]*(1.0+borderx);
		    double normheight = avgheight[c]*(1.0+bordery);

		    dstwidth = normwidth;
		    dstheight = normheight;
		} else {
		    dstwidth = w;
		    dstheight = h;
		}


		double bxi = xi - borderx / 2.0;
		double bxa = xa + borderx / 2.0;
		double byi = yi - bordery / 2.0;
		double bya = ya + bordery / 2.0;

		if ( bxi < 0.0 ) bxi = 0.0;
		if ( byi < 0.0 ) byi = 0.0;
		// refactor-nice.pl: check this substitution
		// old: if ( bxa > img.xsize() - 1 ) bxa = img.xsize() - 1;
		if ( bxa > img.width() - 1 ) bxa = img.width() - 1;
		// refactor-nice.pl: check this substitution
		// old: if ( bya > img.ysize() - 1 ) bya = img.ysize() - 1;
		if ( bya > img.height() - 1 ) bya = img.height() - 1;


	        RectT<int> rect ( bxi, byi,
                      bxa - bxi + 1, bya - byi + 1 );
                NICE::ColorImage *subImage = img.createSubImage ( rect );

		NICE::ColorImage dst ( (int)round(dstwidth), (int)round(dstheight) ); 
		scale ( *subImage, &dst );
#ifndef NOVISUAL
		showImage(dst);
#endif
		std::string dir = classNames.text(c);

		char imgfilename_s [1024];
		sprintf ( imgfilename_s, "%s/image_%06d.jpg", dir.c_str(), counter );

		// refactor-nice.pl: check this substitution
		// old: fprintf (stderr, "%s: %d x %d\n", imgfilename_s, dst.xsize(), dst.ysize() );
		fprintf (stderr, "%s: %d x %d\n", imgfilename_s, dst.width(), dst.height() );
		ImageFile imgf ( imgfilename_s );
		try {
		    imgf.writer ( &dst );
		} catch ( Exception ) {
		    fprintf (stderr, "Failed to write filename %s\n", imgfilename_s );
		    exit(-1);
		}

		counter++;
	    }
	}
    }
   
    return 0;
}
