#ifndef IN_OUT_BASE_H
#define IN_OUT_BASE_H

#include <Pacpus/kernel/Log.h>
#include <Pacpus/kernel/pacpus.h>
#include <Pacpus/kernel/ConnectionBase.h>
#include <Pacpus/kernel/PacpusEvent.h>

#include <QApplication>
#include <QList>
#include <QString>
#include <QStringList>
#include <typeinfo>

namespace pacpus {

class ComponentBase;

class PACPUSLIB_API AbstractInterface
    : public QObject
{
     Q_OBJECT

protected:
    AbstractInterface(QString name, ComponentBase * component, QObject * parent = 0)
        : m_name(name)
        , m_component(component)
        , QObject(parent)
    {
        LOG_DEBUG("constructing abstract connection '" << getName() << "'");
    }

    ~AbstractInterface()
    {}

public:
    QString getSignature();
    QString getName()
    {
        return m_name;
    }
    
    virtual QString getDataType() = 0;
    
    ComponentBase * getComponent()
    {
        return m_component;
    }

    void addConnection(ConnectionBase connection)
    {
        m_connections.append(connection);
    }

    bool removeConnection(ConnectionBase connection)
    {
        return m_connections.removeOne(connection);
    }

    bool hasConnection()
    {
        return m_connections.size() > 0;
    }
    
protected:
    QList<ConnectionBase> & connections()
    {
        return m_connections;
    }

    const QList<ConnectionBase> & connections() const
    {
        return m_connections;
    }

    QString name()
    {
        return m_name;
    }

    ComponentBase * component()
    {
        return m_component;
    }

    const ComponentBase * component() const
    {
        return m_component;
    }
    
private:
    QString m_name;
    ComponentBase * m_component;
    QList<ConnectionBase> m_connections;
};

class PACPUSLIB_API InputInterfaceBase
    : public AbstractInterface
{
    Q_OBJECT

protected:
    InputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0)
        : AbstractInterface(name, component, parent)
    {}

public:
    //InputInterfaceBase(QString name, ComponentBase * component,int a, QObject * parent = 0):AbstractInterface(name,component,parent) {} // TODO add ctor with function pointer

    enum ReadingMode {
        NeverSkip,
        TimeBounded,
        GetLast
    };

    virtual ~InputInterfaceBase()
    {}

    // TODO for serealization prupose (not yet implemented !!!)
    virtual void customEvent(QEvent* e)
    {
        //if(event->type())
        //TODO get event Type anf call callback function

        PacpusEvent * event = dynamic_cast<PacpusEvent *>(e);
        QByteArray buf;
        QDataStream out(&buf, QIODevice::WriteOnly);
        event->streamOut(out);
        // Callback QByteArray
    }

    ReadingMode & readingMode()
    {
        return m_readingMode;
    }

    const ReadingMode & readingMode() const
    {
        return m_readingMode;
    }

    void setReadingMode(ReadingMode mode)
    {
        m_readingMode = mode;
    }

    virtual PacpusEvent* getEventTemplate()
    {
        // TODO: check
        return new PacpusEvent(GENERIC_EVENT);
    }
    
private:
    ReadingMode m_readingMode;

    // metode(QByteArray)
    //QQueue jobQueue_;
};

class PACPUSLIB_API OutputInterfaceBase
    : public AbstractInterface
{
    Q_OBJECT

public:
    OutputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0)
        : AbstractInterface(name, component, parent)
    {}

    virtual ~OutputInterfaceBase()
    {}

    QStringList getInputConnectedList()
    {
        QStringList list;
        for(QList<ConnectionBase>::iterator it = connections().begin(); it != connections().end(); ++it) {
            list.append(it->getInterface()->getName());
        }
        return list;
    }

    // TODO for serealization prupose (not yet implemented !!!)
    void send(/*const*/ QByteArray & data)
    {
        // TODO check at least one Typed connection

        for(QList<ConnectionBase>::iterator it = connections().begin(); it!=connections().end(); ++it){
                QDataStream in(&data,QIODevice::ReadOnly);
                PacpusEvent* event = dynamic_cast<InputInterfaceBase*>(connections().at(0).getInterface())->getEventTemplate();
                event->streamIn(in);
                QApplication::postEvent(it->getInterface(),event,it->getPriority());
        }
    }

};

static bool connectInterface(OutputInterfaceBase* out, InputInterfaceBase * in, int priority, InputInterfaceBase::ReadingMode mode = InputInterfaceBase::GetLast)
{
    if(out->getDataType() == in->getDataType() || out->getDataType() == QString(typeid(QByteArray).name()) || in->getDataType() == QString(typeid(QByteArray).name())) {
        // Add connection
        out->addConnection(ConnectionBase(in,priority));  // TODO make connect function
        in->addConnection(ConnectionBase(out,priority));
        in->setReadingMode(mode);
        //LOG_INFO("connection : Output " << out->getSignature() << " => Input " << in->getSignature());
        return true;
    } else {
        //LOG_WARN("connecting " << out->getSignature() << ":" << out->getDataType() << " to " << in->getSignature() << ":" << in->getDataType() << " failled : DataType incompatible " <<  QString(typeid(QByteArray).name()));
        return false;
    }
}

} // namespace pacpus

#endif // IN_OUT_BASE_H
