/*****************************************************************************
 * $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 SIMULATOR_SIMULATORFACTORY_H
#define SIMULATOR_SIMULATORFACTORY_H
// Simulator includes
#include "Simulator.h"

#include "MMLAPI.h"
/**
 *
 * @ingroup group_cepmodeling_libraries_mml
 *
 * @brief
 * A factory to create Simulator
 */
class MML_API SimulatorFactory {
public:

    /// return the unique instance of the factory
    static SimulatorFactory* getInstance();

    /**
    * Register a class into the map
    * A registered class can be created using createMonitorDisplay()
    *
    * @tparam C a subclass of Simulator
    * @param id unique id to associate with the Class C
    */
    template<typename C>
    bool registerClass(std::string id, bool isInteractive) {
        if (mapObjectCreator.find(id) != mapObjectCreator.end()) {
            return false;
        }
        else {
            mapObjectCreator.insert(std::pair<std::string, CreateSimulatorFunctionPointer>(id, &createTheSimulator<C>));
            mapObjectCreator2.insert(std::pair<std::string, CreateSimulatorFunctionPointer2>(id, &createTheSimulator2<C>));
            mapInteractive.insert(std::pair<std::string, bool>(id, isInteractive));
            if (isInteractive) {
                interactiveSimulators.push_back(id);
            }
            else {
                nonInteractiveSimulators.push_back(id);
            }
            return true;
        }
    }

    /// Returns true if id is in the map
    bool isRegistered(std::string id);

    ///Creates a Simulator based on its string id or return nullptr if there is no id in the map
    Simulator* createSimulator(std::string id, MonitoringManager* monitoringManager);

    ///Creates a Simulator using a specific simulator file based on its string id or return nullptr if there is no id in the map
    Simulator* createSimulator(std::string id, MonitoringManager* monitoringManager, const char* file);

    /// give the number of interactive simulators registered
    int getNumberOfInteractiveSimulators();

    /// give the number of interactive simulators registered
    int getNumberOfNonInteractiveSimulators();

    /// get a interactive simulator name by its index
    std::string getInteractiveSimulator(const unsigned int index);

    /// get a non interactive simulator name by its index
    std::string getNonInteractiveSimulator(const unsigned int index);

    /// return true if the registered simulator id is interactive
    bool isInteractive(std::string id);

private:

    SimulatorFactory() = default;

    typedef Simulator* (*CreateSimulatorFunctionPointer)(MonitoringManager* monitoringManager);

    typedef Simulator* (*CreateSimulatorFunctionPointer2)(MonitoringManager* monitoringManager, const char* file);

    /// A map between Simulator name as string to functions
    std::map<std::string, CreateSimulatorFunctionPointer> mapObjectCreator;

    /// A map between Simulator name as string to functions (for the 2nd constructor that uses a simulator file)
    std::map<std::string, CreateSimulatorFunctionPointer2> mapObjectCreator2;



    /**
     * map the simulator name with the 1 parameter constructor.
     *
    * function whose pointers are inserted into the map
    * @param C  type of Simulator
    * @return an MonitorDisplay which type is C
    */
    template<typename C>
    static Simulator* createTheSimulator(MonitoringManager* monitoringManager) {
        return new C(monitoringManager);
    }

    /**
     * map the simulator name with the 2 parameters constructor (using the given specific simulator file)
     *
    * function whose pointers are inserted into the map
    * @param C  type of Simulator
    * @param file specific simulator file
    * @return an MonitorDisplay which type is C
    *
    */
    template<typename C>
    static Simulator* createTheSimulator2(MonitoringManager* monitoringManager, const char* file) {
        return new C(monitoringManager, file);
    }

    /// map between Simulator name as string to booleen, true if interactive
    std::map<std::string, bool> mapInteractive;

    /// list of interactive simulators
    std::vector<std::string> interactiveSimulators;

    /// list of non interactive simulators
    std::vector<std::string> nonInteractiveSimulators;

    /// unique instance of the factory
    static SimulatorFactory* instance;

};


#endif // SIMULATOR_SIMULATORFACTORY_H
