#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_tree.h"
#include "lip_opcua_server_tree_std_items.h"

#include "lip/app/opcua/lip_opcua_err_codes.h"
#include "lip/app/opcua/lip_opcua_std_nodeids.h"
#include "lip/app/opcua/server/lip_opcua_server_time.h"
#include "lip/app/opcua/server/monitems/lip_opcua_server_monitem.h"
#include "lip/app/opcua/server/lip_opcua_server_params.h"

/* @todo
 * serverarray uri */



#define LIP_OPCUA_FILL_STR_PTR(text)  \
    .data = text, \
    .size = sizeof(text) \


static t_lip_opcua_string_ptr lip_opcua_loc_defs[LIP_OPCUA_LOCALE_CNT] = {
    {LIP_OPCUA_FILL_STR_PTR("")}
};


static const t_lip_opcua_tree_item_descr_variable f_item_const_uint32_descr = {
    LIP_OPCUA_TREE_ITEM_VAR_INIT
    //.typedef_id = &g_lip_opcua_std_type_id_property,
    //.data_type_id = &g_lip_opcua_std_type_id_uint32,
    .cbs.acc.value_read = &lip_opcua_tree_std_item_cb_read_const_uint32
};





#define LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(varname, bname_val, id, val) \
    LIP_OPCUA_TREE_ITEM_DECL_VAR_STD_START(varname, bname_val, id, NULL, f_item_const_uint32_descr) \
    .user.uint = val \
}

t_lip_opcua_err f_item_std_const_uint16_read(const struct st_lip_opcua_tree_item *item, struct st_lip_opcua_session_ctx *session, const t_lip_opcua_item_read_params *params, t_lip_opcua_data_value *val) {
    lip_opcua_variant_set_uint16(&val->value, item->user.uint);
    return LIP_OPCUA_ERR_GOOD;
}

static const t_lip_opcua_tree_item_descr_variable f_item_const_uint16_descr = {
    LIP_OPCUA_TREE_ITEM_VAR_INIT
    //.typedef_id = &g_lip_opcua_std_type_id_property,
    //.data_type_id = &g_lip_opcua_std_type_id_uint16,
    .cbs.acc.value_read = &f_item_std_const_uint16_read
};

#define LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT16(varname, bname_val, id, val) \
    LIP_OPCUA_TREE_ITEM_DECL_VAR_STD_START(varname, bname_val, id, NULL, f_item_const_uint16_descr) \
    .user.uint = val \
};


t_lip_opcua_err f_item_std_const_double_read(const struct st_lip_opcua_tree_item *item, struct st_lip_opcua_session_ctx *session, const t_lip_opcua_item_read_params *params, t_lip_opcua_data_value *val) {
    lip_opcua_variant_set_double(&val->value, item->user.vdouble);
    return LIP_OPCUA_ERR_GOOD;
}

static const t_lip_opcua_tree_item_descr_variable f_item_const_duration_descr = {
    LIP_OPCUA_TREE_ITEM_VAR_INIT
    //.typedef_id = &g_lip_opcua_std_type_id_property,
    //.data_type_id = &g_lip_opcua_std_type_id_duration,
    .cbs.acc.value_read = &f_item_std_const_double_read
};

#define LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_DURATION(varname, bname_val, id, val) \
    LIP_OPCUA_TREE_ITEM_DECL_VAR_STD_START(varname, bname_val, id, NULL, f_item_const_duration_descr) \
    .user.vdouble = val \
};






#if defined LIP_OPCUA_SERVER_PARAM_MAX_ARRAY_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_ARRAY_LEN >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_array_len, "MaxArrayLength", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_ARRAY_LEN, LIP_OPCUA_SERVER_PARAM_MAX_ARRAY_LEN);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_STRING_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_STRING_LEN >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_string_len, "MaxStringLength", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_STRING_LEN, LIP_OPCUA_SERVER_PARAM_MAX_STRING_LEN);
#endif

#if defined LIP_OPCUA_SERVER_PARAM_MAX_BYTESTR_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_BYTESTR_LEN >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_bytestr_len, "MaxByteStringLength", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_BYTESTRING_LEN, LIP_OPCUA_SERVER_PARAM_MAX_BYTESTR_LEN);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_RD && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_RD >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_rd, "MaxNodesPerRead", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_RD, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_RD);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_WR && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_WR >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_wr, "MaxNodesPerWrite", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_WR, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_WR);
#endif

#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_CALL && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_CALL >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_call, "MaxNodesPerMethodCall", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_METHOD_CALL, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_CALL);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_BROWSE && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_BROWSE >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_browse, "MaxNodesPerBrowse", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_BROWSE, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_BROWSE);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_REG_NODES && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_REG_NODES >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_reg_nodes, "MaxNodesPerRegisterNodes", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_REG_NODES, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_REG_NODES);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_TRANS_PATHS && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_TRANS_PATHS >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_trans_paths, "MaxNodesPerTranslateBrowsePathsToNodeIds", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_TRANS_PATHS, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_TRANS_PATHS);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_NODE_MNGMNT && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_NODE_MNGMNT >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_node_mngmnt, "MaxNodesPerNodeManagement", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_NODE_MNGMNT, LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_NODE_MNGMNT);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_MONITEMS_PER_CALL && (LIP_OPCUA_SERVER_PARAM_MAX_MONITEMS_PER_CALL >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_monitems_per_call, "MaxMonitoredItemsPerCall", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_MONITEM_PER_CALL, LIP_OPCUA_SERVER_PARAM_MAX_MONITEMS_PER_CALL);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_DATA_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_DATA_CNT >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_hist_rd_data, "MaxNodesPerHistoryReadData", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_HIST_RD_DATA, LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_DATA_CNT);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_EVT_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_EVT_CNT >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_hist_rd_evts, "MaxNodesPerHistoryReadEvents", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_HIST_RD_EVT, LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_EVT_CNT);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UP_DATA_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UP_DATA_CNT >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_hist_up_data, "MaxNodesPerHistoryUpdateData", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_HIST_UP_DATA, LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UP_DATA_CNT);
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UPEVT_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UPEVT_CNT >= 0)
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT32(f_item_max_nodes_per_hist_up_evts, "MaxNodesPerHistoryUpdateEvents", LIP_OPCUA_NODEID_STD_SERVER_CAPS_OPLIMITS_MAX_NODES_PER_HIST_UP_EVT, LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UPEVT_CNT);
#endif






