/* Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * openUBMC 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.
 */
#define LUA_LIB

#include <errno.h>
#include <lauxlib.h>
#include <lua.h>
#include <openssl/rand.h>
#include <secure/securec.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>

#include "logging.h"
#include "frudata.h"

#define FRU_FILE_MAX_LEN 2048
#define SET_FIELD_INDEX (-2)
#define FRU_MFGDATA_MAX_LEN 3
#define EOK 0

const gchar *g_chassis_type_desc[] = {
    "Unspecified",        "Other",                "Unknown",
    "Desktop",            "Low Profile Desktop",  "Pizza Box",
    "Mini Tower",         "Tower",                "Portable",
    "LapTop",             "Notebook",             "Hand Held",
    "Docking Station",    "All in One",           "Sub Notebook",
    "Space-saving",       "Lunch Box",            "Main Server Chassis",
    "Expansion Chassis",  "SubChassis",           "Bus Expansion Chassis",
    "Peripheral Chassis", "RAID Chassis",         "Rack Mount Chassis",
    "Sealed-case PC",     "Multi-system Chassis", "Compact PCI",
    "Advanced TCA",       "Blade",                "Blade Enclosure",
    "Tablet",             "Convertible",          "Defachable",
    "IoT Gateway",        "Embedded PC",          "Mini PC",
    "Stick PC"
};

/*
 * Description: 设置系统时间到内核中，并检测是否需要同步给M3核（根据时间跳变大小来区分）
 */
