#include "virtual_bau.h"
#include "opcua/ua-server.h"
#include "prot_transf.h"
#include "tag.h"
#include "collector-api.h"
#include "common.h"
#include "bms_map.h"
#include "multilanguage.h"
#include "../ems/lc.h"
#include "../cc_lc_com/define.h"
typedef struct bau_map_desc
{
    int type;
    const char *no;
    const char *description;
    const char *displayName;
    const char *desc;
} bau_node_map_desc;

typedef struct bau_map_ext_info
{
    tag_t **tag_arr;
    int tag_sum;
} bau_map_ext_info;

bau_node_map_desc bau_desc[] = {
    {.type = 1, .no = "SOC", .description = "SOC", .displayName = "RackSOC(%)"},
    {.type = 0, .no = "RackMaxVoltage", .description = "RackMaxVoltage", .displayName = "Rack单体电压最高值(mV)"},
    {.type = 0, .no = "RackMinVoltage", .description = "RackMinVoltage", .displayName = "Rack单体电压最低值(mV)"},
    {.type = 0, .no = "CellVolLowWarn2", .description = "CellVolLowWarn2", .displayName = "单体欠压二级报警"},
    {.type = 0, .no = "CellVolLowWarn3", .description = "CellVolLowWarn3", .displayName = "单体欠压三级报警"},
    {.type = 0, .no = "RackVolLowWarn2", .description = "RackVolLowWarn2", .displayName = "总电压欠压二级报警"},
    {.type = 0, .no = "RackVolLowWarn3", .description = "RackVolLowWarn3", .displayName = "总电压欠压三级报警"},
    {.type = 0, .no = "CellVolHighWarn2", .description = "CellVolHighWarn2", .displayName = "单体过压二级报警"},
    {.type = 0, .no = "CellVolHighWarn3", .description = "CellVolHighWarn3", .displayName = "单体过压三级报警"},
    {.type = 0, .no = "RackVolHighWarn2", .description = "RackVolHighWarn2", .displayName = "总电压过压二级报警"},
    {.type = 0, .no = "RackVolHighWarn3", .description = "RackVolHighWarn3", .displayName = "总电压过压三级报警"},
    {.type = 0, .no = "RackMaxChargeCur", .description = "RackMaxChargeCur", .displayName = "BMS最大充电电流"},
    {.type = 0, .no = "RackMaxDisChaCur", .description = "RackMaxDisChaCur", .displayName = "BMS最大放电电流"},
    {.type = 0, .no = "RackVoltage", .description = "RackVoltage", .displayName = "Rack电池总电压"},
    {.type = 0, .no = "Online", .description = "Online", .displayName = "在线", .desc = "0:离线;1:在线;"},
};

static int bau_ex_fun(void *par)
{
    return 0;
}

static int add_tag_to_ua(proto_forward_t *var, UA_NodeId parent_id, device_t *dev, tag_t *tag)
{
    UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
    UA_Server *server = ua_ser_para->ua_server;
    // 注册UA的回调
    UA_NodeId_init(&tag->node_id);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.description = UA_LOCALIZEDTEXT("zh_CN", tag->name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", tag->disp_name);
    attr.accessLevel = UA_ACCESSLEVELMASK_READ; // | UA_ACCESSLEVELMASK_WRITE;

    UA_Double actposValue_d = 0.0f; // 默认值
    UA_Int32 actposValue_i = 0;     // 默认值
    void *value1 = NULL;
    if (tag->data_type == TYPE_TAG_FLOAT)
    {
        tag->read_cache.to_float = actposValue_d; // 默认值
        attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
        value1 = &actposValue_d;
        UA_Variant_setScalar(&attr.value, value1, &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    else
    {
        //
        if (tag->desc != NULL)
        {
            char enum_name[DEV_NO_LEN + TAG_NAME_LEN + 10] = {0};
            snprintf(enum_name, sizeof(enum_name), "%s_enum", tag->dev_tag_name);
            tag->ua_enum_type = ua_creat_custom_enum(server, enum_name, tag->desc);
        }
        //
        tag->read_cache.to_int = actposValue_i; // 默认值
        attr.dataType = (tag->ua_enum_type == NULL) ? UA_TYPES[UA_TYPES_INT32].typeId : ((ua_custom_enum_type *)tag->ua_enum_type)->typeId;
        value1 = &actposValue_i;
        UA_Variant_setScalar(&attr.value, value1, &UA_TYPES[UA_TYPES_INT32]);
    }

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, tag->dev_tag_name),
                                                     parent_id,
                                                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, tag->name),
                                                     UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, /*timeDataSource,*/ tag, &tag->node_id);
    if (retval != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING,
                     "tag:%s  UA_Server_addVariableNode %s", tag->dev_tag_name, UA_StatusCode_name(retval));
    }

    addValueCallbackToVariable(server, tag->node_id);
    return 0;
}

