//
// Created by magino on 5. 10. 2019.
//

#ifndef Reservation_H
#define Reservation_H

#include "core/UaNodeContext.h"

class Reservation: public UaNodeContext {
private:
    UaServer& _server;
public:
    uint64_t id;
    uint64_t productId;
    UA_Byte actionId;
    uint operationOrder;

    float parameterA;
    float parameterB;


    static UaNodeId TYPE_NODEID;
    // Browse names - are static because i want reach them from everywhere in code
    static const std::string InstanceObj;
    static const std::string ReservationIdVar;
    static const std::string ProductIdVar;
    static const std::string ActionIdVar;
    static const std::string OperationOrderVar;

    static const std::string ParameterAVar;
    static const std::string ParameterBVar;

    static void CreateUaObjectType(UaServer& server) {
        TYPE_NODEID = UaNodeId(server.GetAppNsIndex(), "RsrvtnType");
        server.AddObjectTypeNode("ReservationType",
                                 "ReservationType",
                                 "Object, which holds data of reservations.",
                                 TYPE_NODEID);

        UaNodeId tmpNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        server.AddVariableNode("Reservation Id",
                               Reservation::ReservationIdVar,
                               "Universally unique id of reservation.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_UINT64,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        tmpNodeId = UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        server.AddVariableNode("Product Id",
                               Reservation::ProductIdVar,
                               "Universally unique id of product which made reservation.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_UINT64,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        tmpNodeId = UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        server.AddVariableNode("ActionId",
                               Reservation::ActionIdVar,
                               "ActionId which is reserved in process cell.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_BYTE,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        tmpNodeId = UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());

        server.AddVariableNode("Operation Order",
                               Reservation::OperationOrderVar,
                               "Operation order in recepture.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_BYTE,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        tmpNodeId = UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        server.AddVariableNode("Parameter A",
                               Reservation::ParameterAVar,
                               "Parameter A of reserved ActionId in process cell.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_FLOAT,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        tmpNodeId = UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        server.AddVariableNode("Parameter B",
                               Reservation::ParameterBVar,
                               "Parameter B of reserved ActionId in process cell.",
                               true,
                               UA_ACCESSLEVELMASK_READ,
                               UA_TYPES_FLOAT,
                               UA_VALUERANK_SCALAR,
                               0,
                               nullptr,
                               TYPE_NODEID,
                               tmpNodeId);

        SetTypeLifeCycle(server, TYPE_NODEID);
    }

    Reservation(UaServer& server,
                uint64_t reservationId,
                uint64_t productId,
                UA_Byte actionId,
                UA_Byte operationOrder,
                float parA,
                float parB
                ): _server(server),
                   id(reservationId),
                   productId(productId),
                   actionId(actionId),
                   operationOrder(operationOrder),
                   parameterA(parA),
                   parameterB(parB),
                   UaNodeContext(UaNodeId(server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId())) {}

    // Copy constructor
    Reservation(const Reservation& reservation):
            _server(reservation._server),
            id(reservation.id),
            productId(reservation.productId),
            actionId(reservation.actionId),
            operationOrder(reservation.operationOrder),
            parameterA(reservation.parameterA),
            parameterB(reservation.parameterB) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Reservation Copied");
    }
    // Move constructor
    Reservation(Reservation&& reservation):
            _server(reservation._server),
            id(reservation.id),
            productId(reservation.productId),
            actionId(reservation.actionId),
            operationOrder(reservation.operationOrder),
            parameterA(reservation.parameterA),
            parameterB(reservation.parameterB) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Reservation Moved");
    }

    ~Reservation() = default;

    void DeleteNode() {
        if(!instanceNodeId.IsNull()) {
            _server.DeleteNode(instanceNodeId, true);
            UA_LOG_INFO(UA_Log_Stdout,
                        UA_LOGCATEGORY_USERLAND,
                        "Reservation with node id:%s  has been deleted",
                        instanceNodeId.ToString().c_str());
        }
    }

    bool Instantiate(UaNodeId& parent, UaNodeId& reference, std::string name) {
        instanceNodeId = UaNodeId(_server.GetAppNsIndex(), UaNodeContext::GetUniqueNumericNodeId());
        UaNodeId tmpNodeId = _server.AddObjectNode(std::move(name), Reservation::InstanceObj, "", false, parent, instanceNodeId, reference, Reservation::TYPE_NODEID, this);

        return !tmpNodeId.IsNull() && instanceNodeId == tmpNodeId;
    }

    bool TypeConstruct(UaNodeId& instanceNodeId, UaNodeId& typeNodeId, void* instanceCtx = nullptr) override {
        UaNodeId foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::ReservationIdVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }

        foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::ProductIdVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }

        foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::ActionIdVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }

        foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::OperationOrderVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }

        foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::ParameterAVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }

        foundedNodeId = _server.BrowseSimplifiedSingleNodeId(instanceNodeId, Reservation::ParameterBVar, _server.GetAppNsIndex());
        if(!foundedNodeId.IsNull()) {
            _server.SetNodeContext(foundedNodeId, this);
            SetValueCallback(_server, foundedNodeId);
        }
        return true;
    }

    void ReadValue(UaNodeId& node, const UA_NumericRange* range, const UA_DataValue* value) override {
        std::string name = _server.ReadBrowseName(node);
        UA_Variant val;
        if(name == Reservation::ReservationIdVar) {
            UA_Variant_setScalar(&val, &id, &UA_TYPES[UA_TYPES_UINT64]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }

        if(name == Reservation::ProductIdVar) {
            UA_Variant_setScalar(&val, &productId, &UA_TYPES[UA_TYPES_UINT64]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }
        if(name == Reservation::ActionIdVar) {
            UA_Variant_setScalar(&val, &actionId, &UA_TYPES[UA_TYPES_BYTE]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }

        if(name == Reservation::OperationOrderVar) {
            UA_Variant_setScalar(&val, &operationOrder, &UA_TYPES[UA_TYPES_BYTE]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }
        /*
        if(name.compare(Reservation::parametersVarArray) == 0) {
            UA_Variant_setArray(&val, &parameters[0], parameters.size(), &UA_TYPES[UA_TYPES_INT32]);
            UA_Server_writeValue(server.GetServer(), node.GetNodeId(), val);
        }
        */
        if(name == Reservation::ParameterAVar) {
            UA_Variant_setScalar(&val, &parameterA, &UA_TYPES[UA_TYPES_FLOAT]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }
        if(name == Reservation::ParameterBVar) {
            UA_Variant_setScalar(&val, &parameterB, &UA_TYPES[UA_TYPES_FLOAT]);
            UA_Server_writeValue(_server.GetServer(), node.GetNodeId(), val);
        }
    }
};

UaNodeId Reservation::TYPE_NODEID = UaNodeId();

const std::string Reservation::InstanceObj = "Reservation";

const std::string Reservation::ReservationIdVar = "ReservationId";
const std::string Reservation::ProductIdVar = "ProductId";
const std::string Reservation::ActionIdVar = "ActionId";
const std::string Reservation::OperationOrderVar = "OperationOrder";
const std::string Reservation::ParameterAVar = "ParameterA";
const std::string Reservation::ParameterBVar = "ParameterB";

#endif //Reservation_H