/*
 * Copyright (c) 2022 Huawei Technologies Co.,Ltd.
 *
 * DMS is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * dms_reform_cm_res.c
 *
 *
 * IDENTIFICATION
 *    src/rc/dms_reform_cm_res.c
 *
 * -------------------------------------------------------------------------
 */

#include "dms_reform_cm_res.h"
#include "dms_reform.h"
#include "dms_process.h"
#include "dms_error.h"
#ifdef DMS_TEST
#include "cm_num.h"
#endif

#ifdef DMS_TEST

cm_simulation_t g_cm_simulation;

static config_item_t g_cm_params[] = {
    { CM_REFORMER_ID, CM_TRUE, CM_FALSE, "0", NULL, NULL, "-", "[0, 63]", "INTEGER", NULL, CM_PARAM_REFORMER_ID,
        EFFECT_REBOOT, CFG_INS, NULL, NULL, NULL, NULL },
    { CM_BITMAP_ONLINE, CM_TRUE, CM_FALSE, "3", NULL, NULL, "-", "-", "BIG INTEGER", NULL, CM_PARAM_BITMAP_ONLINE,
        EFFECT_REBOOT, CFG_INS, NULL, NULL, NULL, NULL },
    { CM_VERSION_ONLINE, CM_TRUE, CM_FALSE, "0", NULL, NULL, "-", "-", "BIG INTEGER", NULL, CM_PARAM_VERSION_ONLINE,
        EFFECT_REBOOT, CFG_INS, NULL, NULL, NULL, NULL },
};

static void dms_reform_cm_simulation_refresh(void)
{
    char *value = cm_get_config_value(&g_cm_simulation.config, CM_REFORMER_ID);
    if (cm_str2uint32(value, &g_cm_simulation.params.reformer_id) != CM_SUCCESS) {
        LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to get REFORMER_ID");
    }

    value = cm_get_config_value(&g_cm_simulation.config, CM_BITMAP_ONLINE);
    if (cm_str2uint64(value, &g_cm_simulation.params.bitmap_online) != CM_SUCCESS) {
        LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to get BITMAP_ONLINE");
    }

    value = cm_get_config_value(&g_cm_simulation.config, CM_VERSION_ONLINE);
    if (cm_str2uint64(value, &g_cm_simulation.params.online_version) != CM_SUCCESS) {
        LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to get VERSION_ONLINE");
    }
}


static void dms_reform_cm_simulation_init()
{
    g_cm_simulation.params.reformer_id = CM_INVALID_ID32;
    g_cm_simulation.params.bitmap_online = 0;
    g_cm_simulation.params.online_version = 0;
}

static void dms_reform_cm_simulation_thread(thread_t *thread)
{
    char *cm_config_path = getenv(CM_CONFIG_PATH);
    char cm_config_realpath[CM_MAX_PATH_LEN];
    if (cm_config_path == NULL) {
        LOG_RUN_ERR("[DMS REFORM][cm_simulation]fail to get CM_CONFIG_PATH");
        return;
    }

    int status = realpath_file(cm_config_path, cm_config_realpath, CM_MAX_PATH_LEN);
    if (status != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]invalid cfg dir");
        return;
    }

    cm_set_thread_name("cm_simulation");
    LOG_RUN_INF("[DMS REFORM][cm_simulation]dms_reform_cm_simulation thread started");
    dms_reform_cm_simulation_init();
    mes_block_sighup_signal();

#ifdef OPENGAUSS
    g_dms.callback.dms_thread_init(CM_FALSE, (char **)&thread->reg_data);
#endif

    while (!thread->closed) {
        cm_spin_lock(&g_cm_simulation.lock, NULL);
        status = cm_load_config(g_cm_params, CM_PARAM_COUNT, cm_config_realpath, &g_cm_simulation.config, CM_FALSE);
        if (status != CM_SUCCESS) {
            cm_spin_unlock(&g_cm_simulation.lock);
            LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to load cm simulation");
            cm_sleep(DMS_REFORM_LONG_TIMEOUT);
            continue;
        }
        dms_reform_cm_simulation_refresh();
        cm_spin_unlock(&g_cm_simulation.lock);
        cm_sleep(DMS_REFORM_LONG_TIMEOUT);
    }
}

static void dms_reform_cm_simulation(void)
{
    char *cm_config_path = getenv(CM_CONFIG_PATH);
    char cm_config_realpath[CM_MAX_PATH_LEN];
    if (cm_config_path == NULL) {
        LOG_RUN_ERR("[DMS REFORM][cm_simulation]fail to get CM_CONFIG_PATH");
        return;
    }

    int status = realpath_file(cm_config_path, cm_config_realpath, CM_MAX_PATH_LEN);
    if (status != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]invalid cfg dir");
        return;
    }

    int ret = cm_create_thread(dms_reform_cm_simulation_thread, 0, NULL, &g_cm_simulation.thread);
    if (ret != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM][cm_simulation]fail to create dms_reform_cm_simulation_thread");
    }
}


