#ifndef LIP_OPCUA_VARIANT_H
#define LIP_OPCUA_VARIANT_H


#include "lip_opcua_tstmp.h"
#include "lip_opcua_nodeid.h"


typedef enum {
    LIP_OPCUA_BUILTIN_DATATYPE_NULL         = 0,
    LIP_OPCUA_BUILTIN_DATATYPE_BOOL         = 1, /* A two-state logical value (true or false). */
    LIP_OPCUA_BUILTIN_DATATYPE_SBYTE        = 2, /* An integer value between −128 and 127 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_BYTE         = 3, /* An integer value between 0 and 255 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_INT16        = 4, /* An integer value between −32 768 and 32 767 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_UINT16       = 5, /* An integer value between 0 and 65 535 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_INT32        = 6, /* An integer value between −2 147 483 648 and 2 147 483 647 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_UINT32       = 7, /* An integer value between 0 and 4 294 967 295 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_INT64        = 8, /* An integer value between −9 223 372 036 854 775 808 and 9 223 372 036 854 775 807 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_UINT64       = 9, /* An integer value between 0 and 18 446 744 073 709 551 615 inclusive. */
    LIP_OPCUA_BUILTIN_DATATYPE_FLOAT        = 10, /* An IEEE single precision (32 bit) floating point value. */
    LIP_OPCUA_BUILTIN_DATATYPE_DOUBLE       = 11, /* An IEEE double precision (64 bit) floating point value. */
    LIP_OPCUA_BUILTIN_DATATYPE_STRING       = 12, /* A sequence of Unicode characters. */
    LIP_OPCUA_BUILTIN_DATATYPE_DATETIME     = 13, /* An instance in time. */
    LIP_OPCUA_BUILTIN_DATATYPE_GUID         = 14, /* A 16-byte value that can be used as a globally unique identifier. */
    LIP_OPCUA_BUILTIN_DATATYPE_BYTESTRING   = 15, /* A sequence of octets. */
    LIP_OPCUA_BUILTIN_DATATYPE_XMLELEM      = 16, /* A sequence of Unicode characters that is an XML element. */
    LIP_OPCUA_BUILTIN_DATATYPE_NODEID       = 17, /* An identifier for a node in the address space of an OPC UA Server. */
    LIP_OPCUA_BUILTIN_DATATYPE_EXPNODEID    = 18, /* A NodeId that allows the namespace URI to be specified instead of an index. */
    LIP_OPCUA_BUILTIN_DATATYPE_STATUS       = 19, /* A numeric identifier for an error or condition that is associated with a value or an operation.*/
    LIP_OPCUA_BUILTIN_DATATYPE_QNAME        = 20, /* A name qualified by a namespace.*/
    LIP_OPCUA_BUILTIN_DATATYPE_LOCTEXT      = 21, /* Human readable text with an optional locale identifier. */
    LIP_OPCUA_BUILTIN_DATATYPE_EXTOBJ       = 22, /* A structure that contains an application specific data type that may not be recognized by the receiver. */
    LIP_OPCUA_BUILTIN_DATATYPE_DATAVAL      = 23, /* A data value with an associated status code and timestamps. */
    LIP_OPCUA_BUILTIN_DATATYPE_VARIANT      = 24, /* A union of all of the types specified above. */
    LIP_OPCUA_BUILTIN_DATATYPE_DIAGNINFO    = 25, /* A structure that contains detailed error and diagnostic information associated with a StatusCode. */
} t_lip_opcua_builtin_datetype;




typedef struct {
    t_lip_opcua_builtin_datetype type;
    bool is_array;
    union {
        bool     vbool;
        uint32_t uint;
        int32_t  sint;
        uint64_t uint64;
        int64_t  sint64;
        float    vfloat;
        double   vdouble;
        t_lip_opcua_string_ptr str;
        t_lip_opcua_nodeid node_id;
        t_lip_opcua_datetime  datetime;
        t_lip_opcua_qualified_name qname;
        t_lip_opcua_loc_string_ptr loctext;
        struct {
            const void *ptr1;
            int32_t items_cnt1;
            const void *ptr2;
            int32_t items_cnt2;
        } array;
        struct {
            const t_lip_opcua_nodeid *node_id;
            const void *objptr;
        } extobj;
    } val;
} t_lip_opcua_variant;


#define lip_opcua_variant_set_enum lip_opcua_variant_set_int32


static inline void lip_opcua_variant_set_null(t_lip_opcua_variant *var) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_NULL;
    var->is_array = false;
}

static inline void lip_opcua_variant_set_bool(t_lip_opcua_variant *var, bool bval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_BOOL;
    var->is_array = false;
    var->val.vbool = bval;
}

static inline void lip_opcua_variant_set_int32(t_lip_opcua_variant *var, int32_t ival) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_INT32;
    var->is_array = false;
    var->val.sint = ival;
}

static inline void lip_opcua_variant_set_uint32(t_lip_opcua_variant *var, uint32_t uval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_UINT32;
    var->is_array = false;
    var->val.uint = uval;
}