static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT16(f_item_max_max_browse_cont_pts, "MaxBrowseContinuationPoints", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_BROWSE_CONT_PTS, LIP_OPCUA_SERVER_PARAM_MAX_BROWSE_CONT_PT);
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT16(f_item_max_max_query_cont_pts, "MaxQueryContinuationPoints", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_QUERY_CONT_PTS, LIP_OPCUA_SERVER_PARAM_MAX_QUERY_CONT_PT);
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_UINT16(f_item_max_max_hist_cont_pts, "MaxHistoryContinuationPoints", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MAX_HISTORY_CONT_PTS, LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_CONT_PT);
static LIP_OPCUA_SERVER_TREEITEM_DECL_CONST_DURATION(f_item_min_sample_rate, "MinSupportedSampleRate", LIP_OPCUA_NODEID_STD_SERVER_CAPS_MIN_SUP_SAMPLE_RATE, LIP_OPCUA_SERVER_PARAM_MIN_SAMPLE_RATE_MS);

typedef struct {
    const t_lip_opcua_tree_item *item;
    const t_lip_opcua_tree_item_ref_descr *refs;
} t_lip_opcua_tree_item_extrefs;

static const t_lip_opcua_tree_item *f_std_items[] = {
    &g_lip_opcua_tree_std_item_datatype_boolean,                                    /* 1 */
    &g_lip_opcua_tree_std_item_datatype_sbyte,                                      /* 2 */
    &g_lip_opcua_tree_std_item_datatype_byte,                                       /* 3 */
    &g_lip_opcua_tree_std_item_datatype_int16,                                      /* 4 */
    &g_lip_opcua_tree_std_item_datatype_uint16,                                     /* 5 */
    &g_lip_opcua_tree_std_item_datatype_int32,                                      /* 6 */
    &g_lip_opcua_tree_std_item_datatype_uint32,                                     /* 7 */
    &g_lip_opcua_tree_std_item_datatype_int64,                                      /* 8 */
    &g_lip_opcua_tree_std_item_datatype_uint64,                                     /* 9 */
    &g_lip_opcua_tree_std_item_datatype_float,                                      /* 10 */
    &g_lip_opcua_tree_std_item_datatype_double,                                     /* 11 */
    &g_lip_opcua_tree_std_item_datatype_string,                                     /* 12 */
    &g_lip_opcua_tree_std_item_datatype_date_time,                                  /* 13 */
    &g_lip_opcua_tree_std_item_datatype_guid,                                       /* 14 */
    &g_lip_opcua_tree_std_item_datatype_bytestring,                                 /* 15 */
    &g_lip_opcua_tree_std_item_datatype_xml_element,                                /* 16 */
    &g_lip_opcua_tree_std_item_datatype_nodeid,                                     /* 17 */
    &g_lip_opcua_tree_std_item_datatype_expaneded_nodeid,                           /* 18 */
    &g_lip_opcua_tree_std_item_datatype_status_code,                                /* 19 */
    &g_lip_opcua_tree_std_item_datatype_qualified_name,                             /* 20 */
    &g_lip_opcua_tree_std_item_datatype_localized_text,                             /* 21 */
    &g_lip_opcua_tree_std_item_datatype_struct,                                     /* 22 */
    &g_lip_opcua_tree_std_item_datatype_data_value,                                 /* 23 */
    &g_lip_opcua_tree_std_item_datatype_base,                                       /* 24 */
    &g_lip_opcua_tree_std_item_datatype_diagnostic_info,                            /* 25 */
    &g_lip_opcua_tree_std_item_datatype_number,                                     /* 26 */
    &g_lip_opcua_tree_std_item_datatype_integer,                                    /* 27 */
    &g_lip_opcua_tree_std_item_datatype_uinteger,                                   /* 28 */
    &g_lip_opcua_tree_std_item_datatype_enum,                                       /* 29 */

    &g_lip_opcua_tree_std_item_datatype_decimal,                                    /* 50 */

    &g_lip_opcua_tree_std_item_objtype_base,                                        /* 58 */
    &g_lip_opcua_tree_std_item_objtype_folder,                                      /* 61 */
    &g_lip_opcua_tree_std_item_vartype_base,                                        /* 62 */
    &g_lip_opcua_tree_std_item_vartype_base_data_variable,                          /* 63 */

    &g_lip_opcua_tree_std_item_vartype_property,                                    /* 68 */

    &g_lip_opcua_tree_std_item_objtype_datatype_enc_type,                           /* 76 */
    &g_lip_opcua_tree_std_item_objtype_modeling_rule,                               /* 77 */
    &g_lip_opcua_tree_std_item_modeling_rule_mandatory,                             /* 78 */
    &g_lip_opcua_tree_std_item_modeling_rule_optional,                              /* 80 */
    &g_lip_opcua_tree_std_item_modeling_rule_exposes_its_array,                     /* 83 */
    &g_lip_opcua_tree_std_item_root_folder,                                         /* 84 */
    &g_lip_opcua_tree_std_item_objects_folder,                                      /* 85 */
    &g_lip_opcua_tree_std_item_types_folder,                                        /* 86 */
    &g_lip_opcua_tree_std_item_views_folder,                                        /* 87 */
    &g_lip_opcua_tree_std_item_object_types_folder,                                 /* 88 */
    &g_lip_opcua_tree_std_item_variable_types_folder,                               /* 89 */
    &g_lip_opcua_tree_std_item_data_types_folder,                                   /* 90 */
    &g_lip_opcua_tree_std_item_ref_types_folder,                                    /* 91 */

    &g_lip_opcua_tree_std_item_datatype_access_restriction,                         /* 95 */

    &g_lip_opcua_tree_std_item_datatype_datatype_def,                               /* 97 */
    &g_lip_opcua_tree_std_item_datatype_struct_type,                                /* 98 */
    &g_lip_opcua_tree_std_item_datatype_struct_def,                                 /* 99 */
    &g_lip_opcua_tree_std_item_datatype_enum_def,                                   /* 100 */
    &g_lip_opcua_tree_std_item_datatype_struct_field,                               /* 101 */

    &g_lip_opcua_tree_std_item_datatype_struct_def_enc_bin,                         /* 122 */

    &g_lip_opcua_tree_std_item_datatype_integer_id,                                 /* 288 */
    &g_lip_opcua_tree_std_item_datatype_counter,                                    /* 289 */
    &g_lip_opcua_tree_std_item_datatype_duration,                                   /* 290 */

    &g_lip_opcua_tree_std_item_datatype_utc_time,                                   /* 294 */

    &g_lip_opcua_tree_std_item_datatype_build_info,                                 /* 338 */
    &g_lip_opcua_tree_std_item_datatype_build_info_enc_bin,                         /* 340 */

    &g_lip_opcua_tree_std_item_datatype_server_state,                               /* 852 */

    &g_lip_opcua_tree_std_item_datatype_server_status,                              /* 862 */
    &g_lip_opcua_tree_std_item_datatype_server_status_enc_bin,                      /* 864 */

    &g_lip_opcua_tree_std_item_datatype_range,                                      /* 884 */
    &g_lip_opcua_tree_std_item_datatype_range_enc_bin,                              /* 886 */
    &g_lip_opcua_tree_std_item_datatype_eu_info,                                    /* 887 */
    &g_lip_opcua_tree_std_item_datatype_eu_info_enc_bin,                            /* 889 */

    &g_lip_opcua_tree_std_item_server_type,                                         /* 2004 */
    &g_lip_opcua_tree_std_item_server_type_server_array,                            /* 2005 */
    &g_lip_opcua_tree_std_item_server_type_ns_array,                                /* 2006 */

    &g_lip_opcua_tree_std_item_server_type_service_level,                           /* 2008 */

    &g_lip_opcua_tree_std_item_vartype_server_status,                               /* 2138 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_start_time,                    /* 2139 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_cur_time,                      /* 2140 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_state,                         /* 2141 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info,                    /* 2142 */

    &g_lip_opcua_tree_std_item_server_obj,                                          /* 2253 */
    &g_lip_opcua_tree_std_item_server_server_array,                                 /* 2254 */
    &g_lip_opcua_tree_std_item_server_ns_array,                                     /* 2255 */
    &g_lip_opcua_tree_std_item_server_status,                                       /* 2256 */
    &g_lip_opcua_tree_std_item_server_status_start_time,                            /* 2257 */
    &g_lip_opcua_tree_std_item_server_status_current_time,                          /* 2258 */
    &g_lip_opcua_tree_std_item_server_status_state,                                 /* 2259 */
    &g_lip_opcua_tree_std_item_server_status_build_info,                            /* 2260 */
    &g_lip_opcua_tree_std_item_server_status_build_info_product_name,               /* 2261 */
    &g_lip_opcua_tree_std_item_server_status_build_info_product_uri,                /* 2262 */
    &g_lip_opcua_tree_std_item_server_status_build_info_manufacturer_name,          /* 2263 */
    &g_lip_opcua_tree_std_item_server_status_build_info_software_version,           /* 2264 */
    &g_lip_opcua_tree_std_item_server_status_build_info_build_number,               /* 2265 */
    &g_lip_opcua_tree_std_item_server_status_build_info_build_date,                 /* 2266 */
    &g_lip_opcua_tree_std_item_server_service_level,                                /* 2267 */

    &g_lip_opcua_tree_std_item_vartype_data_item,                                   /* 2365 */
    &g_lip_opcua_tree_std_item_vardecl_data_item_definition,                        /* 2366 */
    &g_lip_opcua_tree_std_item_vardecl_data_item_value_precision,                   /* 2367 */
    &g_lip_opcua_tree_std_item_vartype_analog_item,                                 /* 2368 */
    &g_lip_opcua_tree_std_item_vardecl_analog_item_eu_range,                        /* 2369 */
    &g_lip_opcua_tree_std_item_vartype_discrete_item,                               /* 2372 */
    &g_lip_opcua_tree_std_item_vartype_discrete_two_state,                          /* 2373 */
    &g_lip_opcua_tree_std_item_vardecl_discrete_two_state_false,                    /* 2374 */
    &g_lip_opcua_tree_std_item_vardecl_discrete_two_state_true,                     /* 2375 */
    &g_lip_opcua_tree_std_item_vartype_discrete_multi_state,                        /* 2376 */
    &g_lip_opcua_tree_std_item_vartype_discrete_multi_state_enum_strings,           /* 2377 */


    &g_lip_opcua_tree_std_item_vardecl_server_status_secs_till_shutdown,            /* 2752 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_shutdown_reason,               /* 2753 */

    &g_lip_opcua_tree_std_item_server_status_secs_till_shutdown,                    /* 2992 */
    &g_lip_opcua_tree_std_item_server_status_shutdown_reason,                       /* 2993 */

    &g_lip_opcua_tree_std_item_event_types_folder,                                  /* 3048 */

    &g_lip_opcua_tree_std_item_vartype_build_info,                                  /* 3051 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_product_uri,                      /* 3052 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_manufacturer_name,                /* 3053 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_product_name,                     /* 3054 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_software_version,                 /* 3055 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_build_number,                     /* 3056 */
    &g_lip_opcua_tree_std_item_vardecl_build_info_build_date,                       /* 3057 */


    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_product_uri,        /* 3698 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_manufacturer_name,  /* 3699 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_product_name,       /* 3700 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_software_version,   /* 3701 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_build_number,       /* 3702 */
    &g_lip_opcua_tree_std_item_vardecl_server_status_build_info_build_date,         /* 3703 */

    &g_lip_opcua_tree_std_item_datatype_enum_value,                                 /* 7594 */

    &g_lip_opcua_tree_std_item_datatype_server_state_enum_strings,                  /* 7612 */

    &g_lip_opcua_tree_std_item_datatype_enum_value_enc_bin,                         /* 8251 */

    &g_lip_opcua_tree_std_item_vartype_discrete_multi_state_val,                    /* 11238 */
    &g_lip_opcua_tree_std_item_vartype_discrete_multi_state_val_enum_vals,          /* 11239 */

    &g_lip_opcua_tree_std_item_vartype_discrete_multi_state_val_value_as_text,      /* 11461 */

    &g_lip_opcua_tree_std_item_modeling_rule_optional_placeholder,                  /* 11508 */
    &g_lip_opcua_tree_std_item_modeling_rule_mandatory_placeholder,                 /* 11510 */

    &g_lip_opcua_tree_std_item_vartype_array_item,                                  /* 12021 */
    &g_lip_opcua_tree_std_item_vardecl_array_item_instrument_range,                 /* 12024 */
    &g_lip_opcua_tree_std_item_vardecl_array_item_eu_range,                         /* 12025 */
    &g_lip_opcua_tree_std_item_vardecl_array_item_eu,                               /* 12026 */
    &g_lip_opcua_tree_std_item_vardecl_array_item_title,                            /* 12027 */
    &g_lip_opcua_tree_std_item_vardecl_array_item_axis_scale_type,                  /* 12028 */
    &g_lip_opcua_tree_std_item_vartype_y_array_item,                                /* 12029 */
    &g_lip_opcua_tree_std_item_vardecl_y_array_item_x_axis_def,                     /* 12037 */
    &g_lip_opcua_tree_std_item_vartype_xy_array_item,                               /* 12038 */
    &g_lip_opcua_tree_std_item_vardecl_xy_array_item_x_axis_def,                    /* 12046 */
    &g_lip_opcua_tree_std_item_vartype_image_item,                                  /* 12047 */
    &g_lip_opcua_tree_std_item_vardecl_image_item_x_axis_def,                       /* 12055 */
    &g_lip_opcua_tree_std_item_vardecl_image_item_y_axis_def,                       /* 12056 */
    &g_lip_opcua_tree_std_item_vartype_cube_item,                                   /* 12057 */
    &g_lip_opcua_tree_std_item_vardecl_cube_item_x_axis_def,                        /* 12065 */
    &g_lip_opcua_tree_std_item_vardecl_cube_item_y_axis_def,                        /* 12066 */
    &g_lip_opcua_tree_std_item_vardecl_cube_item_z_axis_def,                        /* 12067 */
    &g_lip_opcua_tree_std_item_vartype_ndim_array_item,                             /* 12068 */
    &g_lip_opcua_tree_std_item_vardecl_ndim_array_item_axis_def,                    /* 12076 */
    &g_lip_opcua_tree_std_item_datatype_axis_scale,                                 /* 12077 */
    &g_lip_opcua_tree_std_item_datatype_axis_scale_enum_strings,                    /* 12078 */
    &g_lip_opcua_tree_std_item_datatype_axis_info,                                  /* 12079 */
    &g_lip_opcua_tree_std_item_datatype_xv,                                         /* 12080 */

    &g_lip_opcua_tree_std_item_datatype_axis_info_enc_bin,                          /* 12089 */
    &g_lip_opcua_tree_std_item_datatype_xv_enc_bin,                                 /* 12090 */

    &g_lip_opcua_tree_std_item_datatype_complex_num,                                /* 12171 */
    &g_lip_opcua_tree_std_item_datatype_double_complex_num,                         /* 12172 */

    &g_lip_opcua_tree_std_item_datatype_complex_num_enc_bin,                        /* 12181 */
    &g_lip_opcua_tree_std_item_datatype_double_complex_num_enc_bin,                 /* 12182 */

    &g_lip_opcua_tree_std_item_struct_type_enum_strings,                            /* 14528 */

    &g_lip_opcua_tree_std_item_datatype_struct_field_enc_bin,                       /* 14844 */

    &g_lip_opcua_tree_std_item_server_type_uris_version,                            /* 15003 */

    &g_lip_opcua_tree_std_item_vartype_base_analog,                                 /* 15318 */

    &g_lip_opcua_tree_std_item_vartype_analog_unit,                                 /* 17497 */
    &g_lip_opcua_tree_std_item_vardecl_analog_unit_eu,                              /* 17502 */

    &g_lip_opcua_tree_std_item_vardecl_base_analog_instrument_range,                /* 17567 */
    &g_lip_opcua_tree_std_item_vardecl_base_analog_eu_range,                        /* 17568 */
    &g_lip_opcua_tree_std_item_vardecl_base_analog_eu,                              /* 17569 */
    &g_lip_opcua_tree_std_item_vartype_analog_unit_range,                           /* 17570 */
    &g_lip_opcua_tree_std_item_vardecl_analog_unit_range_eu,                        /* 17575 */

    &g_lip_opcua_tree_std_item_datatype_index,                                      /* 17588 */

    &g_lip_opcua_tree_std_item_iface_types_folder,                                  /* 17708 */

    &g_lip_opcua_tree_std_item_datatype_version_time,                               /* 20998 */

    &g_lip_opcua_tree_std_item_locations_folder,                                    /* 31915 */

    &g_lip_opcua_tree_std_item_quantities_folder,                                   /* 32530 */




































#if defined LIP_OPCUA_SERVER_PARAM_MAX_ARRAY_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_ARRAY_LEN >= 0)
    &f_item_max_array_len,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_STRING_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_STRING_LEN >= 0)
    &f_item_max_string_len,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_BYTESTR_LEN && (LIP_OPCUA_SERVER_PARAM_MAX_BYTESTR_LEN >= 0)
    &f_item_max_bytestr_len,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_CALL && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_CALL >= 0)
    &f_item_max_nodes_per_call,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_BROWSE && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_BROWSE >= 0)
    &f_item_max_nodes_per_browse,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_RD && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_RD >= 0)
    &f_item_max_nodes_per_rd,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_WR && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_WR >= 0)
    &f_item_max_nodes_per_wr,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_REG_NODES && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_REG_NODES >= 0)
    &f_item_max_nodes_per_reg_nodes,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_TRANS_PATHS && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_TRANS_PATHS >= 0)
    &f_item_max_nodes_per_trans_paths,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_NODE_MNGMNT && (LIP_OPCUA_SERVER_PARAM_MAX_NODES_PER_NODE_MNGMNT >= 0)
    &f_item_max_nodes_per_node_mngmnt,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_MONITEMS_PER_CALL && (LIP_OPCUA_SERVER_PARAM_MAX_MONITEMS_PER_CALL >= 0)
    &f_item_max_monitems_per_call,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_DATA_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_DATA_CNT >= 0)
    &f_item_max_nodes_per_hist_rd_data,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_EVT_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_RD_EVT_CNT >= 0)
    &f_item_max_nodes_per_hist_rd_evts,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UP_DATA_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UP_DATA_CNT >= 0)
    &f_item_max_nodes_per_hist_up_data,
