#ifndef _TMathChannel_h__
#define _TMathChannel_h__

#include <QHash>
#include <QMap>
#include <QMutex>
#include <QScriptEngine>
#include <QSet>
#include <QVector>
#include <QWaitCondition>

#include "DaemonThread.h"
#include "DataTable.h"
#include "TDevice.h"
#include "queue.h"

class MathChannel : public DaemonThread {

    Q_OBJECT

  public:
    MathChannel(int index);
    virtual ~MathChannel();

    int getIndex();
    const QString &getExpression();
    unit_t getUnit();
    TDevice *getDevice();

    void setExpression(const QString &expression);
    void setUnit(unit_t unit);

    virtual void run();
    virtual void shutdown();

  private:
    enum State { READY, SHUTDOWN };

    struct Event {
        int code;
        void *data;
        Event() {}
        Event(int code, void *data) : code(code), data(data) {}
    };

    QMutex mutex;
    QWaitCondition condition;

    // Members that are read-only after construction (no need to lock mutex)
    const int index;
    const QString name;

    // Members accessed by local thread only (no need to lock mutex)
    DataTable *table;
    QScriptEngine engine;
    QString expression;
    QMap<QString, bool> serialNumbers;
    QMap<QString, bool> channelIDs;
    QMap<QString, TDevice *> devicesBySerialNumber;
    QHash<QString, TChannel *> channelsByID;

    // Members accessed by application thread and local thread (MUST LOCK MUTEX)
    State state = READY;
    TDevice *mathDevice;
    Queue events;

    // Private methods

    bool processEvents();
    void processDeviceAdd(const TDevice *device);
    void processDeviceConnect(const TDevice *device);
    void processDeviceDisconnect(const TDevice *device);
    void processDeviceUpdate(const TDevice *device);
    void processExpressionChange();

    TDevice *addDevice(const TDevice *device);
    TDevice *addDevice(const QString &serialNumber);

    void clear();
    void setActive();
    void setInactive();
    bool replaceVariables(QString &output, const DataRow *row);
    void update();
    void update(const DataRow *row);

  private slots:

    // All slots invoked by main application thread (MUST LOCK MUTEX)

    void onDeviceAdded(const TDevice *device);
    void onDeviceConnected(const TDevice *device);
    void onDeviceDisconnected(const TDevice *device);
    void onDeviceUpdated(const TDevice *device);

    void addEvent(int code, void *data);
};

#endif