static inline void lip_opcua_variant_set_uint8(t_lip_opcua_variant *var, uint8_t uval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_BYTE;
    var->is_array = false;
    var->val.uint = uval;
}

static inline void lip_opcua_variant_set_uint16(t_lip_opcua_variant *var, uint16_t uval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_UINT16;
    var->is_array = false;
    var->val.uint = uval;
}

static inline void lip_opcua_variant_set_uint64(t_lip_opcua_variant *var, uint64_t uval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_UINT64;
    var->is_array = false;
    var->val.uint64 = uval;
}


static inline void lip_opcua_variant_set_float(t_lip_opcua_variant *var, float fval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_FLOAT;
    var->is_array = false;
    var->val.vfloat = fval;
}

static inline void lip_opcua_variant_set_double(t_lip_opcua_variant *var, double dval) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_DOUBLE;
    var->is_array = false;
    var->val.vdouble = dval;
}

static inline void lip_opcua_variant_set_datetime(t_lip_opcua_variant *var, t_lip_opcua_datetime datetime) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_DATETIME;
    var->is_array = false;
    var->val.datetime = datetime;
}

static inline void lip_opcua_variant_set_nodeid(t_lip_opcua_variant *var, const t_lip_opcua_nodeid *id) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_NODEID;
    var->is_array = false;
    if (id) {
        var->val.node_id = *id;
    } else {
        var->val.node_id.type = LIP_OPCUA_NODEID_TYPE_INVALID;
    }
}

static inline void lip_opcua_variant_set_str(t_lip_opcua_variant *var, const t_lip_opcua_string_ptr *str) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_STRING;
    var->is_array = false;
    if (str) {
        var->val.str = *str;
    } else {
        var->val.str.size = -1;
    }
}

static inline void lip_opcua_variant_set_qname(t_lip_opcua_variant *var, const t_lip_opcua_qualified_name *name) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_QNAME;
    var->is_array = false;
    if (name) {
        var->val.qname = *name;
    } else {
        var->val.qname.ns_idx = 0;
        var->val.qname.name.size = -1;
    }
}

static inline void lip_opcua_variant_set_loctxt(t_lip_opcua_variant *var, const t_lip_opcua_loc_string_ptr *loctxt) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_LOCTEXT;
    var->is_array = false;
    if (loctxt) {
        var->val.loctext = *loctxt;
    } else {
        var->val.loctext.locale.size = -1;
        var->val.loctext.text.size = -1;
    }
}


static inline void lip_opcua_variant_set_extobj(t_lip_opcua_variant *var, const t_lip_opcua_nodeid *nodeid, const void *objptr) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_EXTOBJ;
    var->is_array = false;
    var->val.extobj.node_id = nodeid;
    var->val.extobj.objptr = objptr;
}




static inline void lip_opcua_variant_set_array_int(t_lip_opcua_variant *var, const int32_t *ivals, int32_t items_cnt) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_INT32;
    var->is_array = true;
    var->val.array.ptr1 = ivals;
    var->val.array.items_cnt1 = items_cnt;
    var->val.array.ptr2 = NULL;
    var->val.array.items_cnt2 = 0;
}

static inline void lip_opcua_variant_set_array_uint(t_lip_opcua_variant *var, const uint32_t *ivals, int32_t items_cnt) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_UINT32;
    var->is_array = true;
    var->val.array.ptr1 = ivals;
    var->val.array.items_cnt1 = items_cnt;
    var->val.array.ptr2 = NULL;
    var->val.array.items_cnt2 = 0;
}


static inline void lip_opcua_variant_set_array_int_2buf(t_lip_opcua_variant *var, const int32_t *buf1, int32_t items_cnt1, const int32_t *buf2, int32_t items_cnt2) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_INT32;
    var->is_array = true;
    var->val.array.ptr1 = buf1;
    var->val.array.items_cnt1 = items_cnt1;
    var->val.array.ptr2 = buf2;
    var->val.array.items_cnt2 = items_cnt2;
}


static inline void lip_opcua_variant_set_array_str(t_lip_opcua_variant *var, const t_lip_opcua_string_ptr *str_list, int32_t items_cnt) {
    var->type = LIP_OPCUA_BUILTIN_DATATYPE_STRING;
    var->is_array = true;
    var->val.array.ptr1 = str_list;
    var->val.array.items_cnt1 = items_cnt;
    var->val.array.ptr2 = NULL;
    var->val.array.items_cnt2 = 0;
}

static inline void lip_opcua_variant_set_array_ext(t_lip_opcua_variant *var, t_lip_opcua_builtin_datetype type, const void *vals, int32_t items_cnt) {
    var->type = type;
    var->is_array = true;
    var->val.array.ptr1 = vals;
    var->val.array.items_cnt1 = items_cnt;
    var->val.array.ptr2 = NULL;
    var->val.array.items_cnt2 = 0;
}




bool lip_opcua_variant_is_null(const t_lip_opcua_variant *var);


#endif // LIP_OPCUA_VARIANT_H