int dms_reform_cm_res_init(void)
{
#ifdef UT_TEST
    reform_info_t *reform_info = DMS_REFORM_INFO;
    drc_res_ctx_t *ctx = DRC_RES_CTX;

    reform_info->first_reform_finish = CM_TRUE;
    ctx->global_buf_res.drc_accessible_stage = DRC_ACCESS_STAGE_ALL_ACCESS;
    ctx->global_lock_res.drc_accessible_stage = DRC_ACCESS_STAGE_ALL_ACCESS;
    ctx->global_alock_res.drc_accessible_stage = DRC_ACCESS_STAGE_ALL_ACCESS;
    ctx->global_xa_res.drc_accessible_stage = DRC_ACCESS_STAGE_ALL_ACCESS;
    printf("DMS FOR UT TEST.\n");
    LOG_RUN_INF("[DMS REFORM]cm_res_init success(FOR UT TEST)");
#else
    printf("DMS FOR DB TEST.\n");
    dms_reform_cm_simulation();
    LOG_RUN_INF("[DMS REFORM]cm_res_init success(FOR DB TEST)");
#endif
    return DMS_SUCCESS;
}

void dms_reform_cm_simulation_uninit(void)
{
    cm_close_thread(&g_cm_simulation.thread);
}

int dms_cm_res_get_online_version(unsigned long long *online_version)
{
    *online_version = g_cm_simulation.params.online_version;
    return DMS_SUCCESS;
}

static void dms_reform_get_online_list(instance_list_t *list_online)
{
    char *cm_config_path = getenv(CM_CONFIG_PATH);
    char cm_config_realpath[CM_MAX_PATH_LEN];
    if (cm_config_path == NULL) {
        for (uint8 i = 0; i < g_dms.inst_cnt; i++) {
            list_online->inst_id_list[list_online->inst_id_count++] = i;
        }
        return;
    }

    int status = realpath_file(cm_config_path, cm_config_realpath, CM_MAX_PATH_LEN);
    if (status != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]invalid cfg dir");
        return;
    }

    for (uint8 i = 0; i < DMS_MAX_INSTANCES; i++) {
        if (bitmap64_exist(&g_cm_simulation.params.bitmap_online, i)) {
            list_online->inst_id_list[list_online->inst_id_count++] = i;
        }
    }
}

int dms_reform_cm_res_get_inst_stat(instance_list_t *list_online, instance_list_t *list_offline,
    instance_list_t *list_unknown, unsigned long long *online_version)
{
    dms_reform_get_online_list(list_online);
    dms_cm_res_get_online_version(online_version);
    return DMS_SUCCESS;
}

void dms_reform_cm_res_lock(void)
{
    return;
}

void dms_reform_cm_res_unlock(void)
{
    return;
}

int dms_reform_cm_res_get_lock_owner(uint8 *owner_id)
{
    *owner_id = (uint8)g_cm_simulation.params.reformer_id;
    return DMS_SUCCESS;
}

void dms_reform_cm_res_trans_lock(uint8 inst_id)
{
    int status = CM_SUCCESS;
    char buf[CM_BUFLEN_32];
    char cm_config_realpath[CM_MAX_PATH_LEN];

    char *cm_config_path = getenv(CM_CONFIG_PATH);
    if (cm_config_path == NULL) {
        g_cm_simulation.params.reformer_id = inst_id;
        return;
    }

    status = realpath_file(cm_config_path, cm_config_realpath, CM_MAX_PATH_LEN);
    if (status != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]invalid cfg dir");
        return;
    }

    int ret = sprintf_s(buf, CM_BUFLEN_32, "%d", (int)inst_id);
    if (ret == -1) {
        return;
    }

    if (DMS_IS_SHARE_REFORMER) {
        cm_spin_lock(&g_cm_simulation.lock, NULL);
        status = cm_alter_config(&g_cm_simulation.config, CM_REFORMER_ID, buf, CONFIG_SCOPE_BOTH, CM_TRUE);
        if (status != CM_SUCCESS) {
            cm_spin_unlock(&g_cm_simulation.lock);
            LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to modify REFORMER_ID");
            return;
        }
        cm_spin_unlock(&g_cm_simulation.lock);
    }

    cm_spin_lock(&g_cm_simulation.lock, NULL);
    status = cm_load_config(g_cm_params, CM_PARAM_COUNT, cm_config_realpath, &g_cm_simulation.config, CM_FALSE);
    if (status != CM_SUCCESS) {
        cm_spin_unlock(&g_cm_simulation.lock);
        LOG_DEBUG_ERR("[DMS REFORM][cm_simulation]fail to load cm simulation");
    }
    dms_reform_cm_simulation_refresh();
    cm_spin_unlock(&g_cm_simulation.lock);
}

#else