#endif
#if defined LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UPEVT_CNT && (LIP_OPCUA_SERVER_PARAM_MAX_HISTORY_UPEVT_CNT >= 0)
    &f_item_max_nodes_per_hist_up_evts,
#endif
    &f_item_max_max_browse_cont_pts,
    &f_item_max_max_query_cont_pts,
    &f_item_max_max_hist_cont_pts,
    &f_item_min_sample_rate,
};



LIP_OPCUA_SERVER_USER_ITEM_MEM(static const t_lip_opcua_tree_item *f_user_items[LIP_OPCUA_SERVER_USER_ITEM_MAX_CNT]);
static unsigned f_user_item_cnt;
#ifdef LIP_OPCUA_SERVER_ITEM_USER_EXTREFS_MAX_CNT
LIP_OPCUA_SERVER_USER_ITEM_MEM(static t_lip_opcua_tree_item_extrefs f_user_item_extrefs[LIP_OPCUA_SERVER_ITEM_USER_EXTREFS_MAX_CNT]);
static unsigned f_user_item_extrefs_cnt;
#endif



void lip_opcua_server_tree_init(void) {
    f_user_item_cnt = 0;
    lip_opcua_server_obj_init();

}


t_lip_errs lip_opcua_server_tree_item_add(const t_lip_opcua_tree_item *item) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (f_user_item_cnt >= LIP_OPCUA_SERVER_USER_ITEM_MAX_CNT) {
        err = LIP_ERR_OPCUA_SERVER_TREE_FULL;
    } else {
        f_user_items[f_user_item_cnt++] = item;
        if ((item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE)
            || (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_OBJECT)) {
            /* проверка по monitoring items, нет ли подписки на элемент с данным node-id */
            lip_opcua_server_monitem_process_item_add(item);
        }
    }
    return err;
}

