/*****************************************************************************
 * $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 MMLMonitorDisplayFactory_H
#define MMLMonitorDisplayFactory_H

#include <map>
#include <vector>
#include <string>

// Monitor includes
#include "Monitor.h"
#include "MMLComponentAPI.h"

class MMLMonitorDisplay;
class MMLComponent;
/**
 * @ingroup group_cepmodeling_components_mml
 *
 * @brief
 * A Factory for creating MMLMonitorDiplay
 *
 *
 **/
class MMLCOMPONENT_API MMLMonitorDisplayFactory {

public:

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

    /**
    * Register a class into the map
    * A registered class can be created using createMonitorDisplay().
    *
    * C is a subclass of MMLMonitorDisplay.
    *
    * @param id unique id to associate with the Class C
    * @param type the type of monitor the class can display
    */
    template<typename C>
    bool registerClass(std::string id, Monitor::type type) {
        if (mapObjectCreator.find(id) != mapObjectCreator.end()) {
            return false;
        }
        else {
            mapObjectCreator.insert(std::pair<std::string, CreateMonitorDisplayFunctionPointer>(id, &createTheMonitorDisplay<C>));
            displaysByType[type].push_back(id);
            return true;
        }
    }

    /**
    * Register a class into the map for classes wich can display every types of monitors.
    *
    * C is a subclass of MMLMonitorDisplay.
    *
    * @param id unique id to associate with the Class C
    */
    template<typename C>
    bool registerClass(std::string id) {
        if (mapObjectCreator.find(id) != mapObjectCreator.end()) {
            return false;
        }
        else {
            mapObjectCreator.insert(std::pair<std::string, CreateMonitorDisplayFunctionPointer>(id, &createTheMonitorDisplay<C>));
            for (unsigned int i = 0; i < Monitor::typeCount; i++) {
                displaysByType[i].push_back(id);
            }

            return true;
        }
    }

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

    ///Creates an MMLMonitorDisplay based on its string id or return null if there is no id in the map
    MMLMonitorDisplay* createMonitorDisplay(std::string id, Monitor* monitor, MMLComponent* manager);

    /// give the number of displays registered for a specified type
    unsigned int getNumberOfDisplaysByType(Monitor::type type);

    /// get a display name by its type and index
    std::string getDisplayByType(Monitor::type type, const unsigned int i);

private:

    MMLMonitorDisplayFactory() = default;

    using CreateMonitorDisplayFunctionPointer = MMLMonitorDisplay * (*)(Monitor* monitor, MMLComponent* manager);

    /// A map  between MonitorDisplay name as string to functions (CreateMonitorDisplayFunctionPointer)
    std::map<std::string, CreateMonitorDisplayFunctionPointer> mapObjectCreator;

    /**
    * function whose pointers are inserted into the map. C is the type of MMLMonitorDisplay
    * @return an MonitorDisplay which type is C
    */
    template<typename C>
    static MMLMonitorDisplay* createTheMonitorDisplay(Monitor* monitor, MMLComponent* manager) {
        return new C(monitor, manager);
    }

    /// list of displays by type
    std::vector<std::string> displaysByType[Monitor::typeCount];

    /// unique instance of the factory
    static MMLMonitorDisplayFactory* instance;
};


#endif // MMLMonitorDisplayFactory_H
