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

#ifndef UANODEID_H
#define UANODEID_H

#include <iostream>
#include <open62541/types.h>
#include <open62541/nodeids.h>

class UaNodeId {
private:
    UA_NodeId _nodeId;
public:
    UaNodeId(): _nodeId(UA_NODEID_NULL) {}
    UaNodeId(UA_NodeId nodeId): _nodeId(nodeId) {}
    UaNodeId(const unsigned short nsIndex, std::string& id): _nodeId(UA_NODEID_STRING(UA_UInt16(nsIndex), &id[0])) {}
    // TODO difference -> <- ??
    UaNodeId(const unsigned short nsIndex, const std::string& id): _nodeId(UA_NODEID_STRING_ALLOC(UA_UInt16(nsIndex), id.c_str())) {}
    UaNodeId(const unsigned short nsIndex, const unsigned id): _nodeId(UA_NODEID_NUMERIC(UA_UInt16(nsIndex), id)) {}
    UaNodeId(const unsigned short index, const UA_Guid guid): _nodeId(UA_NODEID_GUID(UA_UInt16(index), guid)) {}

    const UA_NodeId& GetNodeId() const { return _nodeId; }
    UA_NodeId* Get() { return &_nodeId; }
    int GetNameSpaceIndex() const { return _nodeId.namespaceIndex; }
    void SetNameSpaceIndex(const unsigned short nsIndex) { _nodeId.namespaceIndex = nsIndex; }
    UA_NodeIdType GetIdentifierType() const { return _nodeId.identifierType; }

    bool IsNull() const { return UA_NodeId_isNull(&_nodeId); }
    UaNodeId& operator=(const UA_NodeId& nodeId) {
        _nodeId = nodeId;
    }
    bool operator == (const UaNodeId& nodeId) const {
        return UA_NodeId_equal(&_nodeId, &(nodeId.GetNodeId()));
    }

    bool operator != (const UaNodeId& nodeId) const {
        return !UA_NodeId_equal(&_nodeId, &(nodeId.GetNodeId()));
    }

    std::string ToString() const {
        std::string ret = std::to_string(_nodeId.namespaceIndex) + ":";

        switch (_nodeId.identifierType) {
            case UA_NODEIDTYPE_NUMERIC:
                return ret + std::to_string(_nodeId.identifier.numeric);

            case UA_NODEIDTYPE_BYTESTRING:
            case UA_NODEIDTYPE_STRING:
                return ret + std::string((const char *)(_nodeId.identifier.string.data), _nodeId.identifier.string.length);
            case UA_NODEIDTYPE_GUID: {
                char b[45];
                int l = sprintf(b, "%08X:%04X:%04X[%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X]",
                                _nodeId.identifier.guid.data1,
                                _nodeId.identifier.guid.data2,
                                _nodeId.identifier.guid.data3,
                                _nodeId.identifier.guid.data4[0],
                                _nodeId.identifier.guid.data4[1],
                                _nodeId.identifier.guid.data4[2],
                                _nodeId.identifier.guid.data4[3],
                                _nodeId.identifier.guid.data4[4],
                                _nodeId.identifier.guid.data4[5],
                                _nodeId.identifier.guid.data4[6],
                                _nodeId.identifier.guid.data4[7]);

                return ret + std::string(b, l);
            }
            default:
                break;
        }
        return std::string("Invalid Node Type");
    }
// TODO porozmyslat to spravit const
    static UaNodeId ObjectsFolder;
    static UaNodeId Server;
    static UaNodeId Null;

    /*
        BaseVariableType
            -> BaseDataVariableType
            -> PropertyType
    */
    static UaNodeId BaseVariableType;
    static UaNodeId BaseDataVariableType;
    static UaNodeId PropertyType;

    static UaNodeId BaseObjectType;
    static UaNodeId FolderType;

    static UaNodeId HasComponent;
    static UaNodeId HasProperty;
    static UaNodeId Organizes;
    static UaNodeId HasSubType;
    static UaNodeId HasOrderedComponent;
    static UaNodeId HasModellingRule;
};

#endif //UANODEID_H