static int l_frudata_init(lua_State *L)
{
    gint32 ret;
    FRU_FILE_S fru_file = { 0 };
    TC_SYSTEM_DESC_S system_data = { 0 };

    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);                  // 参数1：fru_id
    size_t fru_file_len = 0;
    guint8 *fru_file_data = (guint8 *)luaL_checklstring(L, 2, &fru_file_len);   // 参数2：电子标签fru域数据
    size_t system_data_len = 0;
    guint8 *system_buf = (guint8 *)luaL_checklstring(L, 3, &system_data_len);   // 参数3：电子标签system域数据
    guint8 is_sup_dft = (guint8)luaL_checkinteger(L, 4);              // 参数4：是否支持DFT读写电子标签
    guint8 eeprom_format = (guint8)luaL_checkinteger(L, 5);           // 参数5：EEP类型
    guint8 is_sys_area = (guint8)luaL_checkinteger(L, 6);             // 参数6：是否有system域

    debug_log(DLOG_INFO, "[frudata] fru(%d) data init", fru_id);

    ret = memcpy_s(&fru_file, sizeof(FRU_FILE_S), fru_file_data, fru_file_len);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d", __FUNCTION__, ret);
    }
    ret = memcpy_s(&system_data, sizeof(TC_SYSTEM_DESC_S), system_buf, system_data_len);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d", __FUNCTION__, ret);
    }

    fru_init(fru_id, &fru_file, system_data, is_sup_dft, eeprom_format, is_sys_area);
    return 1;
}
static int l_ipmi_write_elabel(lua_State *L)
{
    gint32 ret;
    size_t len = 0;
    DFT_RW_ELABEL_REQ_S write_req = {0};
    write_req.fru_id   = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    write_req.area     = (guint8)luaL_checkinteger(L, 2);  // 参数2：fru_area
    write_req.field    = (guint8)luaL_checkinteger(L, 3);  // 参数3：fru_field
    write_req.offset   = (guint16)luaL_checkinteger(L, 4); // 参数4：写入数据偏移位置
    write_req.len      = (guint8)luaL_checkinteger(L, 5);  // 参数5：写入数据长度
    gchar *data = (gchar *)luaL_checklstring(L, 6, &len);  // 参数6：写入数据
    debug_log(DLOG_NOTICE, "[frudata] fru(%d) ipmi write area(%u) filed(%u) data(%s)",
        write_req.fru_id, write_req.area, write_req.field, data);

    ret = ipmi_dft_write_elabel(&write_req, data);
    if (ret != COMP_CODE_SUCCESS) {
        debug_log(DLOG_ERROR, "%s: ipmi_dft_write_elabel fail, ret = %d", __FUNCTION__, ret);
    }

    lua_pushinteger(L, ret);
    return 1;
}
static int l_get_fru_area(lua_State *L)
{
    char data[FRU_MAX_LEN] = {0};
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    debug_log(DLOG_INFO, "[frudata] get fru(%d) area", fru_id);

    if (get_fru_file_by_id(fru_id, data, FRU_MAX_LEN) != COMP_CODE_SUCCESS) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }

    lua_pushlstring(L, data, FRU_MAX_LEN);
    return 1;
}
static int l_get_fru_mfgdate(lua_State *L)
{
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    char data[FRU_MAX_ELABEL_LEN + 1] = { 0 };

    FRU_PRIV_PROPERTY_S *fru_priv = NULL;
    fru_priv = get_fru_priv_by_fru_id(fru_id);
    if (fru_priv == NULL) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }

    calc_date_time(fru_priv->fru_info->board.mfg_date_time, data, sizeof(data));
    lua_pushstring(L, data);

    return 1;
}
static int l_get_tc_system_area(lua_State *L)
{
    char data[MAX_SYSTEM_AREA_LEN] = {0};
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    debug_log(DLOG_INFO, "[frudata] get fru(%d) tc system area", fru_id);

    if (get_tc_system_area_by_id(fru_id, data, MAX_SYSTEM_AREA_LEN) != COMP_CODE_SUCCESS) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }

    lua_pushlstring(L, data, MAX_SYSTEM_AREA_LEN);
    return 1;
}
static int l_get_fru_info(lua_State *L)
{
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    char data[FRU_MAX_ELABEL_LEN + 1] = { 0 };

    FRU_PRIV_PROPERTY_S *fru_priv = NULL;
    fru_priv = get_fru_priv_by_fru_id(fru_id);
    if (fru_priv == NULL) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }

    guint8 *filed_addr[] = {
        (fru_priv->fru_info->chassis.chassis_part_num.content),
        (fru_priv->fru_info->chassis.chassis_serial_num.content),
        (fru_priv->fru_info->board.board_manufacturer.content),
        (fru_priv->fru_info->board.board_product_name.content),
        (fru_priv->fru_info->board.board_serial_num.content),
        (fru_priv->fru_info->board.board_part_num.content),
        (fru_priv->fru_info->board.file_id.content),
        (fru_priv->fru_info->product.product_manufacturer.content),
        (fru_priv->fru_info->product.product_name.content),
        (fru_priv->fru_info->product.product_part_num.content),
        (fru_priv->fru_info->product.product_version.content),
        (fru_priv->fru_info->product.product_serial_num.content),
        (fru_priv->fru_info->product.product_asset_tag.content),
        (fru_priv->fru_info->product.file_id.content)
    };

    if ((fru_priv->fru_info->has_chassis) &&
        (fru_priv->fru_info->chassis.chassis_type < sizeof(g_chassis_type_desc) / sizeof(gchar *))) {
        lua_pushstring(L, g_chassis_type_desc[fru_priv->fru_info->chassis.chassis_type]);
    } else {
        lua_pushstring(L, data);
    }

    calc_date_time(fru_priv->fru_info->board.mfg_date_time, data, sizeof(data));
    lua_pushstring(L, data);
    for (int i = 0; i < 14; i++) {
        memset_s(data, FRU_MAX_ELABEL_LEN + 1, 0, FRU_MAX_ELABEL_LEN + 1);
        (void)memcpy_s(data, FRU_MAX_ELABEL_LEN, filed_addr[i], FRU_MAX_ELABEL_LEN);
        lua_pushstring(L, data);
    }
    GList *custom_info[] = {(fru_priv->fru_info->chassis.custom_info), (fru_priv->fru_info->board.extension_label),
                            (fru_priv->fru_info->product.custom_info)};
    gchar extension[FRU_EXTENSON_LABEL_MAX_LEN + 1] = {0};
    for (int i = 0; i < 3; i++) {
        memset_s(extension, FRU_EXTENSON_LABEL_MAX_LEN, 0, FRU_EXTENSON_LABEL_MAX_LEN);
        combine_extension_label(custom_info[i], extension, sizeof(extension), NULL);
        lua_pushstring(L, extension);
    }

    return 19;
}

