/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001 Topi Mäenpää and Jaakko Viertola
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.11 $
 *********************************************************************/

#ifndef _SAMPLEUTILS_H
#define _SAMPLEUTILS_H


#include <List.h>
#include <Exception.h>
#include <Matrix.h>
#include <strstream>
#include <fstream>
#include <io/File.h>

#include "Sample.h"
#include "Image.h"
#include "FeatureExtractor.h"

namespace prapi
{
	/**
	 * The class SampleUtils includes fuction for handling and making sample sets.
	 **/
	class SampleUtils
	{
	public:
		/**
		 * Reads a sample list form a file.
		 **/
		template <class T,class I,class C> static void readFromFile(std::string file,util::List<Sample<T,I,C> >& sampleList)
			throw (io::IOException&);
		/**
		 * Write a sample list to a file.
		 **/
		template <class T,class I,class C> static void writeToFile(std::string file,const util::List<Sample<T,I,C> >& sampleList)
			throw (io::IOException&);

		/**
		 * Combines the set to the one set.
		 *
		 * @param combinedSet The set which includes all featureVectors.
		 * @param sets The List of sample sets which are wanted to combine.
		 **/
		template <class T,class I,class C> static void combineSets(util::List<Sample<util::List<T>,I,C> >& combinedSet,
																															 const util::List<util::List<Sample<T,I,C> >* >& sets)
			throw (util::InvalidArgumentException&);

		/**
		 * This fuction makes samples from the Matrix mat. In the mask
		 * matrix there are every class marked at the specified
		 * color/grayscale. The funtion works like this first it takes the
		 * the second number from the list values (second therefore that
		 * the first place in the list is background) and then finds the
		 * same areas from the mask matrix and cuts pieces out from the
		 * Matrix mat as many as it can and then it calculates the
		 * featureVector with the extractor and the sets it to the new
		 * sample and then sets the identifier from the names list to the
		 * sample. The class given to the sample will be the same as the
		 * index in the values list.
		 *
		 * Note: If values contain value -1 it means that the class will
		 * be skipped (samples which are in that area wont be made at all.
		 *
		 * @param mat The original matrix.
		 * @param mask The matrix which includes the painted classes.
		 * @param values Contain the information of color/crayscale values.
		 * @param extractor The FeatureExtractor which calculates the featureVectors.
		 * @param width The width of sample "matrix".
		 * @param height The height of sample "matrix".
		 * @param matrixName The name of matrix (will be placed for every sample).
		 **/
		template <class T,class U>
		static util::List<Sample<T,std::string,int> > makeSamples(const util::Matrix<T>& mat,const util::Matrix<U>& mask,
																															const util::List<U>& values, FeatureExtractor<int,util::Matrix<T> >& extractor,
																															int width,int height,std::string matrixName);
		/**
		 * The fuction reads the Inspection Development System (IDS)
		 * TIP_SAMPLE_SET and TIP_DSAMPLE_SET files and converts them to
		 * the format this library undestands.
		 *
		 * @param sampleSet The IDS sampleSet file.
		 **/
		static util::List<Sample<int,ImageSampleIdentifier,int> > readIdsSampleSet(std::string sampleSet)
			throw (util::io::IOException&)
		{
			util::List<std::string> classes;
			util::List<std::string> features;
			return SampleUtils::readIdsSampleSet(sampleSet,classes,features);
		}

		/**
		 * The fuction reads the Inspection Development System (IDS)
		 * TIP_SAMPLE_SET and TIP_DSAMPLE_SET files and converts them to
		 * the format this library undestands.
		 *
		 * @param sampleSet The IDS sampleSet file.
		 * @param classes The name of classes.
		 * @param features The name of features.
		 **/
		static util::List<Sample<int,ImageSampleIdentifier,int> > readIdsSampleSet(std::string sampleSet,util::List<std::string>& classes,
																																							 util::List<std::string>& features)
			throw (util::io::IOException&);

		/**
		 * Create a new sample set out of <i>samples</i> by removing all
		 * features whose index is not listed in <i>enabledFeatures</i>.
		 *
		 * @param samples the sample set to be modified
		 * @param enabledFeatures the indices of the enabled features. 
		 * Make sure these indices are smaller than the number of features
		 * in each sample.
		 * @return a pruned sample set with disabled features removed
		 **/
		template <class T,class I,class C> static void prune(util::List<Sample<T,I,C> >& samples,
																												 const util::List<int>& enabledFeatures);