t_lip_errs lip_opcua_server_tree_item_remove(const t_lip_opcua_tree_item *item) {
    bool fnd = false;
    for (size_t i = 0; (i < f_user_item_cnt) && !fnd; ++i) {
        const t_lip_opcua_tree_item *check_item = f_user_items[i];
        if (check_item == item) {
            fnd = true;
            /* Для объектов, состояние которых может отслеживаться, оповещаем об удалении элемента */
            if ((item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE)
                || (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_OBJECT)) {
                lip_opcua_server_monitem_process_item_delete(item);
            }
            int move_items = f_user_item_cnt - i - 1;
            if (move_items > 0) {
                memmove(&f_user_items[i], &f_user_items[i+1], move_items * sizeof(f_user_items[0]));
            }
            --f_user_item_cnt;
        }
    }
    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_opcua_server_tree_item_add_extref(const t_lip_opcua_tree_item *item, const t_lip_opcua_tree_item_ref_descr *ref_descr) {
#ifdef LIP_OPCUA_SERVER_ITEM_USER_EXTREFS_MAX_CNT
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (f_user_item_extrefs_cnt >= LIP_OPCUA_SERVER_ITEM_USER_EXTREFS_MAX_CNT) {
        err = LIP_ERR_OPCUA_SERVER_TREE_FULL;
    } else {
        f_user_item_extrefs[f_user_item_extrefs_cnt].item = item;
        f_user_item_extrefs[f_user_item_extrefs_cnt].refs = ref_descr;
        ++f_user_item_extrefs_cnt;
    }
    return err;
#else
    return LIP_ERR_OPCUA_SERVER_TREE_FULL
#endif
}


t_lip_opcua_err lip_opcua_server_tree_get_item(struct st_lip_opcua_session_ctx *session, const t_lip_opcua_nodeid *nodeid, const t_lip_opcua_tree_item **item) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    const t_lip_opcua_tree_item *fnd_item = NULL;
    if (lip_opcua_nodeid_check_is_stdns_int(nodeid)) {
        for (size_t i = 0; (i < sizeof(f_std_items)/sizeof(f_std_items[0])) && !fnd_item; ++i) {
            const t_lip_opcua_tree_item *check_item = f_std_items[i];
            if (check_item->nodeid.intval == nodeid->intval) {
                fnd_item = check_item;
            }
        }

        if (!fnd_item) {
            lip_opcua_server_tree_get_reftype(session, nodeid, &fnd_item);
        }

    } else if ((nodeid->ns_idx > 0) && (nodeid->type == LIP_OPCUA_NODEID_TYPE_INT)) {
        for (size_t i = 0; (i < f_user_item_cnt) && !fnd_item; ++i) {
            const t_lip_opcua_tree_item *check_item = f_user_items[i];
            if ((check_item->nodeid.intval == nodeid->intval) && (check_item->nodeid.ns_idx == nodeid->ns_idx)) {
                fnd_item = check_item;
            }
        }
    }



    if (item) {
        *item = fnd_item;
    }

    if ((err == LIP_OPCUA_ERR_GOOD) && !fnd_item) {
        err = LIP_OPCUA_ERR_BAD_NODEID_UNKNOWN;
    }

    return err;
}