static tag_t *new_tag(channel_t *ch, device_t *dev, const char *bau_no, bau_node_map_desc *tag_par)
{
    proto_forward_t *var = (proto_forward_t *)ch->var;
    tag_t *tag = calloc(1, sizeof(tag_t));

    snprintf(tag->name, sizeof(tag->name), "%s", tag_par->description);
    snprintf(tag->disp_name, sizeof(tag->disp_name), "%s", tag_par->displayName);
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", bau_no, tag_par->no);
    tag->desc = (tag_par->desc != NULL) ? strdup(tag_par->desc) : NULL;
    tag->chan_ptr = ch;
    tag->dev_ptr = dev;
    tag->data_type = tag_par->type;
    tag->history_type = HISTORY_TYPE_OFF;
    tag->no_fetch = 1;
    tag->proto_ptr = NULL;
    tag->funptr = NULL;

    tag->ua_enum_type = NULL;
    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        dev->adp->tag = NULL;
        free(tag);
        return NULL;
    }
    return tag;
}


int register_vir_bau_info(void *chan_ptr, void *dev, const char *curdev_no, const char *bau_no, const char *displayName)
{
    channel_t *ch = (channel_t *)chan_ptr;
    proto_forward_t *var = (proto_forward_t *)ch->var;
    device_t *cur_dev = (device_t *)dev;
    UA_NodeId node_id = ua_server_add_single_dev_node((UA_SERVER_PARAM *)var->ua_server_param, cur_dev, bau_no, bau_no, displayName);
    //
    tag_t *tag = NULL;
    //
    int len = sizeof(bau_desc) / sizeof(bau_desc[0]);
    bau_map_ext_info *ext_info = (void *)calloc(1, sizeof(bau_map_ext_info));
    ext_info->tag_arr = (void *)calloc(len, sizeof(tag_t *));
    //
    for (int i = 0; i < len; i++)
    {
        tag = new_tag(ch, cur_dev, bau_no, &bau_desc[i]);
        add_tag_to_ua(var, node_id, cur_dev, tag);
        ext_info->tag_arr[i] = tag;
    }
    ext_info->tag_sum = len;
    cur_dev->ext_par = ext_info;
    cur_dev->ext_fun = bau_ex_fun;

    return 0;
}

/*---------------------------------------------------------------------------------------------------------------------------------------------------------*/

typedef struct bcu_dev_info
{
    char no[DEV_NO_LEN];
    char group[DEV_NAME_LEN];
} bcu_dev_info;
static bcu_dev_info *g_bcu_dev_arr = NULL;
static int g_bcu_dev_capacity = 0;
static int bcu_dev_num = 0;

//avrage
static const char *bau_node_ave_sync_tab[] = {
    CURRENT_SOC,
};

//sum
static const char *bau_node_sum_sync_tab[] = {
    RACK_MAX_CHARGE_CUR,
    RACK_MAX_DISCHA_CUR,
};

//or
static const char *bau_node_or_sync_tab[] = {
    CELL_UNDER_VOL2,
    CELL_UNDER_VOL3,
    RACK_UNDER_VOL2,
    RACK_UNDER_VOL3,
    CELL_OVER_VOL2,
    CELL_OVER_VOL3,
    RACK_OVER_VOL2,
    RACK_OVER_VOL3,
};

static const char *bau_node_and_sync_tab[] = {
    STATE_ONLINE,
};

static const char *bau_node_vol_charg_power_tab[] = {
    RACK_VOLTAGE,
};


static const char *bau_node_max_sync_tab[] = {
    RACK_MAX_VOLT,
};

static const char *bau_node_min_sync_tab[] = {
    RACK_MIN_VOLT,
};

int get_bcu_dev_info(const char* no, const char* group) {
    for (int i = 0; i < bcu_dev_num; i++) {
        if (strcmp(g_bcu_dev_arr[i].no, no) == 0 && strcmp(g_bcu_dev_arr[i].group, group) == 0)
            return bcu_dev_num;
    }
    if (bcu_dev_num >= g_bcu_dev_capacity) {
        int new_capacity = g_bcu_dev_capacity == 0 ? 30 : g_bcu_dev_capacity * 2;
        bcu_dev_info *new_arr = realloc(g_bcu_dev_arr, new_capacity * sizeof(bcu_dev_info));
        if (new_arr == NULL) {
            /* 内存分配失败 */
            proto_syslog(LOG_ERR, "realloc failed for g_bcu_dev_arr, capacity=%d", new_capacity);
            return bcu_dev_num;
        }
        g_bcu_dev_arr = new_arr;
        g_bcu_dev_capacity = new_capacity;
    }
    snprintf(g_bcu_dev_arr[bcu_dev_num].no, DEV_NO_LEN, "%s", no);
    snprintf(g_bcu_dev_arr[bcu_dev_num].group, DEV_NAME_LEN, "%s", group);
    bcu_dev_num++;
    return bcu_dev_num;
}

