/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC 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.
 * -------------------------------------------------------------------------
 *
 * cm_dbs_map.c
 *
 *
 * IDENTIFICATION
 * src/common/cm_dbs_map.c
 *
 * -------------------------------------------------------------------------
 */
#include "cm_dbs_module.h"
#include "cm_dbs_map.h"
#include "cm_hash.h"
#include "cm_list.h"
#include "cm_log.h"
#include "cm_malloc.h"
#include "securec.h"
#include "cm_latch.h"
#include "cm_spinlock.h"

#define CM_DBS_MAP_HASH_TABLE_SIZE 1024
// 设置为最大规格,1023:最大datafile数量,18000:最大session数量,1024 * 3:预留redo/file类型
#define CM_DBS_MAX_HANDLE_SIZE (1023 * 18000 + 1024 * 3)

typedef struct {
    cm_list_head node;
    int32 handle;
    char obj_name[MAX_DBS_FS_FILE_PATH_LEN + 1];
    uint32 type;
    cm_dbs_map_item_s item;
} cm_dbs_map_value_s;

typedef struct {
    latch_t latch;
    cm_list_head hs_list;
} cm_dbs_map_hs_list;

static struct {
    spinlock_t hdl_seed_lock; // 仅用于分配handle时加锁
    int32 hdl_seed;
    uint32 used_cnt;
    cm_dbs_map_hs_list hs_cache[CM_DBS_MAP_HASH_TABLE_SIZE];
} g_cm_dbs_cache_mgr = { 0 };

typedef enum en_cm_dbs_latch_type {
    LATCH_TYPE_S,
    LATCH_TYPE_X,
} cm_dbs_latch_type_e;

static void cm_dbs_map_latch(cm_dbs_latch_type_e latch_type, uint32 index)
{
    if (latch_type == LATCH_TYPE_S) {
        cm_latch_s(&g_cm_dbs_cache_mgr.hs_cache[index].latch, 0, OG_FALSE, NULL);
    } else {
        cm_latch_x(&g_cm_dbs_cache_mgr.hs_cache[index].latch, 0, NULL);
    }
}

static void cm_dbs_map_unlatch(uint32 index)
{
    cm_unlatch(&g_cm_dbs_cache_mgr.hs_cache[index].latch, NULL);
}

void cm_dbs_map_init(void)
{
    g_cm_dbs_cache_mgr.used_cnt = 0;
    g_cm_dbs_cache_mgr.hdl_seed = CM_DBS_INVALID_HANDLE;
    for (uint32 idx = 0; idx < CM_DBS_MAP_HASH_TABLE_SIZE; idx++) {
        cm_list_init(&g_cm_dbs_cache_mgr.hs_cache[idx].hs_list);
    }
}

void cm_dbs_map_deinit(void)
{
    cm_list_head *list = NULL;
    cm_list_head *node = NULL;
    cm_list_head *tmp = NULL;
    cm_dbs_map_value_s *entry = NULL;
    for (uint32 idx = 0; idx < CM_DBS_MAP_HASH_TABLE_SIZE; idx++) {
        cm_dbs_map_latch(LATCH_MODE_X, idx);
        list = &(g_cm_dbs_cache_mgr.hs_cache[idx].hs_list);
        cm_list_for_each_safe(node, tmp, list) {
            cm_list_remove(node);
            entry = cm_list_entry(node, cm_dbs_map_value_s, node);
            cm_free(entry);
        }
        cm_dbs_map_unlatch(idx);
    }
    g_cm_dbs_cache_mgr.hdl_seed = CM_DBS_INVALID_HANDLE;
}

static uint32 cm_dbs_cache_calc_index_by_handle(int32 handle)
{
    return cm_hash_uint32((uint32)handle, CM_DBS_MAP_HASH_TABLE_SIZE);
}

static cm_dbs_map_value_s *cm_dbs_cache_get_by_index(uint32 index, int32 handle)
{
    cm_list_head *list = &(g_cm_dbs_cache_mgr.hs_cache[index].hs_list);
    cm_list_head *node = NULL;
    cm_dbs_map_value_s *entry = NULL;
    cm_list_for_each(node, list) {
        entry = cm_list_entry(node, cm_dbs_map_value_s, node);
        if (entry->handle == handle) {
            return entry;
        }
    }
    return NULL;
}

static bool32 cm_dbs_map_handle_exist(int32 handle)
{
    uint32 index = cm_dbs_cache_calc_index_by_handle(handle);
    cm_dbs_map_latch(LATCH_TYPE_X, index);
    cm_dbs_map_value_s *value = cm_dbs_cache_get_by_index(index, handle);
    cm_dbs_map_unlatch(index);
    return value != NULL;
}

static int32 cm_dbs_cache_gen_handle(void)
{
    while (OG_TRUE) {
        if (g_cm_dbs_cache_mgr.hdl_seed == INT32_MAX) {
            g_cm_dbs_cache_mgr.hdl_seed = 0;
        } else {
            g_cm_dbs_cache_mgr.hdl_seed++;
        }
        if (cm_dbs_map_handle_exist(g_cm_dbs_cache_mgr.hdl_seed)) {
            continue;
        }
        return g_cm_dbs_cache_mgr.hdl_seed;
    }
}

