// %pacpus:license{
// This file is part of the PACPUS framework distributed under the
// CECILL-C License, Version 1.0.
// %pacpus:license}
/// @file
/// @author  Gerald Dhermobez <firstname.surname@utc.fr>
/// @author  Marek Kurdej <firstname.surname@utc.fr>
/// @author  Samuel Gosselin <firstname.surname@utc.fr>
/// @author  Julien Moras <firstname.surname@utc.fr>
/// @date    February, 2006
/// @version $Id: ComponentBase.h 76 2013-01-10 17:05:10Z kurdejma $
/// @copyright Copyright (c) UTC/CNRS Heudiasyc 2006 - 2013. All rights reserved.
/// @brief Generic ComponentBase class. This is an abstract class.
///
/// Detailed description.
/// @todo        - see if some methods can be private with ComponentManager
///              friendship
///              - include the copy of Xml node in param here
///              - see if there is a possibility to avoid the constraint
///              on parameters in the constructor of derived class

#ifndef DEF_PACPUS_COMPONENTBASE_H
#define DEF_PACPUS_COMPONENTBASE_H

#include <Pacpus/kernel/ComponentManager.h>
#include <Pacpus/kernel/pacpus.h>
#include <Pacpus/kernel/XmlComponentConfig.h>
#include <Pacpus/kernel/InputOutputBase.h>

#include <QString>
#include <QMap>

class QWidget;

namespace pacpus {

// Forward declarations.
class ComponentManager;

template <typename T, class C>
class InputInterface;

template <typename T, class C>
class OutputInterface;

/** ComponentBase
 * @brief Base class of a Pacpus component.
 */
class PACPUSLIB_API ComponentBase
{
    friend class ComponentManager;

public:
    /**
     * Enumeration of the state that can take a component, the three last states suppose
     * that the component is started.
     */
    enum COMPONENT_STATE
    {
      STOPPED,
      NOT_MONITORED,
      MONITOR_OK,
      MONITOR_NOK
    };

    /** Resulting state of a component after its configuration. */
    enum COMPONENT_CONFIGURATION
    {
      CONFIGURED_OK,
      NOT_CONFIGURED,
      CONFIGURATION_DELAYED,
      CONFIGURED_FAILED
    };

    /** Ctor of ComponentBase.
     * @param name Name of your component.
     */
    ComponentBase(const QString& name);

    /** Dtor of ComponentBase. */
    virtual ~ComponentBase();

    /** Return the state of the component.
     * @return Value of the current state.
     */
    COMPONENT_STATE getState();

    /** Check whether the component if configurer or not.
     * @return True if the component is configured, otherwise false.
     */
    bool isConfigured() const;

    /** Return the name of the component.
     * @return Name of the component.
     */
    QString name() const;
    QString getName() const;

    InputInterfaceBase * getInput(QString name) const;

    OutputInterfaceBase * getOutput(QString name) const;

protected:
    /** Change the state of the component.
     * @param state New component state.
     */
    void setState(COMPONENT_STATE state);

    /** Called when the component starts, you must override this function. */
    virtual void startActivity() = 0;

    /** Called when the component stops, you must override this function. */
    virtual void stopActivity() = 0;

    /** Called by the ComponentManager, it configure the component thanks a XML node.
     * @param config Component's XML node.
     * @return State of the configuration.
     * FIXME: 'config' should be const, but we can't change the prototype without breaking
     * old stuff.
     */
    virtual COMPONENT_CONFIGURATION configureComponent(XmlComponentConfig config) = 0;

    // virtual QString getType() = 0;

    // Not Pure virtual, no need to implement if not needed !!!
    virtual void addInputs() {}
    virtual void addOutputs() {}

protected:
    typedef QMap<QString, InputInterfaceBase *> InputsMap;
    typedef QMap<QString, OutputInterfaceBase *> OutputsMap;

    // TODO: use std::function<void (const DataType &)>
    // TODO: use std::mem_fun<void (const DataType &)>
    template <typename DataType, class ComponentType, typename Function>
    void addInput(const char * name, Function function)
    {
        typedef InputInterface<DataType, ComponentType> InputType;
        InputType * connection = new InputType(name, dynamic_cast<ComponentType *>(this), function);
        inputs().insert(name, connection);
    }

    template <typename DataType, class ComponentType>
    void addOutput(const char * name)
    {
        typedef OutputInterface<DataType, ComponentType> OutputType;
        OutputType * connection = new OutputType(name, dynamic_cast<ComponentType *>(this));
        outputs().insert(name, connection);
    }

    template <typename DataType, class ComponentType>
    InputInterface<DataType, ComponentType> *
        getTypedInput(const char * name) const
    {
        return dynamic_cast<InputInterface<DataType, ComponentType> *>(getInput(name));
    }

    template <typename DataType, class ComponentType>
    OutputInterface<DataType, ComponentType> *
        getTypedOutput(const char * name) const
    {
        return dynamic_cast<OutputInterface<DataType, ComponentType> *>(getOutput(name));
    }

    bool isActive() const;
    void setActive(bool isActive);
    bool isRecording() const;
    void setRecording(bool isRecording);

    InputsMap & inputs();
    const InputsMap & inputs() const;
    OutputsMap & outputs();
    const OutputsMap & outputs() const;

    COMPONENT_CONFIGURATION configurationState() const;
    void setConfigurationState(COMPONENT_CONFIGURATION state);

    const XmlComponentConfig xmlParameters() const;
    
private:
    /// called by the ComponentManager to start the component
    int startComponent();

    /// called by the ComponentManager to stop the component
    int stopComponent();

private:
    /// The XML node that is got in the configureComponent method
    XmlComponentConfig param;

    /// the name of the component. It is this one in the XML config file
    QString m_componentName;
    
    /// is the component active?
    volatile bool m_isActive;

    /// is the component is recording data?
    bool m_isRecording;

    /// a pointer to the manager of components
    ComponentManager * m_manager;

    InputsMap m_inputs;
    OutputsMap m_outputs;

    /// a pointer to an optional widget
    QWidget * m_ui;

    /// store the state of the component
    COMPONENT_STATE m_componentState;

    /// is the component configured (ie configureComponent method was called)
    COMPONENT_CONFIGURATION m_configurationState;
};

} // pacpus

#endif // DEF_PACPUS_COMPONENTBASE_H