static void bau_node_avg_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int sum = 0, count = 0, avrage = 0;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                sum += dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name);
            }
            else
            {
                sum += dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name);
            }
            count++;
        }
    }

    avrage = sum / count;

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, avrage);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, avrage);
    }
    free(set_tag_name);
}

static void bau_node_sum_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int sum = 0;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                sum += dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name);
            }
            else
            {
                sum += dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name);
            }
        }
    }


    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, sum);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, sum);
    }
    free(set_tag_name);
}

static void bau_node_or_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int or_result = 0;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                if (dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name))
                {
                    or_result = 1;
                    break;
                }
            }
            else
            {
                if (dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name) != 0.0f)
                {
                    or_result = 1;
                    break;
                }
            }
        }
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, or_result);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, or_result);
    }
    free(set_tag_name);
}

static void bau_node_and_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int and_result = 1;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                if (!dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name))
                {
                    and_result = 0;
                    break;
                }
            }
            else
            {
                if (dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name) == 0.0f)
                {
                    and_result = 0;
                    break;
                }
            }
        }
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, and_result);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, and_result);
    }
    free(set_tag_name);
}

static void bau_node_vol_charg_power_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    double voltage = 0.0;
    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                voltage = dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name);
            }
            else
            {
                voltage = dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name);
            }
        }
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, voltage);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, voltage);
    }
    free(set_tag_name);
}


static void bau_node_max_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int first = 1;
    int max_i = 0;
    float max_f = 0.0f;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                int value = dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name);
                if (first || value > max_i)
                {
                    max_i = value;
                    first = 0;
                }
            }
            else
            {
                float value = dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name);
                if (first || value > max_f)
                {
                    max_f = value;
                    first = 0;
                }
            }
        }
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, max_i);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, max_f);
    }
    free(set_tag_name);
}

static void bau_node_min_sync_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    const char *bau_no = ((bcu_dev_info *)par)->group;
    int first = 1;
    int min_i = 0;
    float min_f = 0.0f;

    for (int i = 0; i < bcu_dev_num; i++)
    {
        if (strcmp(bau_no, g_bcu_dev_arr[i].group) == 0)
        {
            if (data_value->type == 0)
            {
                int value = dev_get_dev_tag_int(g_bcu_dev_arr[i].no, set_tag_name);
                if (first || value < min_i)
                {
                    min_i = value;
                    first = 0;
                }
            }
            else
            {
                float value = dev_get_dev_tag_float(g_bcu_dev_arr[i].no, set_tag_name);
                if (first || value < min_f)
                {
                    min_f = value;
                    first = 0;
                }
            }
        }
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(bau_no, set_tag_name, min_i);
    }
    else
    {
        dev_set_dev_tag_float(bau_no, set_tag_name, min_f);
    }
    free(set_tag_name);
}

static int bau_collector_variant_monitor_add(pcs_ctrl_var_t *var, const char **node, size_t size, ems_data_par_cb cb)
{
    char node_name[TAG_NAME_LEN] = {};
    for (int i = 0; i < bcu_dev_num; ++i) {
        for (int j = 0; j < size; j++)
        {
            snprintf(node_name, sizeof(node_name), "%s.%s", g_bcu_dev_arr[i].no, node[j]);
            //
            if (0 != collector_variant_monitor_add_one_with_par(var, node_name, cb, &g_bcu_dev_arr[i], 0))
            {
                ems_syslog(LOG_ERR, "BCU add monitor fail:%s", node_name);
            }
            else
            {
                ems_syslog(LOG_NOTICE, "BCU add monitor success:%s", node_name);
            }
        }
    }

    return 0;
}



int bau_variant_cb_register(void *var) {
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_ave_sync_tab, sizeof(bau_node_ave_sync_tab) / sizeof(char *), bau_node_avg_sync_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_sum_sync_tab, sizeof(bau_node_sum_sync_tab) / sizeof(char *), bau_node_sum_sync_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_or_sync_tab, sizeof(bau_node_or_sync_tab) / sizeof(char *), bau_node_or_sync_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_and_sync_tab, sizeof(bau_node_and_sync_tab) / sizeof(char *), bau_node_and_sync_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_vol_charg_power_tab, sizeof(bau_node_vol_charg_power_tab) / sizeof(char *), bau_node_vol_charg_power_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_max_sync_tab, sizeof(bau_node_max_sync_tab) / sizeof(char *), bau_node_max_sync_tab_cb);
    bau_collector_variant_monitor_add((pcs_ctrl_var_t *)var, bau_node_min_sync_tab, sizeof(bau_node_min_sync_tab) / sizeof(char *), bau_node_min_sync_tab_cb);
    
    return 0;
}