status_t cm_dbs_map_set(const char *name, cm_dbs_map_item_s *item, int32 *handle, uint32 type)
{
    cm_dbs_map_value_s *value = (cm_dbs_map_value_s *)cm_malloc(sizeof(cm_dbs_map_value_s));
    if (value == NULL) {
        OG_LOG_RUN_ERR("Out of memory(%lu).", sizeof(cm_dbs_map_value_s));
        return OG_ERROR;
    }
    errno_t err = strcpy_s(value->obj_name, sizeof(value->obj_name), name);
    if (err != EOK) {
        cm_free(value);
        OG_LOG_RUN_ERR("Failed(%d) to copy the object name(%s).", err, name);
        return OG_ERROR;
    }
    err = memcpy_s(&value->item, sizeof(value->item), item, sizeof(cm_dbs_map_item_s));
    if (err != EOK) {
        cm_free(value);
        OG_LOG_RUN_ERR("Failed(%d) to copy the object item.", err);
        return OG_ERROR;
    }
    cm_list_init(&(value->node));
    value->type = type;
    cm_spin_lock(&g_cm_dbs_cache_mgr.hdl_seed_lock, NULL);
    if (g_cm_dbs_cache_mgr.used_cnt > CM_DBS_MAX_HANDLE_SIZE) {
        cm_spin_unlock(&g_cm_dbs_cache_mgr.hdl_seed_lock);
        cm_free(value);
        OG_LOG_RUN_ERR("the dbs file handles is used up, max num %u.", CM_DBS_MAX_HANDLE_SIZE);
        return OG_ERROR;
    }
    g_cm_dbs_cache_mgr.used_cnt++;
    value->handle = cm_dbs_cache_gen_handle();
    cm_spin_unlock(&g_cm_dbs_cache_mgr.hdl_seed_lock);

    uint32 index = cm_dbs_cache_calc_index_by_handle(value->handle);
    cm_dbs_map_latch(LATCH_TYPE_X, index);
    cm_list_add(&(value->node), &(g_cm_dbs_cache_mgr.hs_cache[index].hs_list));
    *handle = value->handle;
    cm_dbs_map_unlatch(index);
    return OG_SUCCESS;
}

status_t cm_dbs_map_get(int32 handle, cm_dbs_map_item_s *item)
{
    uint32 index = cm_dbs_cache_calc_index_by_handle(handle);
    cm_dbs_map_latch(LATCH_TYPE_S, index);
    cm_dbs_map_value_s *value = cm_dbs_cache_get_by_index(index, handle);
    if (value == NULL) {
        cm_dbs_map_unlatch(index);
        OG_LOG_RUN_ERR("The cache at handle(%d) is invalid.", handle);
        return OG_ERROR;
    }
    errno_t err = memcpy_s(item, sizeof(cm_dbs_map_item_s), &value->item, sizeof(value->item));
    cm_dbs_map_unlatch(index);
    if (err != EOK) {
        OG_LOG_RUN_ERR("Failed(%d) to copy the object item.", err);
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

void cm_dbs_map_remove(int32 handle)
{
    uint32 index = cm_dbs_cache_calc_index_by_handle(handle);
    cm_dbs_map_latch(LATCH_TYPE_X, index);
    cm_dbs_map_value_s *value = cm_dbs_cache_get_by_index(index, handle);
    if (value == NULL) {
        cm_dbs_map_unlatch(index);
        OG_LOG_RUN_WAR("The cache at handle(%d) is invalid.", handle);
        return;
    }
    cm_list_remove(&(value->node));
    cm_free(value);
    cm_dbs_map_unlatch(index);

    cm_spin_lock(&g_cm_dbs_cache_mgr.hdl_seed_lock, NULL);
    g_cm_dbs_cache_mgr.used_cnt--;
    cm_spin_unlock(&g_cm_dbs_cache_mgr.hdl_seed_lock);
}

bool32 cm_dbs_map_exist(const char *name, uint32 type)
{
    cm_list_head *list = NULL;
    cm_list_head *node = NULL;
    cm_dbs_map_value_s *entry = NULL;
    for (uint32 idx = 0; idx < CM_DBS_MAP_HASH_TABLE_SIZE; idx++) {
        cm_dbs_map_latch(LATCH_TYPE_S, idx);
        list = &(g_cm_dbs_cache_mgr.hs_cache[idx].hs_list);
        cm_list_for_each(node, list)
        {
            entry = cm_list_entry(node, cm_dbs_map_value_s, node);
            if (strcmp(name, entry->obj_name) == 0 && type == entry->type) {
                cm_dbs_map_unlatch(idx);
                return OG_TRUE;
            }
        }
        cm_dbs_map_unlatch(idx);
    }
    return OG_FALSE;
}

void cm_dbs_map_update(int32 handle, cm_dbs_map_item_s *item)
{
    uint32 index = cm_dbs_cache_calc_index_by_handle(handle);
    cm_dbs_map_latch(LATCH_TYPE_X, index);
    cm_dbs_map_value_s *value = cm_dbs_cache_get_by_index(index, handle);
    if (value == NULL) {
        cm_dbs_map_unlatch(index);
        OG_LOG_RUN_WAR("The cache at handle(%d) is invalid.", handle);
        return;
    }
    errno_t err = memcpy_s(&value->item, sizeof(value->item), item, sizeof(cm_dbs_map_item_s));
    cm_dbs_map_unlatch(index);
    if (err != EOK) {
        OG_LOG_RUN_WAR("Failed(%d) to update cache at handle(%d).", err, handle);
        return;
    }
}

void cm_dbs_map_get_name(int32 handle, char *name, int32 size)
{
    uint32 index = cm_dbs_cache_calc_index_by_handle(handle);
    cm_dbs_map_latch(LATCH_TYPE_S, index);
    cm_dbs_map_value_s *value = cm_dbs_cache_get_by_index(index, handle);
    if (value == NULL) {
        cm_dbs_map_unlatch(index);
        OG_LOG_RUN_WAR("The cache at handle(%d) is invalid.", handle);
        return;
    }
    errno_t err = strcpy_s(name, size, value->obj_name);
    cm_dbs_map_unlatch(index);
    if (err != EOK) {
        OG_LOG_RUN_ERR("Failed(%d) to copy the object name(%s).", err, name);
        return;
    }
}