int dms_reform_cm_res_init(void)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    int ret = DMS_SUCCESS;

    ret = cm_res_mgr_init(DMS_LIBCLIENT_PATH, cm_res_mgr, NULL);
    if (ret != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]cm_res_mgr_init, error: %d", ret);
        return ret;
    }

    ret = cm_res_init(cm_res_mgr, g_dms.inst_id + DMS_RESOURCE_ID_BASE, DMS_CM_RES_NAME, NULL);
    if (ret != DMS_SUCCESS) {
        LOG_RUN_ERR("[DMS REFORM]cm_res_init, error: %d", ret);
        return ret;
    }

    LOG_RUN_INF("[DMS REFORM]cm_res_init success");
    return DMS_SUCCESS;
}


int dms_reform_cm_res_get_inst_stat(instance_list_t *list_online, instance_list_t *list_offline,
    instance_list_t *list_unknown, uint64 *online_version)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    cm_res_mem_ctx_t res_mem_ctx;
    if (cm_res_init_memctx(&res_mem_ctx) != CM_SUCCESS) {
        DMS_THROW_ERROR(ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST);
        return ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST;
    }
    cm_res_stat_ptr_t res_stat = cm_res_get_stat(cm_res_mgr, &res_mem_ctx);
    if (res_stat == NULL) {
        cm_res_uninit_memctx(&res_mem_ctx);
        DMS_THROW_ERROR(ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST);
        return ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST;
    }

    uint32 inst_count = 0;
    if (cm_res_get_instance_count(&inst_count, cm_res_mgr, res_stat) != CM_SUCCESS
        || cm_res_get_cm_version(online_version, cm_res_mgr, res_stat) != CM_SUCCESS) {
        cm_res_free_stat(cm_res_mgr, res_stat);
        cm_res_uninit_memctx(&res_mem_ctx);
        DMS_THROW_ERROR(ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST);
        return ERRNO_DMS_REFORM_FAIL_GET_STAT_LIST;
    }
    for (uint32 i = 0; i < inst_count; i++) {
        const cm_res_inst_info_ptr_t inst_info = cm_res_get_instance_info(cm_res_mgr, res_stat, i);
        CM_ASSERT(inst_info != NULL);

        int inst_id = cm_res_get_inst_instance_id(cm_res_mgr, inst_info);
        inst_id -= DMS_RESOURCE_ID_BASE;
        CM_ASSERT(inst_id >= 0 && inst_id < DMS_MAX_INSTANCES);

        int is_work_member = cm_res_get_inst_is_work_member(cm_res_mgr, inst_info);
        if (!is_work_member) {
            continue;
        }

        int stat = cm_res_get_inst_stat(cm_res_mgr, inst_info);
        CM_ASSERT(stat < INST_STAT_COUNT);

        if (stat == INST_STAT_ONLINE) {
            list_online->inst_id_list[list_online->inst_id_count++] = (uint8)inst_id;
        } else if (stat == INST_STAT_OFFLINE) {
            list_offline->inst_id_list[list_offline->inst_id_count++] = (uint8)inst_id;
        } else if (stat == INST_STAT_UNKNOWN) {
            list_unknown->inst_id_list[list_unknown->inst_id_count++] = (uint8)inst_id;
        }
    }

    cm_res_free_stat(cm_res_mgr, res_stat);
    cm_res_uninit_memctx(&res_mem_ctx);
    return DMS_SUCCESS;
}

void dms_reform_cm_res_lock(void)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    (void)cm_res_lock(cm_res_mgr, DMS_REFORMER_LOCK);
}

void dms_reform_cm_res_unlock(void)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    (void)cm_res_unlock(cm_res_mgr, DMS_REFORMER_LOCK);
}

int dms_reform_cm_res_get_lock_owner(uint8 *owner_id)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    uint32 temp_id;
    int ret = DMS_SUCCESS;

    ret = cm_res_get_lock_owner(cm_res_mgr, DMS_REFORMER_LOCK, &temp_id);
    if (ret == CM_RES_TIMEOUT) {
        DMS_THROW_ERROR(ERRNO_DMS_REFORM_GET_LOCK_FAILED);
        return ERRNO_DMS_REFORM_GET_LOCK_FAILED;
    } else if (ret == CM_RES_SUCCESS) {
        *owner_id = (uint8)(temp_id - DMS_RESOURCE_ID_BASE);
    } else {
        *owner_id = CM_INVALID_ID8;
    }
    return DMS_SUCCESS;
}

void dms_reform_cm_res_trans_lock(uint8 inst_id)
{
    cm_res_mgr_t *cm_res_mgr = &g_dms.cm_res_mgr;
    uint32 res_inst_id = inst_id + DMS_RESOURCE_ID_BASE;

    int ret = cm_res_trans_lock(cm_res_mgr, DMS_REFORMER_LOCK, res_inst_id);
    if (ret == DMS_SUCCESS) {
        LOG_RUN_INF("[DMS REFORM]success to trans reformer lock to %d", inst_id);
    }
}
#endif