/*****************************************************************************
 * $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$
 ****************************************************************************/

#ifndef REFRENCE_REFERENCE_H
#define REFRENCE_REFERENCE_H

#include <string>
// Monitor includes
#include "MonitoringManager.h"

class Monitor;

/**
 * @ingroup group_cepmodeling_libraries_mml
 *
 * @brief
 * A reference is used to compare to simulated data.
 *
 * An instance of this class corresponds to the data-binding for type Reference of MonitoringModel.xsd.
 *
 * A reference therefore contains:
 * - a MMLout (xml type MonitoringOut)
 * - a target to consider inside the MMLout
 *
 * All MonitoringOut data is also managed by this class.
 * A MonitoringOut contains:
 * - the path to a PML document, this is managed by the pml private attribute.
 * - vector of timestep that contains (time, vector values), this is managed by inner struct timeStep.
 *
 * The PML document given in the MMLout is called the PML Reference.
 *
 * The PML Reference can either be at any state (beginning, rest shape, final step...etc...)
 * It is mainly used to get the list of Structure/Component to be used in correlation with the target field of the
 * reference.
 *
 * @note
 * Some monitors are directly using the position of the atoms given in the PML Reference to compute their values.
 * Therefore it is STONGLY advised to give a PML Reference that corresponds to the FINAL step of the simulation (position
 * at the end of the simulation/experiments).
 */
class Reference {

public:
    /// constructor
    Reference(mml::Reference reference, MonitoringManager* monitoringManager);
    /// destructor
    virtual ~Reference();

    /**
     * get the nearest reference point in the "target" using "Position" monitor at given time
     * @param pos atom's position (with eventual offset)
     * @param time time to get the Position
     * @param ref a table where positon of the nearest atom in reference will be stored
     * @return true if method succeded (a nearest atom was found in reference)
     */
    bool getNearest(double pos[3], double time, double ref[3]);

    /**
     * get the nearest reference point in the "target" using "Position" monitor of the PML Reference.
     * This is a fast method that gives the nearest position relatively to the PML Reference.
     *
     * @param pos atom's position (with eventual offset)
     * @param ref a table where positon of the nearest atom in reference will be stored
     * @return true if method succeded (a nearest atom was found in reference)
     */
    bool getNearest(double pos[3], double ref[3]);

    /**
     * get distance to a triangular mesh, the target of the reference must contain triangles.
     * This is a fast method that gives the distance relatively to the triangles in the PML Reference.
     *
     * \note This is a heuristic version, which means, it does not test all triangles to find the lowest. It
     * first look for the nearest atom in the PML Reference and then only check the triangles around this nearest atom.
     *
     * \note This is not the exact distance to the nearest triangle, but an approximation given by
     * the function distanceToTrianglePlane().
     *
     * @param pos atom's position (with eventual offset)
     * @param dist the distance
     * @return true if method succeded (a triangular mesh was found)
     */
    bool getDistanceToTriangularMesh(double pos[3], double& dist);

    /**
     * get the the values of a given monitor which do not depend of time or an atom (e.g. geometrical data)
     * @param type name of the monitor, must be a existing monitor in the mmlOut document
     * @param ref a table where data will be stored
     * @return true if method succeded (an existing monitor named "type" was found)
     */
    bool getMonitoredData(std::string type, double ref[]);

    /**
     * get the the values of a given monitor at a given time which do not depend an atom (e.g. volume)
     * if there is no corresponding time in reference, it return data for the first higher time.
     * @param type name of the monitor, must be a existing monitor in the mmlOut document
     * @param time time to get the values
     * @param realTime the nearest time found if realTime=infinity, the nearest is the final state
     * @param ref a table where data will be stored
     * @return true if method succeded
     */
    bool getMonitoredData(std::string type, double time, double& realTime, double ref[]);

    /**
     * get the the values of a given monitor at a given time and a given atom's index (e.g. position)
     * @param type name of the monitor, must be a existing monitor in the mmlOut document
     * @param time time to get the values
     * @param index atom's index
     * @param realTime the nearest time found if realTime=infinity, the nearest is the final state
     * @param ref a table where data will be stored
     * @return true if method succeded
     */
    bool getMonitoredData(std::string type, double time, int index, double& realTime, double ref[]);

    /// return a string relative to Reference type
    std::string toString();

private:

    /// private structure with monitors for a time step
    struct timeStep {
        double time;
        std::multimap<std::string, Monitor*> monitorsMap;
    };

    /// the mmlOut document
    std::string mmlOutFile;
    /// Object in the file generated by xsdcxx
    std::unique_ptr<mml::MonitoringOut> mmlOut;
    /// monitoring manager
    MonitoringManager* monitoringManager;
    /// target
    std::string target;
    /// pml of the reference
    PhysicalModel* pml;
    /** vector which contain alls monitors for each time step
     */
    std::vector<timeStep*> data;
    /// current data index, stored to avoid search from beginning in the data vector
    int CurrentIndex;

};

#endif // REFRENCE_REFERENCE_H