t_lip_opcua_err lip_opcua_server_tree_item_get_display_name(const struct st_lip_opcua_tree_item *item, struct st_lip_opcua_session_ctx *session, t_lip_opcua_loc_string_ptr *locstr) {
    if (item) {
        if (item->display_name) {
            lip_opcua_server_tree_get_loc_text(item->display_name, session, locstr);
        } else {
            locstr->locale = lip_opcua_loc_defs[0];
            locstr->text = item->browse_name.name;
        }
    } else {
        locstr->locale.size = -1;
        locstr->text.size = -1;
    }
    return LIP_ERR_SUCCESS;
}

t_lip_opcua_err lip_opcua_server_tree_get_loc_text(const t_lip_opcua_loc_string_ptr_list *locvar, struct st_lip_opcua_session_ctx *session, t_lip_opcua_loc_string_ptr *locstr) {
    if (locvar) {
        /** @todo select cur locale */
        if (locvar->strlist[0].data && (locvar->strlist[0].size >= 0)) {
            locstr->locale = lip_opcua_loc_defs[0];
            locstr->text = locvar->strlist[0];
        } else {
            locstr->locale.size = -1;
            locstr->text.size = -1;
        }
    } else {
        locstr->locale.size = -1;
        locstr->text.size = -1;
    }
    return LIP_ERR_SUCCESS;
}

