// FileManager.h - Obsahuje definice funkcí pro ukládání klasifikátorů a trénovacích množin do souboru
// Autor:   Jan Benda
// Datum:   13.03.2021

#include "Filemanager.h"

namespace filemanager
	{
    
    void writeClassifiersXML(cv::FileStorage iFileStorage, Classification::Classifiers iClassifiers, std::string iName)
        {
        iFileStorage << iName << "{";
        iFileStorage << "Bayes" << "{";
        if(!iClassifiers.bayesClassifier.empty())
            {
            if(iClassifiers.bayesClassifier->isTrained())
                {
                iFileStorage << "State" << 1;
                iClassifiers.bayesClassifier->write(iFileStorage);
                }
            else
                iFileStorage << "State" << 0;
            }
        else
            iFileStorage << "State" << 0;
        iFileStorage << "Bayes" << "}";
        iFileStorage << "K-Nearest" << "{";
        if(!iClassifiers.knearestClassifier.empty())
            {
            if(iClassifiers.knearestClassifier->isTrained())
                {
                iFileStorage << "State" << 1;
                iClassifiers.knearestClassifier->write(iFileStorage);
                }
            else
                iFileStorage << "State" << 0;
            }
        else
            iFileStorage << "State" << 0;
        iFileStorage << "K-Nearest" << "}";
        iFileStorage << "SVM" << "{";
        if(!iClassifiers.svmClassifier.empty())
            {
            if(iClassifiers.svmClassifier->isTrained())
                {
                iFileStorage << "State" << 1;
                iClassifiers.svmClassifier->write(iFileStorage);
                }
            else
                iFileStorage << "State" << 0;
            }
        else
            iFileStorage << "State" << 0;
        iFileStorage << "SVM" << "}";
        iFileStorage << "R-Trees" << "{";
        if(!iClassifiers.rtreesClassifier.empty())
            {
            if(iClassifiers.rtreesClassifier->isTrained())
                {
                iFileStorage << "State" << 1;
                iClassifiers.rtreesClassifier->write(iFileStorage);
                }
            else
                iFileStorage << "State" << 0;
            }
        else
            iFileStorage << "State" << 0;
        iFileStorage << "R-Trees" << "}";
        iFileStorage << "ANN_MLP" << "{";
        if(!iClassifiers.annClassifier.empty())
            {
            if(iClassifiers.annClassifier->isTrained())
                {
                iFileStorage << "State" << 1;
                iClassifiers.annClassifier->write(iFileStorage);
                }
            else
                iFileStorage << "State" << 0;
            }
        else
            iFileStorage << "State" << 0;
        iFileStorage << "ANN_MLP" << "}";

        iFileStorage << "Labels" << iClassifiers.iLabels;
        iFileStorage << iName << "}";
        }

    void writeDatasetXML(cv::FileStorage iFileStorage, const std::vector<Dataset::TrainingSample> iTraining, std::string iName)
        {
        iFileStorage << iName << "{";
        iFileStorage << "nSamples" << (int) iTraining.size();
        if(iTraining.size() > 0)
            {
            iFileStorage << "nFeatures" << (int) iTraining.at(0).iFeatureNames.size();
            iFileStorage << "FeatureNames" << iTraining.at(0).iFeatureNames;

            for(int i = 0; i < iTraining.size(); i++)
                {
                iFileStorage << "SampleData" + std::to_string(i) << "{";
                iFileStorage << "SampleName" << iTraining.at(i).iSampleName;
                iFileStorage << "Features" << "{";
                for(size_t j = 0; j < iTraining.at(0).iFeatureNames.size(); j++)
                    iFileStorage << iTraining.at(i).iFeatureNames.at(j) << iTraining.at(i).iFeatureValues.at(j);
                iFileStorage << "}";
                iFileStorage << "Label" << iTraining.at(i).iLabel;
                iFileStorage << "}";
                }
            }
        iFileStorage << "}";
        }

    void writeXML(std::string& iFilename, Classification::Classifiers& iClassifiers_909, Classification::Classifiers& iClassifiers_910, Classification::Classifiers& iClassifiers_911, Classification::Classifiers& iClassifiers_912, Dataset::Dataset& iDataset)
        {
        cv::FileStorage fs;
        fs.open(iFilename, cv::FileStorage::WRITE);

        try
            {
            if(fs.isOpened())
                {
                writeClassifiersXML(fs, iClassifiers_909, "iClassifiers_909");
                writeClassifiersXML(fs, iClassifiers_910, "iClassifiers_910");
                writeClassifiersXML(fs, iClassifiers_911, "iClassifiers_911");
                writeClassifiersXML(fs, iClassifiers_912, "iClassifiers_912");

                writeDatasetXML(fs, iDataset.iTraining9, "Data9");
                writeDatasetXML(fs, iDataset.iTraining10, "Data10");
                writeDatasetXML(fs, iDataset.iTraining11, "Data11");
                writeDatasetXML(fs, iDataset.iTraining12, "Data12");
                }
            fs.release();
            }
        catch(std::exception& e)
            {
            fs.release();
            }

        }

    void readClassifiersXML(Classification::Classifiers& iClassifiers, const std::string iName, cv::FileNode iNode)
        {
        short state = 0;
        bool trained = false;

        if(iClassifiers.bayesClassifier.empty())
            iClassifiers.bayesClassifier = cv::ml::NormalBayesClassifier::create();
        if(iClassifiers.knearestClassifier.empty())
            iClassifiers.knearestClassifier = cv::ml::KNearest::create();
        if(iClassifiers.svmClassifier.empty())
            iClassifiers.svmClassifier = cv::ml::SVM::create();
        if(iClassifiers.rtreesClassifier.empty())
            iClassifiers.rtreesClassifier = cv::ml::RTrees::create();
        if(iClassifiers.annClassifier.empty())
            iClassifiers.annClassifier = cv::ml::ANN_MLP::create();

        cv::FileNode nodecl = iNode[iName];
        cv::FileNode bayes = nodecl["Bayes"];
        cv::FileNode knearest = nodecl["K-Nearest"];
        cv::FileNode svm = nodecl["SVM"];
        cv::FileNode rtrees = nodecl["R-Trees"];
        cv::FileNode ann = nodecl["ANN_MLP"];

        bayes["State"] >> state;
        if(state == 1)
            {
            iClassifiers.bayesClassifier->read(bayes);
            trained = true;
            }
        else
            {
            if(!iClassifiers.bayesClassifier->isTrained())
                {
                iClassifiers.bayesClassifier->clear();
                iClassifiers.bayesClassifier = nullptr;
                }
            }

        state = 0;
        knearest["State"] >> state;
        if(state == 1)
            {
            iClassifiers.knearestClassifier->read(knearest);
            trained = true;
            }
        else
            {
            if(!iClassifiers.knearestClassifier->isTrained())
                {
                iClassifiers.knearestClassifier->clear();
                iClassifiers.knearestClassifier = nullptr;
                }
            }

        state = 0;
        svm["State"] >> state;
        if(state == 1)
            {
            iClassifiers.svmClassifier->read(svm);
            trained = true;
            }
        else
            {
            if(!iClassifiers.svmClassifier->isTrained())
                {
                iClassifiers.svmClassifier->clear();
                iClassifiers.svmClassifier = nullptr;
                }
            }

        state = 0;
        rtrees["State"] >> state;
        if(state == 1)
            {
            iClassifiers.rtreesClassifier->read(rtrees);
            trained = true;
            }
        else
            {
            if(!iClassifiers.rtreesClassifier->isTrained())
                {
                iClassifiers.rtreesClassifier->clear();
                iClassifiers.rtreesClassifier = nullptr;
                }
            }

        state = 0;
        ann["State"] >> state;
        if(state == 1)
            {
            iClassifiers.annClassifier->read(ann);
            trained = true;
            }
        else
            {
            if(!iClassifiers.annClassifier->isTrained())
                {
                iClassifiers.annClassifier->clear();
                iClassifiers.annClassifier = nullptr;
                }
            }

        if(trained)
            nodecl["Labels"] >> iClassifiers.iLabels;

        }

    void readDatasetXML(std::vector<Dataset::TrainingSample>& iTraining, const std::string iName, cv::FileNode iNode)
        {
        cv::FileNode data9 = iNode[iName];

        int nSamples = 0, nFeatures = 0;
        float temp_value = 0.f;
        cv::FileNode sample, features;
        std::vector<std::string> FeatureNames;
        Dataset::TrainingSample temp_sample;

        data9["nSamples"] >> nSamples;
        if(nSamples > 0)
            {
            data9["nFeatures"] >> nFeatures;
            data9["FeatureNames"] >> FeatureNames;

            for(int n = 0; n < nSamples; n++)
                {
                sample = data9["SampleData" + std::to_string(n)];
                sample["SampleName"] >> temp_sample.iSampleName;
                features = sample["Features"];
                for(size_t j = 0; j < nFeatures; j++)
                    {
                    temp_sample.iFeatureNames.push_back(FeatureNames.at(j));
                    features[FeatureNames.at(j)] >> temp_value;
                    temp_sample.iFeatureValues.push_back(temp_value);
                    }
                sample["Label"] >> temp_sample.iLabel;
                bool existing = false;
                for(size_t i = 0; i < iTraining.size() && !existing; i++)
                    if(iTraining.at(i).iSampleName == temp_sample.iSampleName)
                        existing = true;
                if(!existing)
                    iTraining.push_back(temp_sample);
                temp_sample.iFeatureNames.clear();
                temp_sample.iFeatureValues.clear();
                temp_sample.iLabel = "";
                temp_sample.iSampleName = "";
                }

            }
        }

    void readXML(std::string& iFilename, Classification::Classifiers& iClassifiers_909, Classification::Classifiers& iClassifiers_910, Classification::Classifiers& iClassifiers_911, Classification::Classifiers& iClassifiers_912, Dataset::Dataset& iDataset)
        {
        cv::FileStorage fs;

        fs.open(iFilename, cv::FileStorage::READ);
        try
            {
            if(fs.isOpened())
                {
                cv::FileNode node = fs.root();
                readClassifiersXML(iClassifiers_909, "iClassifiers_909", node);
                readClassifiersXML(iClassifiers_910, "iClassifiers_910", node);
                readClassifiersXML(iClassifiers_911, "iClassifiers_911", node);
                readClassifiersXML(iClassifiers_912, "iClassifiers_912", node);

                readDatasetXML(iDataset.iTraining9, "Data9", node);
                readDatasetXML(iDataset.iTraining10, "Data10", node);
                readDatasetXML(iDataset.iTraining11, "Data11", node);
                readDatasetXML(iDataset.iTraining12, "Data12", node);
                }
            fs.release();
            }

        catch(std::exception& e)
            {
            fs.release();
            }

        }
	}