static int l_get_system_info(lua_State *L)
{
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    char data[FRU_MAX_ELABEL_LEN] = {0};
    debug_log(DLOG_INFO, "[frudata] get fru(%d) system info", fru_id);

    FRU_PRIV_PROPERTY_S *fru_priv = NULL;
    fru_priv = get_fru_priv_by_fru_id(fru_id);
    if (fru_priv == NULL) {
        return RET_ERR;
    }
    guint8 *system_addr[] = {
        (fru_priv->system_info->system_area_st.sys_mfg_name.data),
        (fru_priv->system_info->system_area_st.sys_product_name.data),
        (fru_priv->system_info->system_area_st.sys_ver.data),
        (fru_priv->system_info->system_area_st.sys_sn.data),
    };
    gchar *tc_system_addr[] = {
        (fru_priv->system_info->tc_system_desc_st.sys_mfg_name),
        (fru_priv->system_info->tc_system_desc_st.sys_product_name),
        (fru_priv->system_info->tc_system_desc_st.sys_sn),
    };
    if (fru_priv->eeprom_format == EEPROM_FORMAT_TIANCHI) {
        guint8 sys_ver = fru_priv->system_info->tc_system_desc_st.sys_ver;
        lua_pushinteger(L, sys_ver);
        for (int i = 0; i < 3; i++) {
            memset_s(data, FRU_MAX_ELABEL_LEN, 0, FRU_MAX_ELABEL_LEN);
            (void)memcpy_s(data, FRU_MAX_ELABEL_LEN, tc_system_addr[i], FRU_MAX_ELABEL_LEN);
            lua_pushstring(L, data);
        }
        return 4;
    }

    for (int i = 0; i < 4; i++) {
        memset_s(data, FRU_MAX_ELABEL_LEN, 0, FRU_MAX_ELABEL_LEN);
        (void)memcpy_s(data, FRU_MAX_ELABEL_LEN, system_addr[i], FRU_MAX_ELABEL_LEN);
        lua_pushstring(L, data);
    }

    return 4;
}
static int l_clear_fru_info(lua_State *L)
{
    gint32 ret;
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    debug_log(DLOG_NOTICE, "[frudata] clear fru(%d) info", fru_id);

    ret = ipmi_dft_clear_elabel(fru_id);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "%s: ipmi_dft_clear_elabel fail, ret = %d", __FUNCTION__, ret);
    }

    lua_pushinteger(L, ret);
    return 1;
}
static int l_ipmi_read_elabel(lua_State *L)
{
    gchar resp_data[MAX_READ_FRU_DATA_LEN + 2] = {0};
    DFT_RW_ELABEL_REQ_S read_req = { 0 };

    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    guint8 area   = (guint8)luaL_checkinteger(L, 2);  // 参数2：fru_area
    guint8 field  = (guint8)luaL_checkinteger(L, 3);  // 参数3：fru_field
    guint8 len    = (guint8)luaL_checkinteger(L, 4);  // 参数4：读取数据偏移位置
    guint8 offset = (guint8)luaL_checkinteger(L, 5);  // 参数5：读取数据长度
    debug_log(DLOG_INFO, "[frudata] fru(%d) ipmi read elabel (%d-%d-%d-%d) ", fru_id, area, field, len, offset);

    read_req.fru_id = fru_id;
    read_req.area = area;
    read_req.field = field;
    read_req.len = len;
    read_req.offset = offset;
    gint32 ret = ipmi_dft_read_elabel(&read_req, resp_data, MAX_READ_FRU_DATA_LEN);
    lua_pushinteger(L, ret);
    lua_pushinteger(L, resp_data[1]);
    if (ret != COMP_CODE_SUCCESS) {
        return 2;
    }

    if (area == FRU_AREA_BOARDINFO && field == FRU_BOARD_MFGDATE) {
        lua_newtable(L);
        int len_s = (len > FRU_MFGDATA_MAX_LEN || len == 0) ? FRU_MFGDATA_MAX_LEN : len;
        for (int i = 0; i < len_s; i++) {
            lua_newtable(L);
            // 第2个字节是返回值内容
            lua_pushinteger(L, resp_data[i + 2]);
            lua_setfield(L, SET_FIELD_INDEX, "Index");
            lua_rawseti(L, SET_FIELD_INDEX, i + 1);
        }
    } else if (area == FRU_AREA_CHASSISINFO && field == FRU_CHASSIS_TYPE &&
        get_fru_priv_by_fru_id(fru_id)->fru_info->has_chassis) {
        // 第2个字节是返回值内容
        lua_pushinteger(L, resp_data[2]);
    } else {
        // 第2个字节是返回值内容
        lua_pushstring(L, &resp_data[2]);
    }

    return 3;
}

static int l_delete_fru_info(lua_State *L)
{
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    debug_log(DLOG_NOTICE, "[frudata] delete fru(%d) info", fru_id);

    delete_fru_info_by_fru_id(fru_id);
    return 1;
}

static int l_dft_custom(lua_State *L)
{
    gint32 ret;
    gchar operator_log[OPERATOR_LOG] = {0};
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);  // 参数1：fru_id
    guint8 custom_flag = (guint8)luaL_checkinteger(L, 2);  // 参数2：custom_flag

    ret = ipmi_dft_custom(fru_id, custom_flag, operator_log, OPERATOR_LOG);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "%s: ipmi_dft_custom fail, ret = %d", __FUNCTION__, ret);
    }

    lua_pushinteger(L, ret);
    lua_pushstring(L, operator_log);

    // 返回2个值
    return 2;
}