t_lip_opcua_err lip_opcua_server_tree_read(struct st_lip_opcua_session_ctx *session, const t_lip_opcua_read_value_id *readid,
                                           t_lip_opcua_ret_tstmp_types tstmps, t_lip_opcua_data_value *value) {
    const t_lip_opcua_tree_item *item = NULL;
    t_lip_opcua_err err = lip_opcua_server_tree_get_item(session, &readid->nodeid, &item);
    if (err == LIP_OPCUA_ERR_GOOD) {
        value->status = LIP_OPCUA_ERR_GOOD;
        if (readid->attr == LIP_OPCUA_ATTR_ID_VALUE) {
            t_lip_opcua_item_read_params params;
            params.tstmps = tstmps;
            err = lip_opcua_server_tree_item_read_value(item, session, &params, value);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_EVENT_NOTIFIER) {
            const t_lip_opcua_tree_item_descr_object *obj_descr = lip_opcua_tree_item_object_descr(item);
            if (!obj_descr) {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            } else {
                lip_opcua_variant_set_uint8(&value->value, obj_descr->evt_notifier_flags);                
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_NODE_ID) {
            lip_opcua_variant_set_nodeid(&value->value, &item->nodeid);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_NODE_CLASS) {
            lip_opcua_variant_set_enum(&value->value, item->class_descr_hdr->node_class);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_BROWSE_NAME) {
            lip_opcua_variant_set_qname(&value->value, &item->browse_name);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_DISPLAY_NAME) {
            t_lip_opcua_loc_string_ptr loctxt;
            lip_opcua_server_tree_item_get_display_name(item, session, &loctxt);
            lip_opcua_variant_set_loctxt(&value->value, &loctxt);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_DESCRIPTION) {
            /** @todo supported! */
            lip_opcua_variant_set_loctxt(&value->value, NULL);
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_DATA_TYPE) {
            const t_lip_opcua_datatype_descr *data_descr = NULL;
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                data_descr = lip_opcua_tree_item_variable_descr(item)->data_descr;
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE_TYPE) {
                data_descr = lip_opcua_tree_item_vartype_descr(item)->data_descr;
            }

            if (data_descr) {
                lip_opcua_variant_set_nodeid(&value->value, data_descr?  data_descr->type_id : NULL);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_VALUE_RANK) {
            const t_lip_opcua_datatype_descr *data_descr = NULL;
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                data_descr = lip_opcua_tree_item_variable_descr(item)->data_descr;
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE_TYPE) {
                data_descr = lip_opcua_tree_item_vartype_descr(item)->data_descr;
            }

            if (data_descr) {
                lip_opcua_variant_set_int32(&value->value, data_descr ?  data_descr->rank : LIP_OPCUA_VARIABLE_RANK_ANY);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_ARRAY_DIMENSIONS) {
            const t_lip_opcua_datatype_descr *data_descr = NULL;
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                data_descr = lip_opcua_tree_item_variable_descr(item)->data_descr;
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE_TYPE) {
                data_descr = lip_opcua_tree_item_vartype_descr(item)->data_descr;
            }

            if (data_descr) {
                const uint32_t *dimensions = NULL;
                int32_t dimensions_cnt = -1;
                /** @todo возможно ввести отдельный callback для ручного получения при желании.
                 *  Сейчас для неопределенного числа возвращаем NULL */
                if (data_descr && (data_descr->rank > 0)) {
                    dimensions_cnt = data_descr->rank;
                    if (dimensions_cnt == 1) {
                        dimensions = &data_descr->dimension.value;
                    } else {
                        dimensions = data_descr->dimension.list;
                    }
                }
                lip_opcua_variant_set_array_uint(&value->value, dimensions, dimensions_cnt);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if ((readid->attr == LIP_OPCUA_ATTR_ID_ACCESS_LEVEL) || (readid->attr == LIP_OPCUA_ATTR_ID_USER_ACCESS_LEVEL)) {
            /** @todo учет прав пользователя - разделение USER_ACCESS_LEVEL от ACCESS_LEVEL */
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                const t_lip_opcua_tree_item_descr_variable *var_descr = lip_opcua_tree_item_variable_descr(item);
                uint8_t acc_lvl = 0;
                if (var_descr->cbs.acc.get_access_lvl) {
                    err = var_descr->cbs.acc.get_access_lvl(item, readid->attr == LIP_OPCUA_ATTR_ID_USER_ACCESS_LEVEL ? session : NULL, &acc_lvl);
                } else {
                    if (var_descr->cbs.acc.value_read) {
                        acc_lvl |= LIP_OPCUA_ACCESS_LEVEL_CUR_RD;
                    }
                    if (var_descr->cbs.acc.value_write) {
                        acc_lvl |= LIP_OPCUA_ACCESS_LEVEL_CUR_WR;
                    }
                    /** @todo - history */
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    lip_opcua_variant_set_uint8(&value->value, acc_lvl);
                }
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_MIN_SAMPLING_INTERVAL) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                t_lip_opcua_duration interval = LIP_OPCUA_SERVER_PARAM_MIN_SAMPLE_RATE_MS;
                const t_lip_opcua_tree_item_descr_variable *var_descr = lip_opcua_tree_item_variable_descr(item);
                if (var_descr->cbs.mon  && var_descr->cbs.mon->get_min_sampling_interval) {
                    err = var_descr->cbs.mon->get_min_sampling_interval(item, &interval);
                }

                if (err == LIP_OPCUA_ERR_GOOD) {
                    lip_opcua_variant_set_double(&value->value, interval);
                }
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_HISTORIZING) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
                /** @todo - поддержка истории */
                lip_opcua_variant_set_bool(&value->value, false);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }

        } else if (readid->attr == LIP_OPCUA_ATTR_ID_IS_ABSTRACT) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_REFERENCE_TYPE) {
                lip_opcua_variant_set_bool(&value->value, lip_opcua_tree_item_reftype_descr(item)->flags & LIP_OPCUA_TREE_ITEM_REFTYPE_FLAG_ABSTRACT);
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_OBJECT_TYPE) {
                lip_opcua_variant_set_bool(&value->value, lip_opcua_tree_item_objtype_descr(item)->is_abstract);
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE_TYPE) {
                lip_opcua_variant_set_bool(&value->value, lip_opcua_tree_item_vartype_descr(item)->is_abstract);
            } else if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_DATA_TYPE) {
                lip_opcua_variant_set_bool(&value->value, lip_opcua_tree_item_datatype_descr(item)->is_abstract);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_SYMMETRIC) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_REFERENCE_TYPE) {
                lip_opcua_variant_set_bool(&value->value, lip_opcua_tree_item_reftype_descr(item)->flags & LIP_OPCUA_TREE_ITEM_REFTYPE_FLAG_SYMMETRIC);
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_INVERSE_NAME) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_REFERENCE_TYPE) {
                const t_lip_opcua_tree_item_descr_reftype *reftype = lip_opcua_tree_item_reftype_descr(item);
                if (!(reftype->flags & LIP_OPCUA_TREE_ITEM_REFTYPE_FLAG_SYMMETRIC)) {
                    t_lip_opcua_loc_string_ptr loctxt;
                    lip_opcua_server_tree_get_loc_text(&reftype->inverse_name, session, &loctxt);
                    lip_opcua_variant_set_loctxt(&value->value, &loctxt);
                } else {
                    err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
                }
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else if (readid->attr == LIP_OPCUA_ATTR_ID_DATA_TYPE_DEFINITION) {
            if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_DATA_TYPE) {
                const t_lip_opcua_tree_item_descr_datatype *datatype_descr = lip_opcua_tree_item_datatype_descr(item);
                if (datatype_descr->typedef_variant == LIP_OPCUA_TREE_ITEM_DATATYPE_TYPEDEF_VARIANT_STRUCT) {
                    lip_opcua_variant_set_extobj(&value->value, &g_lip_opcua_tree_std_item_datatype_struct_def.nodeid, datatype_descr->typedef_struct);
                } else {
                    err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
                }
            } else {
                err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
            }
        } else {
            err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
        }
    }

    if ((err != LIP_OPCUA_ERR_GOOD) && value) {
        value->status = err;
    }

    return err;
}

