#ifndef IN_OUT_INTERFACE_H
#define IN_OUT_INTERFACE_H

#include <Pacpus/kernel/Log.h>
#include <Pacpus/kernel/InputOutputBase.h>
#include <QApplication>
#include <QByteArray>
#include <QThread>
#include <typeinfo>

namespace pacpus {

template <typename T, class C>
class InputInterface
    : public InputInterfaceBase
{
public:
    InputInterface(QString name, C * component, void (C::*m)(const T&))
        : InputInterfaceBase(name, component, component)
        , method(m)
    {}

    ~InputInterface()
    {}

    size_t getDataSize()
    {
        return sizeof(T);
    }

    QString getDataType()
    {
        return QString(typeid(T).name());
    }

    PacpusEvent* getEventTemplate()
    {
        return new PacpusTypedEvent<T>(TYPED_EVENT);
    }

    void customEvent(QEvent* event)
    {
        // TODO check component state started
        //if(_component) get state
        switch (event->type()) {
        case TYPED_EVENT:
            {

            PacpusTypedEvent<T> * typedEvent = dynamic_cast<PacpusTypedEvent<T> *> (event);

            LOG_DEBUG("Receiver " << getSignature() << " thread " << QThread::currentThread());



            if (typedEvent->timerange() < 500 && readingMode() == TimeBounded) {
                LOG_WARN("Incorrect TimeRange (0), switch to NeverSkip");
                readingMode() = NeverSkip;
            }

            switch (readingMode()) {
            case TimeBounded:

                if (road_time() - typedEvent->time() > typedEvent->timerange()) {
                    LOG_DEBUG("Data skip " << this->getSignature());
                    break;
                }

                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
                break;

            case GetLast:

                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
                // delete all remaining events
                QCoreApplication::removePostedEvents(this, TYPED_EVENT);
                break;

            case NeverSkip:
                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
                break;

            default:
                LOG_WARN("Unknown reading mode " << readingMode());
            }
            break;
        }

            // Add here new event type if needed

        default:
            LOG_WARN("Unknown event ID " << event->type());
            break;
        }
    event->accept();
    }

    // TODO for Pulling mode (not yet implemented !!!)
    T& getData() {
        T data;
        // TODO ask output data;

        return data;
    }

protected:
    void (C::*method)(const T&);

};

template <typename T, class C>
class OutputInterface : public OutputInterfaceBase
{
public:
    OutputInterface(QString name, C * component):OutputInterfaceBase(name,component,component) {}
    ~OutputInterface() {}

    // Used by Components to send data througth typed output
    void send(const T & data, road_time_t t = road_time(), road_timerange_t tr = 0)
    {
        // FIXME Data Shared
        //QSharedPointer<T> sharedPointer = new T(data);

        for (QList<ConnectionBase>::iterator it = connections().begin(); it != connections().end(); ++it) {
            QApplication::postEvent(it->getInterface(),new PacpusTypedEvent<T>(TYPED_EVENT,data,t,tr),it->getPriority()); // Event is delete by the event loop handler
            //qDebug() << "sender " << it->getInterface()->getSignature() <<  " thread " << QThread::currentThread() << " Data & " << &data << " ";
        }
    }

    size_t getDataSize()
    {
        return sizeof(T);
    }

    QString getDataType()
    {
        return QString(typeid(T).name());
    }
};


} // namespace pacpus

#endif // IN_OUT_INTERFACE_H
