/* 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.
 */
#include "l_ctrl.h"

#include "ctrl.h"
#include "l_callbacks.h"
#include "l_sml_adapter.h"

namespace sml {

register_ctrl_oob::register_ctrl_oob()
{
    reset_zero(this);
}

register_ctrl_oob::~register_ctrl_oob()
{
}

void register_ctrl_oob::register_to(lua_State *L, luawrap::stack_table &t)
{
    luawrap::restore_stack_top _s(L);

    auto cls = luawrap::lua_class<register_ctrl_oob>(L);
    cls.ctor<>();
    // .def_readwrite("在lua中访问的变量名", &要访问的变量)，用于公有非成员变量的访问
    cls.def_readwrite("i_controller_index", &register_ctrl_oob::i_controller_index);
    cls.def_readwrite("i_controller_typeid", &register_ctrl_oob::i_controller_typeid);
    cls.def_readwrite("i_oob_operate", &register_ctrl_oob::i_oob_operate);
    // .def_property("给lua访问的变量名"， &访问时调用的函数，&设置时调用的函数)
    cls.def_property("i_oob_interface_type", c_func_wrap(L, register_ctrl_oob::get_interface_type),
                     c_func_wrap(L, register_ctrl_oob::set_interface_type));
    cls.def_property("mctp_eid", c_func_wrap(L, register_ctrl_oob::get_eid),
                     c_func_wrap(L, register_ctrl_oob::set_eid));
    cls.def_property("mctp_phy_addr", c_func_wrap(L, register_ctrl_oob::get_phy_addr),
                     c_func_wrap(L, register_ctrl_oob::set_phy_addr));
    // .def("给lua调用的方法名"， &实际操作的函数)
    cls.def("register_controller", c_func_wrap(L, register_ctrl_oob::register_controller));
    cls.def("unregister_controller", c_func_wrap(L, register_ctrl_oob::unregister_controller));
    cls.def("register_sml_adapter_function", c_func_wrap(L, register_ctrl_oob::register_sml_adapter_function));

    t.set("register_ctrl_oob", cls);
}

uint32_t register_ctrl_oob::register_controller(lua_State *L)
{
    return l_sml_adapter::sml_adapter_register_controller(this);
}

uint32_t register_ctrl_oob::unregister_controller(lua_State *L)
{
    return l_sml_adapter::sml_adapter_unregister_controller(this);
}

void register_ctrl_oob::register_sml_adapter_function(lua_State *L)
{
    l_sml_adapter::register_sml_adapter_function(this);
}

void register_ctrl_oob::set_interface_type(lua_State *L, int interface_type)
{
    i_oob_interface_type = interface_type;
    if (i_oob_interface_type == OOB_TYPE_OVER_PCIE) {
        // 将mctp_writeread_cb方法作为函数指针注册到smlib库
        register_info.over_mctp.mctp_writeread_func = l_callbacks::mctp_writeread_cb;
    } else if (i_oob_interface_type == OOB_TYPE_OVER_I2C) {
        register_info.over_i2c.i2c_write_func     = l_callbacks::i2c_write_cb;
        register_info.over_i2c.i2c_writeread_func = l_callbacks::i2c_writeread_cb;
    }
}

int register_ctrl_oob::get_interface_type() const
{
    return i_oob_interface_type;
}

static int32_t update_ctrl_info(lua_State *L, SML_CTRL_BASIC_INFO_S *info, uint8_t ctrl_index)
{
    if (auto ret = l_sml_adapter::sml_adapter_get_ctrl_info(ctrl_index, info); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    return 1;
}

static luawrap::n_ret get_ctrl_pd_list(lua_State *L, uint8_t ctrl_index)
{
    SML_CTRL_PD_LIST_S ctrl;
    reset_zero(&ctrl);
    ctrl.i_controller_index = ctrl_index;
    if (auto ret = l_sml_adapter::sml_adapter_get_ctrl_pd_list(&ctrl); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto t = luawrap::stack::new_table(L);
    for (auto i = 0; i < ctrl.o_pd_count; ++i) {
        luawrap::restore_stack_top _s(L);

        auto pd = luawrap::stack::new_table(L, 0, 3);
        pd.set("device_id", ctrl.o_pd_deviceIDs[i]);
        pd.set("slot_num", ctrl.o_pd_slot_num[i]);
        pd.set("enclosure_id", ctrl.o_pd_enclosure_id[i]);
        t.set(i + 1, pd);
    }
    return 1;
}

static luawrap::n_ret get_ctrl_ld_list(lua_State *L, uint8_t ctrl_index)
{
    SML_CTRL_LD_LIST_S ctrl;
    reset_zero(&ctrl);
    ctrl.i_controller_index = ctrl_index;
    if (auto ret = l_sml_adapter::sml_adapter_get_ctrl_ld_list(&ctrl); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto t = luawrap::stack::new_table(L);
    for (auto i = 0; i < ctrl.o_ld_count; ++i) {
        t.set(i + 1, ctrl.o_ld_targetIDs[i]);
    }
    return 1;
}

static luawrap::n_ret get_ctrl_array_list(lua_State *L, uint8_t ctrl_index)
{
    SML_CTRL_ARRAY_LIST_S ctl;
    reset_zero(&ctl);
    ctl.i_controller_index = ctrl_index;
    if (auto ret = l_sml_adapter::sml_adapter_get_ctrl_array_list(&ctl); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto t = luawrap::stack::new_table(L);
    for (auto i = 0; i < ctl.o_array_count; ++i) {
        t.set(i + 1, ctl.o_array_refs[i]);
    }
    return 1;
}

static luawrap::n_ret get_ctrl_boot_devices(lua_State *L, guint8 raid_index)
{
    SML_CTRL_BOOTABLE_DEVICES_S ctrl_reboot_devices;
    reset_zero(&ctrl_reboot_devices);

    ctrl_reboot_devices.i_controller_index = raid_index;

    auto ret = l_sml_adapter::sml_adapter_get_ctrl_boot_devices(&ctrl_reboot_devices);
    if (ret != SML_SUCCESS) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    lua_pushinteger(L, ctrl_reboot_devices.o_bootable_devices[0]);
    lua_pushinteger(L, ctrl_reboot_devices.o_bootable_devices[1]);
    return std::placeholders::_2;
}

static luawrap::n_ret get_ctrl_sas_addr(lua_State *L, uint8_t raid_index)
{
    SML_CTRL_SAS_ADDR_S ctrl_sas_addr;
    reset_zero(&ctrl_sas_addr);

    ctrl_sas_addr.i_controller_index = raid_index;

    auto ret = l_sml_adapter::sml_adapter_get_ctrl_sas_addr(&ctrl_sas_addr);
    if (ret != SML_SUCCESS) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    lua_pushstring(L, ctrl_sas_addr.o_sas_addr);
    return 1;
}

static luawrap::n_ret get_ctrl_work_mode(lua_State *L, SML_CTRL_BASIC_INFO_S *info)
{
    int  i = 1;
    auto t = luawrap::stack::new_table(L);
    if (info->operations.ctrl_operations.support_raid) {
        t.set(i++, "RAID");
    }
    if (info->operations.ctrl_operations.support_jbod) {
        t.set(i++, "JBOD");
    }
    if (info->operations.ctrl_operations.support_hba) {
        t.set(i++, "HBA");
    }
    if (info->operations.ctrl_operations.support_mixed) {
        t.set(i++, "Mixed");
    }
    return 1;
}

static uint32_t get_ctrl_properties(lua_State *L, SML_CTRL_BASIC_INFO_S *info)
{
    return *luawrap::void_pointer<uint32_t *>(&info->properties).u.p;
}

static const char *get_ctrl_name(SML_CTRL_BASIC_INFO_S *info)
{
    return info->ctrl_name;
}

static const char *get_ctrl_sn(SML_CTRL_BASIC_INFO_S *info)
{
    return info->ctrl_sn;
}

static const char *get_fw_version(SML_CTRL_BASIC_INFO_S *info)
{
    return info->fw_version;
}

static const char *get_nvdata_version(SML_CTRL_BASIC_INFO_S *info)
{
    return info->nvdata_version;
}

static const char *get_hardware_revision(SML_CTRL_BASIC_INFO_S *info)
{
    return info->hardware_revision;
}

static luawrap::n_ret get_ctrl_operations(lua_State *L, SML_CTRL_BASIC_INFO_S *info)
{
    auto t = luawrap::stack::new_table(L);
    t.set("pd_operations", *luawrap::void_pointer<uint32_t *>(&info->operations.pd_operations).u.p);
    t.set("ld_operations", *luawrap::void_pointer<uint32_t *>(&info->operations.ld_operations).u.p);
    t.set("ctrl_operations", *luawrap::void_pointer<uint32_t *>(&info->operations.ctrl_operations).u.p);
    return 1;
}

void l_ctrl::def_basic_properties(lua_State *L, luawrap::lua_class<SML_CTRL_BASIC_INFO_S> &cls)
{
    cls.def_readonly("last_update_timestamp", &SML_CTRL_BASIC_INFO_S::last_update_timestamp);
    cls.def_property("properties", c_func_wrap(L, get_ctrl_properties));
    cls.def_property("operations", c_func_wrap(L, get_ctrl_operations));
    cls.def_property("ctrl_name", c_func_wrap(L, get_ctrl_name));
    cls.def_property("ctrl_sn", c_func_wrap(L, get_ctrl_sn));
    cls.def_property("fw_version", c_func_wrap(L, get_fw_version));
    cls.def_property("nvdata_version", c_func_wrap(L, get_nvdata_version));
    cls.def_property("hardware_revision", c_func_wrap(L, get_hardware_revision));
    cls.def_readonly("ld_present_conut", &SML_CTRL_BASIC_INFO_S::ld_present_conut);
    cls.def_readonly("ld_degraded_count", &SML_CTRL_BASIC_INFO_S::ld_degraded_count);
    cls.def_readonly("ld_offline_count", &SML_CTRL_BASIC_INFO_S::ld_offline_count);
    cls.def_readonly("pd_present_count", &SML_CTRL_BASIC_INFO_S::pd_present_count);
    cls.def_readonly("pddisk_present_count", &SML_CTRL_BASIC_INFO_S::pddisk_present_count);
    cls.def_readonly("pddisk_prefail_count", &SML_CTRL_BASIC_INFO_S::pddisk_prefail_count);
    cls.def_readonly("pddisk_fail_count", &SML_CTRL_BASIC_INFO_S::pddisk_fail_count);
    cls.def_readonly("memory_size", &SML_CTRL_BASIC_INFO_S::memory_size);
    cls.def_readonly("memory_ecc_count", &SML_CTRL_BASIC_INFO_S::memory_ecc_count);
    cls.def_readonly("memory_ecc_bucket_size", &SML_CTRL_BASIC_INFO_S::memory_ecc_bucket_size);
    cls.def_readonly("memory_ce_count", &SML_CTRL_BASIC_INFO_S::memory_ce_count);
    cls.def_readonly("memory_uce_count", &SML_CTRL_BASIC_INFO_S::memory_uce_count);
}

void l_ctrl::def_raid_properties(lua_State *L, luawrap::lua_class<SML_CTRL_BASIC_INFO_S> &cls)
{
    cls.def_readonly("max_pd_raid0", &SML_CTRL_BASIC_INFO_S::max_pd_raid0);
    cls.def_readonly("max_pd_raid1", &SML_CTRL_BASIC_INFO_S::max_pd_raid1);
    cls.def_readonly("max_pd_raid5", &SML_CTRL_BASIC_INFO_S::max_pd_raid5);
    cls.def_readonly("max_pd_raid6", &SML_CTRL_BASIC_INFO_S::max_pd_raid6);
    cls.def_readonly("max_pd_raid10", &SML_CTRL_BASIC_INFO_S::max_pd_raid10);
    cls.def_readonly("max_pd_raid50", &SML_CTRL_BASIC_INFO_S::max_pd_raid50);
    cls.def_readonly("max_pd_raid60", &SML_CTRL_BASIC_INFO_S::max_pd_raid60);
    cls.def_readonly("max_lds_per_array", &SML_CTRL_BASIC_INFO_S::max_lds_per_array);
    cls.def_readonly("max_lds", &SML_CTRL_BASIC_INFO_S::max_lds);
    cls.def_readonly("allow_mix_ssd_hdd", &SML_CTRL_BASIC_INFO_S::allow_mix_ssd_hdd);
    cls.def_readonly("allow_ssd_mix", &SML_CTRL_BASIC_INFO_S::allow_ssd_mix);
    cls.def_readonly("raid0_supported", &SML_CTRL_BASIC_INFO_S::raid0_supported);
    cls.def_readonly("raid1_supported", &SML_CTRL_BASIC_INFO_S::raid1_supported);
    cls.def_readonly("raid5_supported", &SML_CTRL_BASIC_INFO_S::raid5_supported);
    cls.def_readonly("raid6_supported", &SML_CTRL_BASIC_INFO_S::raid6_supported);
    cls.def_readonly("raid10_supported", &SML_CTRL_BASIC_INFO_S::raid10_supported);
    cls.def_readonly("raid50_supported", &SML_CTRL_BASIC_INFO_S::raid50_supported);
    cls.def_readonly("raid60_supported", &SML_CTRL_BASIC_INFO_S::raid60_supported);
    cls.def_readonly("raid1adm_supported", &SML_CTRL_BASIC_INFO_S::raid1adm_supported);
    cls.def_readonly("raid10adm_supported", &SML_CTRL_BASIC_INFO_S::raid10adm_supported);
    cls.def_readonly("raid1triple_supported", &SML_CTRL_BASIC_INFO_S::raid1triple_supported);
    cls.def_readonly("raid10triple_supported", &SML_CTRL_BASIC_INFO_S::raid10triple_supported);
    cls.def_readonly("ssc_raid0_unsupported", &SML_CTRL_BASIC_INFO_S::ssc_raid0_unsupported);
    cls.def_readonly("ssc_raid1_supported", &SML_CTRL_BASIC_INFO_S::ssc_raid1_supported);
    cls.def_readonly("ssc_raid5_supported", &SML_CTRL_BASIC_INFO_S::ssc_raid5_supported);
    cls.def_readonly("min_pd_raid0", &SML_CTRL_BASIC_INFO_S::min_pd_raid0);
    cls.def_readonly("min_pd_raid1", &SML_CTRL_BASIC_INFO_S::min_pd_raid1);
    cls.def_readonly("min_pd_raid5", &SML_CTRL_BASIC_INFO_S::min_pd_raid5);
    cls.def_readonly("min_pd_raid6", &SML_CTRL_BASIC_INFO_S::min_pd_raid6);
    cls.def_readonly("min_pd_raid10", &SML_CTRL_BASIC_INFO_S::min_pd_raid10);
    cls.def_readonly("min_pd_raid50", &SML_CTRL_BASIC_INFO_S::min_pd_raid50);
    cls.def_readonly("min_pd_raid60", &SML_CTRL_BASIC_INFO_S::min_pd_raid60);
}

void l_ctrl::def_other_properties(lua_State *L, luawrap::lua_class<SML_CTRL_BASIC_INFO_S> &cls)
{
    cls.def_readonly("ctrl_temp", &SML_CTRL_BASIC_INFO_S::ctrl_temp);
    cls.def_readonly("mode", &SML_CTRL_BASIC_INFO_S::mode);
    cls.def_readonly("spare_activation_mode", &SML_CTRL_BASIC_INFO_S::spare_activation_mode);
    cls.def_readonly("pcie_link_width", &SML_CTRL_BASIC_INFO_S::pcie_link_width);
    cls.def_readonly("nobattery_write_cache", &SML_CTRL_BASIC_INFO_S::nobattery_write_cache);
    cls.def_readonly("read_cache_percent", &SML_CTRL_BASIC_INFO_S::read_cache_percent);
    cls.def_readonly("write_cache_policy", &SML_CTRL_BASIC_INFO_S::write_cache_policy);
    cls.def_readonly("ctrl_warnig_info_reported", &SML_CTRL_BASIC_INFO_S::ctrl_warnig_info_reported);
    cls.def_readonly("cache_pinned", &SML_CTRL_BASIC_INFO_S::cache_pinned);
    cls.def_readonly("maint_pd_fail_history", &SML_CTRL_BASIC_INFO_S::maint_pd_fail_history);
    cls.def_readonly("device_interface", &SML_CTRL_BASIC_INFO_S::device_interface);
    cls.def_readonly("min_strip", &SML_CTRL_BASIC_INFO_S::min_strip);
    cls.def_readonly("max_strip", &SML_CTRL_BASIC_INFO_S::max_strip);
    cls.def_readonly("consis_check_enabled", &SML_CTRL_BASIC_INFO_S::consis_check_enabled);
    cls.def_readonly("consis_check_period", &SML_CTRL_BASIC_INFO_S::consis_check_period);
    cls.def_readonly("consis_check_rate", &SML_CTRL_BASIC_INFO_S::consis_check_rate);
    cls.def_readonly("consis_check_repair", &SML_CTRL_BASIC_INFO_S::consis_check_repair);
    cls.def_readonly("consis_check_status", &SML_CTRL_BASIC_INFO_S::consis_check_status);
    cls.def_readonly("consis_check_totalvd", &SML_CTRL_BASIC_INFO_S::consis_check_totalvd);
    cls.def_readonly("consis_check_completedvd", &SML_CTRL_BASIC_INFO_S::consis_check_completedvd);
    cls.def_readonly("consis_check_delay", &SML_CTRL_BASIC_INFO_S::consis_check_delay);
    cls.def_readonly("member_drive_detection_sw", &SML_CTRL_BASIC_INFO_S::member_drive_detection_sw);
    cls.def_readonly("raw_drive_detection_sw", &SML_CTRL_BASIC_INFO_S::raw_drive_detection_sw);

    cls.def_property("work_mode", c_func_wrap(L, get_ctrl_work_mode));
}

static int32_t ctrl_operation(lua_State *L, uint8_t controller_index, uint16_t operation, const std::string_view &data)
{
    SML_CTRL_OPERTATION_S param;
    reset_zero(&param);

    param.i_controller_index = controller_index;
    param.i_operation = operation;
    param.i_param_ptr = reinterpret_cast<gpointer>(const_cast<char *>(data.data()));
    param.i_param_size = data.size();

    return l_sml_adapter::sml_adapter_ctrl_operation(&param);
}
// 获取控制器的错误码
static luawrap::n_ret get_ctrl_faultcode(lua_State *L, uint8_t ctrl_index)
{
    SML_CTRL_HEALTH_STATUS_S ctrl_health;
    reset_zero(&ctrl_health);
    ctrl_health.i_controller_index = ctrl_index;
    if (auto ret = l_sml_adapter::sml_adapter_get_ctrl_health_status(&ctrl_health); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto ctrl_health_code = luawrap::stack::new_table(L, 0, 5);
    ctrl_health_code.set("health_status_code", ctrl_health.o_health_status_code);
    ctrl_health_code.set("hw_err", ctrl_health.o_hw_err);
    ctrl_health_code.set("clk_err", ctrl_health.o_clk_err);
    ctrl_health_code.set("power_err", ctrl_health.o_power_err);
    ctrl_health_code.set("capacity_err", ctrl_health.o_capacity_err);
    ctrl_health_code.set("flash_err", ctrl_health.o_flash_err);
    ctrl_health_code.set("err_change", ctrl_health.o_err_change);
    return 1;
}

static luawrap::n_ret clear_all_controller_info(lua_State *L)
{
    l_sml_adapter::sml_adapter_clear_all_controller_info();
    return 1;
}

static luawrap::n_ret get_ctrl_init_state(lua_State *L, uint8_t ctrl_index)
{
    auto ret = l_sml_adapter::sml_adapter_get_ctrl_init_state(ctrl_index);
    lua_pushinteger(L, ret);
    return 1;
}

// 收集单个控制器日志信息
static uint16_t dump_ctrl_single_log(lua_State *L, uint8_t ctrl_index, char* src_dir, uint8_t log_type, char* log_name)
{
    return l_sml_adapter::sml_adapter_dump_ctrl_single_log(ctrl_index, src_dir, log_type, log_name);
}

// 把控制器日志解析为明文
static uint16_t parse_controller_bin_log(lua_State *L, uint8_t ctrl_index,
    const std::string_view &src_dir, const std::string_view &dest_dir)
{
    const char *src_ptr = src_dir.data();
    const char *dest_ptr = dest_dir.data();
    return l_sml_adapter::sml_adapter_parse_controller_bin_log(ctrl_index, src_ptr, dest_ptr);
}

// 将新增映射以json格式化后的字符串传下去
static gint32 trans_drive_data(lua_State *L, uint8_t ctrl_index, const char* json_raw_str)
{
    return l_sml_adapter::sml_adapter_pd_trans_drive_data(ctrl_index, json_raw_str);
}

static gint32 upgrade_ctrl_process(lua_State *L, uint8_t ctrl_index, const std::string_view &path_sv)
{
    std::string path(path_sv);
    return l_sml_adapter::sml_adapter_ctrl_fw_upgrade(ctrl_index, path.c_str());
}

void l_ctrl::register_to(lua_State *L, luawrap::stack_table &t)
{
    luawrap::restore_stack_top _s(L);

    auto cls = luawrap::lua_class<SML_CTRL_BASIC_INFO_S>(L).ctor<>();
    cls.def("reset_zero", c_func_wrap(L, reset_zero<SML_CTRL_BASIC_INFO_S>));
    cls.def("update", c_func_wrap(L, update_ctrl_info));
    def_basic_properties(L, cls);
    def_raid_properties(L, cls);
    def_other_properties(L, cls);

    t.set("SML_CTRL_BASIC_INFO_S", cls);

    t.set("get_ctrl_pd_list", c_func_wrap(L, get_ctrl_pd_list));
    t.set("get_ctrl_ld_list", c_func_wrap(L, get_ctrl_ld_list));
    t.set("get_ctrl_array_list", c_func_wrap(L, get_ctrl_array_list));
    t.set("get_ctrl_sas_addr", c_func_wrap(L, get_ctrl_sas_addr));
    t.set("clear_all_controller_info", c_func_wrap(L, clear_all_controller_info));
    t.set("get_ctrl_init_state", c_func_wrap(L, get_ctrl_init_state));
    t.set("get_ctrl_boot_devices", c_func_wrap(L, get_ctrl_boot_devices));
    t.set("get_ctrl_faultcode", c_func_wrap(L, get_ctrl_faultcode));
    t.set("dump_ctrl_single_log", c_func_wrap(L, dump_ctrl_single_log));
    t.set("parse_controller_bin_log", c_func_wrap(L, parse_controller_bin_log));
    t.set("trans_drive_data", c_func_wrap(L, trans_drive_data));
    t.set("upgrade_ctrl_process", c_func_wrap(L, upgrade_ctrl_process));
    t.set("ctrl_operation", c_func_wrap(L, ctrl_operation));
}
}  // namespace sml