t_lip_opcua_err lip_opcua_server_tree_write(struct st_lip_opcua_session_ctx *session, const t_lip_opcua_write_value *wrval) {
    const t_lip_opcua_tree_item *item = NULL;
    t_lip_opcua_err err = lip_opcua_server_tree_get_item(session, &wrval->nodeid, &item);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (wrval->attr == LIP_OPCUA_ATTR_ID_VALUE) {
            t_lip_opcua_item_write_params params;
            params.range = wrval->range;
            err = lip_opcua_server_tree_item_write_value(item, session, &params, &wrval->value);
        } else {
            err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_server_tree_item_write_value(const struct st_lip_opcua_tree_item *item, struct st_lip_opcua_session_ctx *session,
                                                       const t_lip_opcua_item_write_params *params, const t_lip_opcua_data_value *value) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
        const t_lip_opcua_tree_item_descr_variable *var_descr = lip_opcua_tree_item_variable_descr(item);
        if (!var_descr->cbs.acc.value_write) {
            err = LIP_OPCUA_ERR_BAD_NOT_WRITABLE;
        } else {
            err = var_descr->cbs.acc.value_write(item, session, params, value);
        }
    } else {
        err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
    }
    return err;
}

t_lip_opcua_err lip_opcua_server_tree_item_update_tstmps(const struct st_lip_opcua_tree_item *item, const t_lip_opcua_item_read_params *params, t_lip_opcua_data_value *value) {
    bool srv_tstmp = (params->tstmps == LIP_OPCUA_RET_TSTMP_TYPE_SERVER) || (params->tstmps == LIP_OPCUA_RET_TSTMP_TYPE_BOTH);
    bool src_tstmp = ((params->tstmps == LIP_OPCUA_RET_TSTMP_TYPE_SOURCE) || (params->tstmps == LIP_OPCUA_RET_TSTMP_TYPE_BOTH)) && (value->src_time.ns100 == 0);
    if (srv_tstmp || src_tstmp) {
        lip_opcua_get_cur_tstmp(&value->srv_time);
        if (src_tstmp) {
            value->src_time = value->srv_time;
        }
    }
    return LIP_OPCUA_ERR_GOOD;
}

t_lip_opcua_err lip_opcua_server_tree_item_read_value(const struct st_lip_opcua_tree_item *item, struct st_lip_opcua_session_ctx *session, const t_lip_opcua_item_read_params *params, t_lip_opcua_data_value *value) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    if (item->class_descr_hdr->node_class == LIP_OPCUA_NODECLASS_VARIABLE) {
        const t_lip_opcua_tree_item_descr_variable *var_descr = lip_opcua_tree_item_variable_descr(item);

        if (!var_descr->cbs.acc.value_read) {
            err = LIP_OPCUA_ERR_BAD_NOT_READABLE;
        } else {

            value->status = var_descr->cbs.acc.value_read(item, session, params, value);
            lip_opcua_server_tree_item_update_tstmps(item, params, value);
        }
    } else {
        err = LIP_OPCUA_ERR_BAD_ATTRIBUTEID_INVALID;
    }
    return err;
}