		/**
		 * Returns the maximum true class index in <i>samples</i> plus
		 * one.
		 **/
		template <class T,class I,class C> static int countClasses(const util::List<Sample<T,I,C> >& samples);
	};

	template <class T,class I,class C> int SampleUtils::countClasses(const util::List<Sample<T,I,C> >& samples)
	{
		int maxIndex = -1;
		for (int i=samples.getLength(); i--; )
			if ((int)samples[i].getTrueClass() > maxIndex)
				maxIndex = (int)samples[i].getTrueClass();
		return maxIndex + 1;
	}

	template <class T,class I,class C> void SampleUtils::prune(util::List<Sample<T,I,C> >& samples,
																														 const util::List<int>& enabledFeatures)
	{
		using namespace util;

		for (int i=0; i<samples.getLength(); i++)
			{
				List<T> newFeatures(enabledFeatures.getLength());
				for (int t=0; t<enabledFeatures.getLength(); t++)
					newFeatures += samples[i].featureVector()[enabledFeatures[t]];
				samples[i].featureVector() = newFeatures;
			}
	}

	template <class T,class I,class C> void SampleUtils::readFromFile(std::string file,util::List<Sample<T,I,C> >& sampleList)
		throw (io::IOException&)
	{
		std::ifstream in(file.c_str());
		if(!in)
			throw io::IOException("SampleUtils::readFromFile(std::string, util::List<Sample<T,I,C> >&): Can't open file "+file);

		off_t size = io::File::getSize(file);
		char bfr[size];
		in.read(bfr,size);
		std::istrstream stream(bfr,size);
		in.close();
		
		stream >> sampleList;
	}
	
	template <class T,class I,class C> static void writeToFile(std::string file,const util::List<Sample<T,I,C> >& sampleList)
		throw (io::IOException&)
	{
		std::ofstream out(file.c_str());
		if(!out)
			throw io::IOException("SampleUtils::writeToFile(std::string, const util::List<Sample<T,I,C> >&): Can't open file "+file);
		out << sampleList;
		out.close();
	}

	
	template <class T,class U>
	util::List<Sample<T,std::string,int> > SampleUtils::makeSamples(const util::Matrix<T>& mat,const util::Matrix<U>& mask,
																																	const util::List<U>& values, FeatureExtractor<int,util::Matrix<T> >& extractor,
																																	int width,int height,std::string matrixName)
	{
		util::List<Sample<T,std::string,int> > result;
		int len = values.getLength();
				
		// go through all classes
		for(int i=1;i<values.getLength();i++)
			{ // make sure that it's not background
				if(values[i] != -1)
					{
						util::List<std::string> coord;
						util::List<util::Matrix<T> > splitMat(Image::split(mat,mask,width,height,coord,values[i]));
						for(int index=0;index<splitMat.getLength();index++)
							{	
								std::string identifier(matrixName+" "+coord[index]);
								util::List<T> lst(extractor.getFeatureVector(splitMat[index]));
								Sample<T,std::string,int> sample(lst,i);
								sample.setIdentifier(identifier);
								result += sample;
							}
					}
			}
		return result;
	}

	template <class T,class I,class C> void SampleUtils::combineSets(util::List<Sample<util::List<T>,I,C> >& combinedSet,
																																	 const util::List<util::List<Sample<T,I,C> >* >& sets)
		throw (util::InvalidArgumentException&)
	{
		// then adding the features to the same list
		int length=sets[0]->getLength();
		int featureCount=sets.getLength();
		for(int i=1;i<featureCount;i++)
			if(length != sets[i]->getLength())
				throw util::InvalidArgumentException("SampleUtils::combineSets(util::List<Sample<util::List<T>,I,C> >&, util::List<util::List<Sample<T,I,C> >* >&) : Length of sets differ.");

		combinedSet.setLength(0);
		combinedSet.setCapacity(length);
		
		for(int i=0;i<length;i++)
		{
			// make the new sample
			Sample<util::List<T>,I,C> sample(sets[0]->elementAt(i).getTrueClass());
			sample.setIdentifier(sets[0]->elementAt(i).getIdentifier());
			sample.featureVector().setLength(featureCount);
			// add features
			for (int j=0;j<featureCount;j++) sample.featureVector()[j]=sets[j]->elementAt(i).getFeatureVector();
			
			combinedSet += sample;
		}
	}
}
#endif
