/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
// Monitor includes
#include "Monitor.h"

// Tools includes
#include "Tools.h"
#include "AtomIterator.h"

#include <sstream>

using namespace std;

// -------------------- constructor --------------------
Monitor::Monitor(mml::Monitor* m, MonitoringManager* monitoringManager, Monitor::type type): mappedObject(m) {
    valueType = type;
    startAt = timeParameter2double(m->startAt());
    stopAt = timeParameter2double(m->stopAt());
    index = (int) m->index();
    target = m->target();
    dx = 0;
    dy = 0;
    dz = 0;
    if (m->dx().present()) {
        dx = m->dx().get();
    }
    if (m->dy().present()) {
        dx = m->dy().get();
    }
    if (m->dz().present()) {
        dx = m->dz().get();
    }
    this->monitoringManager = monitoringManager;

    switch (valueType) {
        case Monitor::SCALAR:
            dimension = 1;
            break;
        case Monitor::SCALARSET:
            dimension = 1;
            break;
        case Monitor::VECTORSET:
            dimension = 3;
            break;
        case Monitor::MATRIX_33SET:
            dimension = 9;
            break;
    }

    //add references
    mml::Monitor::reference_sequence& it(m->reference());
    for (mml::Monitor::reference_iterator i(it.begin()); i != it.end(); ++i) {
        references.push_back(new Reference(*i, monitoringManager));
    }

    // how to store monitors data?
    // strategy here is:
    //	- 1) for monitors used to calculate data (during simulation): store indexes and monitored values in vectors, so that insertion (at every single step) is quick (O(1)) (no search needed)
    //	- 2) for monitors created from MMlout: store data in a map between indexes and corresponding values, so that research is quick (O(log(n))) (insertion is done once for all)
    //
    if (mappedObject->indexes().present() && mappedObject->data().present()) {
        // monitor used for output and there are indexes
        std::string ind = mappedObject->indexes().get();
        std::string data = mappedObject->data().get();
        istringstream issInd(ind, istringstream::in);
        istringstream issData(data, istringstream::in);


        while (!issInd.eof() && !issData.eof()) {
            int ind;
            issInd >> ind;
            for (int i = 0; i < dimension; i++) {
                double d;
                issData >> d;
                indexToValuesMap[ind].push_back(d);

            }
        }

    }
    else if (mappedObject->data().present()) {
        // global output monitors, there is no index, data are stored in index 0
        std::string data = mappedObject->data().get();
        istringstream issData(data, istringstream::in);


        while (!issData.eof()) {
            for (int i = 0; i < dimension; i++) {
                double d;
                issData >> d;
                indexToValuesMap[0].push_back(d);
            }
        }
    }

    // we also add data in vectors to use output monitors in "replay" mode
    //TODO this should be somewhat merged with previous
    if (!m->indexes().present()) {
        // mmlIn monitor, no data in target indexes
        AtomIterator iter = AtomIterator(monitoringManager->getInitPml(), target);
        for (iter.begin(); !iter.end(); iter.next()) {
            indexes.push_back(iter.currentAtom()->getIndex());
        }
    }

    // add data if exist
    if (m->data().present()) {
        std::string s = m->data().get();
        istringstream iss(s, istringstream::in);
        while (!iss.eof()) {
            double d;
            iss >> d;
            values.push_back(d);
        }
    }

}

// -------------------- destructor --------------------
Monitor::~Monitor() {
    for (unsigned int i = 0; i < references.size(); i++) {
        delete references[i];
    }
}

// // -------------------- write --------------------
// void Monitor::write(){
//   // write indexes
//   string sInd = "";
//   string sData= "";
//
//   std::map<int,std::vector<double> >::iterator it;
//   for (it=indexToValuesMap.begin();it!=indexToValuesMap.end();it++){
//     ostringstream ssInd;
//     ssInd << it->first;
//     sInd = sInd + ssInd.str() + " ";
//
//     ostringstream ssData;
//     for(int i=0;i<dimension;i++){
//       ssData << it->second[i];
//       sData = sData + ssData.str() + " ";
//     }
//   }
//   mappedObject->indexes(sInd);
//   mappedObject->data(sData);
// }

// -------------------- write --------------------
void Monitor::write() {
    // write indexes
    string s = "";
    for (unsigned int i = 0; i < indexes.size(); i++) {
        ostringstream ss;
        ss << indexes[i];
        s = s + ss.str() + " ";
    }
    mappedObject->indexes(s);

    // write data
    s = "";
    for (unsigned int i = 0; i < values.size(); i++) {
        ostringstream ss;
        ss << values[i];
        s = s + ss.str() + " ";
    }
    mappedObject->data(s);
}

// -------------------- getReferenceName --------------------
std::string Monitor::getReferenceName() {
    std::string s = "";
    for (unsigned int i = 0; i < references.size(); i++) {
        s += references[i]->toString();
    }
    return s;
}

// -------------------- getIndex --------------------
int Monitor::getIndex() {
    return index;
}


// -------------------- getStartAt --------------------
double Monitor::getStartAt() {
    return startAt;
}

// -------------------- getStopAt --------------------
double Monitor::getStopAt() {
    return stopAt;
}

// -------------------- getTargetName --------------------
std::string Monitor::getTargetName() {
    return target;
}

// -------------------- getValueType --------------------
Monitor::type Monitor::getValueType() {
    return valueType;
}

// -------------------- getValuesOfIndex --------------------
bool Monitor::getValuesOfIndex(int i, double values[]) {
    std::map<int, std::vector<double> >::iterator it;
    it = indexToValuesMap.find(i);
    if (it != indexToValuesMap.end()) {
        for (int j = 0; j < dimension; j++) {
            values[j] = it->second[j];
        }
        return true;
    }
    return false;
}

// -------------------- getIndex --------------------
int Monitor::getIndexOfValues(const unsigned int i) {
    if (i < indexes.size()) {
        return indexes[i];
    }
    else {
        return -1;
    }
}

// -------------------- getValue --------------------
double Monitor::getValue(const unsigned int i) {
    if (i < values.size()) {
        return values[i];
    }
    else {
        return 0.0;
    }
}

// -------------------- getNumberOfIndex --------------------
unsigned int Monitor::getNumberOfIndex() {
    if (indexes.size() > 0) {
        return indexes.size();
    }
    else {
        return indexToValuesMap.size();
    }
}

// -------------------- getNumberOfValues --------------------
unsigned int Monitor::getNumberOfValues() {
    return values.size();
}