static bool f_check_pass_reftype(const t_lip_opcua_tree_item *reftype, const t_lip_opcua_tree_item_browse_params *params) {
    bool pass;
    if (params->ref_type == NULL) {
        pass = true;
    } else {
        const t_lip_opcua_tree_item *check_ref = reftype;
        if (params->include_subtypes) {
            while ((check_ref != params->ref_type) && (check_ref != NULL)) {
                check_ref = lip_opcua_tree_item_reftype_descr(check_ref)->base_ref;
            }
        }
        pass = check_ref == params->ref_type;
    }
    return pass;
}

static bool f_check_ref(const t_lip_opcua_tree_item_ref *ref, const t_lip_opcua_tree_item_browse_params *params) {
    bool pass = f_check_pass_reftype(ref->ref_type, params);
    if (pass && !ref->target_node) {
        pass = false;
    }
    if (pass && (params->browse_dir != LIP_OPCUA_BROWSE_DIR_BOTH)) {
        pass = (ref->inverse) ?
                   params->browse_dir == LIP_OPCUA_BROWSE_DIR_INVERSE :
                   params->browse_dir == LIP_OPCUA_BROWSE_DIR_FORWARD;
    }
    if (pass && (params->node_class_mask != 0)) {
        pass = (params->node_class_mask & ref->target_node->class_descr_hdr->node_class) != 0;
    }

    if (pass && (params->qname.name.size > 0)  && (params->qname.name.data != NULL)) {
        pass = ref->target_node && lip_opcua_qname_is_eq(&ref->target_node->browse_name, &params->qname);
    }

    return pass;
}


static uint32_t f_get_refs(const struct st_lip_opcua_tree_item_ref_descr *src_refs, const t_lip_opcua_tree_item_browse_params *params, t_lip_opcua_tree_item_ref *res_refs, uint32_t max_ref_cnt) {
    uint32_t cur_ref_cnt = 0;
    if (src_refs) {
        bool last = false;
        for ( const t_lip_opcua_tree_item_ref_descr *pref = src_refs; !last && (cur_ref_cnt < max_ref_cnt); ++pref) {
            if (pref->rec_type == LIP_OPCUA_TREE_ITEM_REFF_DESCR_TYPE_SINGLE) {
                const t_lip_opcua_tree_item_ref ref = {
                    .inverse = pref->inverse,
                    .ref_type = pref->ref_type,
                    .target_node = pref->target_node,
                };

                if (f_check_ref(&ref, params)) {
                    res_refs[cur_ref_cnt++] = ref;
                }
            } else if (pref->rec_type == LIP_OPCUA_TREE_ITEM_REFF_DESCR_TYPE_MULTIPLE) {
                t_lip_opcua_tree_item_ref ref;
                ref.inverse = pref->inverse;
                ref.ref_type = pref->ref_type;
                for (uint8_t target_idx = 0; target_idx < pref->cnt; ++target_idx) {
                    ref.target_node = pref->target_nodes[target_idx];
                    if (f_check_ref(&ref, params)) {
                        res_refs[cur_ref_cnt++] = ref;
                    }
                }
            } else if (pref->rec_type == LIP_OPCUA_TREE_ITEM_REFF_DESCR_TYPE_REF_DESCR_LIST) {
                cur_ref_cnt += f_get_refs(pref->ref_descr_list, params, &res_refs[cur_ref_cnt], max_ref_cnt - cur_ref_cnt);
            }
            last = LIP_OPCUA_TREE_REF_IS_LAST(*pref);
        }
    }
    return cur_ref_cnt;
}

t_lip_opcua_err lip_opcua_server_tree_item_browse(const t_lip_opcua_tree_item *item, const t_lip_opcua_tree_item_browse_params *params, t_lip_opcua_tree_item_ref *refs, uint32_t *refs_cnt) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    uint32_t max_ref_cnt = *refs_cnt;
    uint32_t cur_ref_cnt = 0;
    cur_ref_cnt = f_get_refs(item->class_descr_hdr->refs, params, refs, max_ref_cnt);
    cur_ref_cnt += f_get_refs(item->refs, params, &refs[cur_ref_cnt], max_ref_cnt - cur_ref_cnt);
#ifdef LIP_OPCUA_SERVER_ITEM_USER_EXTREFS_MAX_CNT
    for (unsigned i = 0; i < f_user_item_extrefs_cnt; ++i) {
        const t_lip_opcua_tree_item_extrefs *extrefs = &f_user_item_extrefs[i];
        if (extrefs->item == item) {
            cur_ref_cnt += f_get_refs(extrefs->refs, params, &refs[cur_ref_cnt], max_ref_cnt - cur_ref_cnt);
        }
    }
#endif
    *refs_cnt = cur_ref_cnt;
    return err;
}

const t_lip_opcua_nodeid *lip_opcua_server_tree_item_get_typedef_id(const struct st_lip_opcua_tree_item *item) {
    const t_lip_opcua_nodeid *ret = NULL;
    if (item) {
        static const t_lip_opcua_tree_item_browse_params params = {
            .ref_type = &g_lip_opcua_tree_std_item_reftype_has_type_definition,
            .include_subtypes = true,
            .browse_dir = LIP_OPCUA_BROWSE_DIR_FORWARD,
            .node_class_mask = LIP_OPCUA_NODECLASS_OBJECT_TYPE | LIP_OPCUA_NODECLASS_VARIABLE_TYPE
        };
        t_lip_opcua_tree_item_ref ref;
        uint32_t ref_cnt = 1;
        t_lip_opcua_err err = lip_opcua_server_tree_item_browse(item, &params, &ref, &ref_cnt);
        if (err == LIP_ERR_SUCCESS) {
            if ((ref_cnt == 1) && ref.target_node) {
                ret = &ref.target_node->nodeid;
            }
        }
    }
    return ret;
}

#endif