static int l_get_dft_custom(lua_State *L)
{
    gint32 ret;
    gint32 config_parameter = 0;

    ret = ipmi_get_dft_custom_cmd(&config_parameter);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "%s: ipmi_get_dft_custom fail, ret = %d", __FUNCTION__, ret);
    }

    lua_pushinteger(L, ret);
    lua_pushinteger(L, config_parameter);
    // 返回2个值
    return 2;
}

// 标准电子标签读命令
static int l_ipmi_read_frudata(lua_State *L)
{
    guint8 resp_data[MAX_READ_FRU_DATA_LEN + 2] = {0};
    READ_FRU_DATA_REQ_S read_req = { 0 };

    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);
    guint16 offset = (guint16)luaL_checkinteger(L, 2);
    guint8 length = (guint8)luaL_checkinteger(L, 3);
    guint8 chan_type = (guint8)luaL_checkinteger(L, 4);
    debug_log(DLOG_INFO, "[frudata] fru(%d) ipmi read", fru_id);

    read_req.fru_id = fru_id;
    read_req.offset = offset;
    read_req.cnt = length;

    gint32 ret = ipmi_read_frudata(&read_req, resp_data, sizeof(resp_data), chan_type);
    if (ret != COMP_CODE_SUCCESS) {
        debug_log(DLOG_ERROR, "%s: ipmi_read_frudata fail, ret = %d", __FUNCTION__, ret);
    }

    lua_pushinteger(L, ret);
    lua_pushinteger(L, resp_data[1]);
    lua_newtable(L);
    for (int i = 0; i < resp_data[1]; i++) {
        lua_newtable(L);
        // 第2个字节是返回值内容
        lua_pushinteger(L, resp_data[i + 2]);
        lua_setfield(L, SET_FIELD_INDEX, "Index");
        lua_rawseti(L, SET_FIELD_INDEX, i + 1);
    }
    // 返回3个值
    return 3;
}


// 标准电子标签写命令
static int l_ipmi_write_data(lua_State *L)
{
    size_t src_id_len = 0;
    size_t data_len = 0;
    gint32 ret = RET_OK;
    gboolean is_sync = FALSE;

    WRITE_FRU_DATA_REQ_S write_req;
    (void)memset_s(&write_req, sizeof(WRITE_FRU_DATA_REQ_S), 0, sizeof(WRITE_FRU_DATA_REQ_S));

    write_req.time = (gulong)luaL_checkinteger(L, 1);                  // 参数1：当前时间
    gchar *src_id = (gchar *)luaL_checklstring(L, 2, &src_id_len);  // 参数2：消息来源标识
    write_req.fru_id = (guint8)luaL_checkinteger(L, 3);                // 参数3：fru id
    write_req.offset = (guint16)luaL_checkinteger(L, 4);               // 参数4：数据偏移地址
    gchar *data = (gchar *)luaL_checklstring(L, 5, &data_len);      // 参数5：写入的数据

    ret = memcpy_s(write_req.src_id, (sizeof(write_req.src_id) - 1), src_id, src_id_len);
    if (ret != RET_OK) {
        return luaL_error(L, "get src id info failed, fru_id = %d", write_req.fru_id);
    }
    write_req.data_len = (guint32)data_len;
    debug_log(DLOG_DEBUG, "%s: ipmi write data, time(%lu), data_len(%d), src_id(%s)", __FUNCTION__, write_req.time,
        write_req.data_len, write_req.src_id);

    ret = ipmi_standard_write_frudata(&write_req, data, &is_sync);
    lua_pushinteger(L, ret);
    lua_pushinteger(L, is_sync);
    return 2;
}

// 初始化非天池组件 elabel 偏移信息
static int l_get_elabel_eep_len(lua_State *L)
{
    lua_pushinteger(L, ELABEL_INFO_AREA_SIZE);
    lua_pushinteger(L, ELABEL_INFO_EXTEND_SIZE);
    lua_pushinteger(L, ELABEL_EXTEND_INFO_SIZE);
    return 3;
}

