//
// Created by magino on 3. 8. 2019.
//

#ifndef UAMONITOREDITEM_H
#define UAMONITOREDITEM_H

#include <functional>

#include <open62541/client.h>
#include <open62541/client_highlevel.h>
#include <open62541/client_config_default.h>
#include <open62541/client_subscriptions.h>

#include "core/UaNodeId.h"

class UaSubscription;
class UaClient;

// Callback for a (data change)  monitored item
typedef std::function<void (UaSubscription&, UA_DataValue*)> MonitorItemFunc;
// call back for an event
//typedef std::function<void (UaSubscription &, VariantArray &)> monitorEventFunc;

class UaMonitoredItem {
private:
    UaSubscription& _sub;

protected:
    UA_MonitoredItemCreateResult result;

    /* Callback for DataChange notifications */
    static  void DataChangeNotificationCallback
        (UA_Client* client, UA_UInt32 subId, void* subContext,
         UA_UInt32 monId, void* monContext, UA_DataValue* value);

    /* Callback for Event notifications */
    static void EventNotificationCallback
        (UA_Client* client, UA_UInt32 subId, void* subContext,
         UA_UInt32 monId, void* monContext, size_t nEventFields, UA_Variant* eventFields);

    /* Callback for the deletion of a UaMonitoredItem */
    static void DeleteMonitoredItemCallback
        (UA_Client* client, UA_UInt32 subId, void* subContext,
         UA_UInt32 monId, void* monContext);

public:
    // TODO protected constructor? because i do not want instantiate UaMonitoredItem class on it own
    UaMonitoredItem(UaSubscription& subscription) : _sub(subscription) {}
    virtual ~UaMonitoredItem() { Remove(); }

    UaSubscription& GetSubscription() { return _sub;}
    unsigned GetId() { return result.monitoredItemId; }

    virtual void DataChangeNotification(UA_DataValue*) {}
    virtual void EventNotification(size_t, UA_Variant*) {}
    virtual void DeleteMonitoredItem() { Remove(); }
    virtual bool Remove();

    // UA_Client_MonitoredItems_delete
    // UA_Client_MonitoredItems_deleteSingle
    // UA_Client_MonitoredItems_modify

    // UA_Client_MonitoredItems_setMonitoringMode // Disabled / Sampling / Reporting !!! modify also UaMonitoredItem
    // UA_SetTriggeringResponse TODO what is triggering?
};

class MonitoredItemDataChange : public UaMonitoredItem {
private:
    MonitorItemFunc _func;

public:
    MonitoredItemDataChange(UaSubscription& sub) : UaMonitoredItem(sub) {}
    MonitoredItemDataChange(UaSubscription& sub, MonitorItemFunc func) : UaMonitoredItem(sub), _func(func) {}
    void SetFunction(MonitorItemFunc func) { _func = func; }

    virtual void DataChangeNotification(UA_DataValue* value) {
        if (_func) _func(GetSubscription(), value);
    }

    bool AddDataChange(UaNodeId& nodeId, UA_TimestampsToReturn timeStamps = UA_TIMESTAMPSTORETURN_BOTH);
    bool AddDataChange(UaNodeId &nodeId, UA_TimestampsToReturn timeStamps, unsigned samplingInterval, bool discardOldest = true, unsigned queueSize = 1);
};

// UA_Client_MonitoredItems_createEvent
// UA_Client_MonitoredItems_createEvents
/*!
    \brief The MonitoredItemEvent class
*/
/*
class MonitoredItemEvent : public UaMonitoredItem {
    monitorEventFunc _func; // the event call functor
    EventFilterSelect * _events = nullptr; // filter for events
public:

    MonitoredItemEvent(ClientSubscription &s) : UaMonitoredItem(s) {}
    MonitoredItemEvent(monitorEventFunc f, ClientSubscription &s) : UaMonitoredItem(s), _func(f) {}

    bool remove()
    {
        bool ret = UaMonitoredItem::remove();
        if(_events) delete _events;
        return ret;
    }

    void SetFunction(monitorEventFunc f) {
        _func = f;
    }

    virtual void eventNotification(size_t nEventFields, UA_Variant *eventFields) {
        if (_func) {
            VariantArray va;
            va.setList(nEventFields, eventFields);
            _func(subscription(), va); // invoke functor
            va.release();
        }
    }

    bool addEvent(UaNodeId &n,  EventFilterSelect *events, UA_TimestampsToReturn ts = UA_TIMESTAMPSTORETURN_BOTH);
};
 */
#endif //UAMONITOREDITEM_H