#ifndef SVMClassifier_H
#define SVMClassifier_H

#include "Geometry.hpp"
#include "Mesh.hpp"

#include "CellCluster.hpp"

#include <libsvm/svm.h>

using namespace std;

namespace mgx {
#define Malloc(type,n) (type *)malloc((n)*sizeof(type))


  struct FileData {

    // ordered vector of measures, created when a training file is read
    std::vector<QString> measures;

    // main data array, each vector has the measure value in the same order as the measure vector above
    std::vector<std::vector<double> > data;

    // parent labels in order
    std::vector<int> dataLabels;

    // set of parent labels
    std::set<int> labels;

    // number of cells
    int size;

  };


  // class to deal with the txt files needed by libSVM
  class FileHandlerSVM{

    public:

    // read a training file and create a FileData structure
    bool readTrainingFile(FileData& fd, QString filename);

    // balance the different classes in number by duplicating random entries of underrepresented data
    bool balanceTrainingFile(FileData& fd);

    bool augmentTrainingFile(FileData& fd, int repeats, double noiseFactor);

    void shuffleTrainingFile(FileData& fd);

    void limitTrainingFile(FileData& fd, int maxEntries);

    bool createFileData(FileData& fd, std::set<int>& allLabels, QStringList& fileFeatures, CellCluster& cc, IntFloatAttr& parentLabels);

    // write a training file from a FileData structure
    bool writeTrainingFile(FileData& fd, QString filename, bool append);

    

  };


class SVMClassifier
  {
    public:
    // lib svm objects
    svm_model *model;

    svm_node *x_space;
    svm_parameter param;     
    svm_problem prob;

    std::set<QString> modelFeatures;
    //std::vector<std::set<QString> > featureVectorOneOut;

    int nr_fold;
    double featureThreshold;

    int optGridSize;

    FileData allData;

    //SVMClassifier();

    // create subset of the input set where one feature is deleted in each subset
    std::vector<std::set<QString> > featureSubsetCreation(std::set<QString> allFeatures);
    // enlarge an existing set by one new feature
    std::vector<std::set<QString> > featureEnlargedSetCreation(std::set<QString> allCurrentFeatures, std::set<QString> allFeatures);

    // feature reduction
    std::set<QString> featureReduction(std::set<QString> allFeatures, bool fullParameterSearch);


    void attemptFeatureAdd(std::set<QString> currentFeatureSet, std::set<QString> allFeatures, bool fullParameterSearch,
    	double& bestCV, std::set<QString>& bestFeatureSet);

    void attemptFeatureRem(std::set<QString> currentFeatureSet, std::set<QString> allFeatures, bool fullParameterSearch,
    	double& bestCV, std::set<QString>& bestFeatureSet);

    std::set<QString> featureInclusion(std::set<QString> allFeatures, bool fullParameterSearch);

    std::set<QString> geneticFeatureSelection(std::set<QString> allFeatures);

    // initialize parms
    bool initialize(QString svmType, QString kernelType, double pGamma, double pC, double featureTh, int kFold);

    // 
    double optimizeSVMParameter(svm_problem& svmProb, svm_parameter& svmParam);
    double optimizeSVMParameter(svm_problem& svmProb, svm_parameter& svmParam, int gridSize, double cStart, double cStop,
    	double gammaStart, double gammaStop);

    double doCrossValidation(svm_problem& svmProb, svm_parameter& svmParam, int nrFold);

    void freeMemory();

    // generate required libSVM structures from a FileData structure
    bool generateDataSVM(FileData& fd, svm_problem& svmProb, std::set<QString>& fileFeatures, svm_node *x_spaceProb);

  };

}

#endif