// 获取非天池组件 elabel 信息
static int l_get_elabel_info(lua_State *L)
{
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);
    FRU_PRIV_PROPERTY_S *fru_priv = NULL;
    fru_priv = get_fru_priv_by_fru_id(fru_id);
    if (fru_priv == NULL) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }

    char data1[sizeof(ELABEL_INFO_S)] = {0};
    char data2[ELABEL_EXTEND_INFO_SIZE] = {0};
    memcpy_s(data1, sizeof(ELABEL_INFO_S), fru_priv->elabel_info, sizeof(ELABEL_INFO_S));
    memcpy_s(data2, ELABEL_EXTEND_INFO_SIZE,
        &(fru_priv->elabel_info->internal_extend), sizeof(ELABEL_EXTEND_INFO_SIZE));
    lua_pushlstring(L, data1, sizeof(ELABEL_INFO_S));
    lua_pushlstring(L, data2, ELABEL_EXTEND_INFO_SIZE);
    return 2;
}

// 初始化非天池组件 elabel 信息
static int l_elabel_init(lua_State *L)
{
    ELABEL_INFO_S elabel = { 0 };
    size_t elabel_info_len = 0;
    size_t elabel_multi_len = 0;
    size_t elabel_extend_len = 0;
    guint8 fru_id = (guint8)luaL_checkinteger(L, 1);
    guint8 *elabel_info = (guint8 *)luaL_checklstring(L, 2, &elabel_info_len);
    guint8 *elabel_multi = (guint8 *)luaL_checklstring(L, 3, &elabel_multi_len);
    guint8 *elabel_extend = (guint8 *)luaL_checklstring(L, 4, &elabel_extend_len);
    gint32 ret = memcpy_s(&elabel, sizeof(ELABEL_INFO_S), elabel_info, elabel_info_len);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: Get elabel info failed! ret = %d", __FUNCTION__, ret);
    }
    ret = memcpy_s(&elabel.multi_record, sizeof(guint8) * MAX_MULTI_LEN, elabel_multi, elabel_multi_len);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: Get elabel multi record failed! ret = %d", __FUNCTION__, ret);
    }
    ret = memcpy_s(&elabel.internal_extend, sizeof(ELABEL_EXTEND_AREA_S), elabel_extend, elabel_extend_len);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: Get elabel internal extend failed! ret = %d", __FUNCTION__, ret);
    }
    FRU_PRIV_PROPERTY_S *fru_priv = NULL;
    fru_priv = get_fru_priv_by_fru_id(fru_id);
    if (fru_priv == NULL) {
        return luaL_error(L, "get frudata failed, fru_id = %d", fru_id);
    }
    ret = init_elabel_info_v2(fru_priv, &elabel);
    lua_pushinteger(L, ret);
    return 1;
}

LUAMOD_API int luaopen_frudata_intf(lua_State *L)
{
    luaL_checkversion(L);
    luaL_Reg l[] = {
        // clang-format off
        {"frudata_init", l_frudata_init},
        {"ipmi_write_elabel", l_ipmi_write_elabel},
        {"ipmi_read_elabel", l_ipmi_read_elabel},
        {"get_fru_area", l_get_fru_area},
        {"get_fru_mfgdate", l_get_fru_mfgdate},
        {"get_tc_system_area", l_get_tc_system_area},
        {"get_fru_info", l_get_fru_info},
        {"get_system_info", l_get_system_info},
        {"clear_fru_info", l_clear_fru_info},
        {"delete_fru_info", l_delete_fru_info},
        {"dft_custom", l_dft_custom},
        {"get_dft_custom", l_get_dft_custom},
        {"ipmi_read_frudata", l_ipmi_read_frudata},
        {"ipmi_write_frudata", l_ipmi_write_data},
        {"get_elabel_eep_len", l_get_elabel_eep_len},
        {"get_elabel_info", l_get_elabel_info},
        {"elabel_init", l_elabel_init},
        {NULL, NULL},
        // clang-format on
    };
    luaL_newlib(L, l);

    lua_pushinteger(L, G_CHECKSUM_MD5);
    lua_setfield(L, -2, "G_CHECKSUM_MD5");  // -2: table index
    lua_pushinteger(L, G_CHECKSUM_SHA1);
    lua_setfield(L, -2, "G_CHECKSUM_SHA1");  // -2: table index
    lua_pushinteger(L, G_CHECKSUM_SHA256);
    lua_setfield(L, -2, "G_CHECKSUM_SHA256");  // -2: table index
    lua_pushinteger(L, G_CHECKSUM_SHA512);
    lua_setfield(L, -2, "G_CHECKSUM_SHA512");  // -2: table index
    lua_pushinteger(L, G_CHECKSUM_SHA384);
    lua_setfield(L, -2, "G_CHECKSUM_SHA384");  // -2: table index

    return 1;
}