/* 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_pd.h"

#include "pd.h"
#include "l_sml_adapter.h"
#include "pd_log_adapter.h"


namespace sml {
static luawrap::inner_struct_ptr<SML_PD_PROGRESS_INFO_S> get_proginfo(SML_PD_BASIC_INFO_S *p)
{
    return {&p->proginfo};
}

static luawrap::inner_struct_ptr<SML_PD_SPARE_BLOCK_INFO_S> get_spareblock(SML_PD_BASIC_INFO_S *p)
{
    return {&p->spare_block};
}

static luawrap::inner_struct_ptr<EST_LFSP_HW_DEFINED_S> get_hw_defined_estimated_lifespan(SML_PD_BASIC_INFO_S *p)
{
    return {&p->estimated_lifespan.hw_defined};
}

static luawrap::inner_struct_ptr<EST_LFSP_VENDOR_S> get_vendor_estimated_lifespan(SML_PD_BASIC_INFO_S *p)
{
    return {&p->estimated_lifespan.vendor};
}

static luawrap::inner_struct_ptr<SML_PD_WRITE_AMP_INFO_S> get_write_amp(SML_PD_BASIC_INFO_S *p)
{
    return {&p->write_amp};
}

static luawrap::inner_struct_ptr<WRITE_AMP_HW_DEFINED_S> get_hwdefined_write_amp(SML_PD_BASIC_INFO_S *p)
{
    return {&p->write_amp.hw_defined};
}

static luawrap::inner_struct_ptr<WRITE_AMP_VENDOR_S> get_vendor_write_amp(SML_PD_BASIC_INFO_S *p)
{
    return {&p->write_amp.vendor};
}

static int32_t update_pd_info(lua_State *L, SML_PD_BASIC_INFO_S *pd, uint8_t ctrl_index, uint16_t pd_device_id)
{
    if (auto ret = l_sml_adapter::sml_adapter_get_pd_info(ctrl_index, pd_device_id, pd); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    return 1;
}

static int32_t update_pd_sas_smart_info(lua_State *L, SML_PD_SAS_SMART_INFO *pd, uint8_t ctrl_index,
                                        uint16_t pd_device_id)
{
    if (auto ret = l_sml_adapter::sml_adapter_get_pd_sas_smart_info(ctrl_index, pd_device_id, pd); ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    return 1;
}

static int32_t update_slow_disk_info(lua_State *L, SML_PD_SLOW_DATA_S *pd, uint8_t ctrl_index,
    uint16_t pd_device_id)
{
    if (auto ret = l_sml_adapter::sml_adapter_get_pd_slow_disk_info(ctrl_index, pd_device_id, (void *)pd);
        ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    return 1;
}

static int32_t update_pd_sas_smart_info_spec(lua_State *L, SML_PD_SAS_SMART_INFO *pd, uint8_t ctrl_index,
    uint16_t pd_device_id, uint8_t filter)
{
    if (auto ret = l_sml_adapter::sml_adapter_get_pd_sas_smart_info_spec(ctrl_index, pd_device_id, pd, filter);
        ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }
    return 1;
}

static const char *get_manufacture_data(SML_PD_SAS_SMART_INFO *info)
{
    if (!(g_utf8_validate(info->manufacture_data, strlen(info->manufacture_data), NULL)) ||
        strlen(info->manufacture_data) == 0) {
        return "";
    }
    return info->manufacture_data;
}

static int32_t pd_operation(lua_State *L, uint8_t controller_index, uint16_t device_id, uint16_t operation,
                            const std::string_view &data)
{
    SML_PD_OPERTATION_S pd;
    reset_zero(&pd);
    pd.i_controller_index = controller_index;
    pd.i_device_id        = device_id;
    pd.i_operation        = operation;
    pd.i_param_ptr        = reinterpret_cast<gpointer>(const_cast<char *>(data.data()));
    pd.i_param_size       = data.size();

    return l_sml_adapter::sml_adapter_pd_operation(&pd);
}

static const char *get_sas_addr1(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->sas_addr1, strlen(info->sas_addr1), NULL)) || strlen(info->sas_addr1) == 0) {
        return "N/A";
    }
    return info->sas_addr1;
}

static const char *get_sas_addr2(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->sas_addr2, strlen(info->sas_addr2), NULL)) || strlen(info->sas_addr2) == 0) {
        return "N/A";
    }
    return info->sas_addr2;
}

static const char *get_serial_num(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->serial_num, strlen(info->serial_num), NULL)) || strlen(info->serial_num) == 0) {
        return "";
    }
    return info->serial_num;
}

static const char *get_model(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->model, strlen(info->model), NULL)) || strlen(info->model) == 0) {
        return "";
    }
    return info->model;
}

static const char *get_firmware_version(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->firmware_version, strlen(info->firmware_version), NULL)) ||
        strlen(info->firmware_version) == 0) {
        return "";
    }
    return info->firmware_version;
}

static const char *get_manufacturer(SML_PD_BASIC_INFO_S *info)
{
    if (!(g_utf8_validate(info->manufacturer, strlen(info->manufacturer), NULL)) || strlen(info->manufacturer) == 0) {
        return "";
    }
    return info->manufacturer;
}

void l_pd::def_properties(lua_State *L, luawrap::lua_class<SML_PD_BASIC_INFO_S> &cls)
{
    cls.def_readonly("identify_status", &SML_PD_BASIC_INFO_S::identify_status);
    cls.def_readonly("device_id", &SML_PD_BASIC_INFO_S::device_id);
    cls.def_readonly("sequence_num", &SML_PD_BASIC_INFO_S::sequence_num);
    cls.def_readonly("health", &SML_PD_BASIC_INFO_S::health);
    cls.def_readonly("fde_capable", &SML_PD_BASIC_INFO_S::fde_capable);
    cls.def_readonly("fw_state", &SML_PD_BASIC_INFO_S::fw_state);
    cls.def_readonly("power_state", &SML_PD_BASIC_INFO_S::power_state);
    cls.def_readonly("scsi_dev_type", &SML_PD_BASIC_INFO_S::scsi_dev_type);
    cls.def_readonly("device_speed", &SML_PD_BASIC_INFO_S::device_speed);
    cls.def_readonly("link_speed", &SML_PD_BASIC_INFO_S::link_speed);
    cls.def_readonly("encl_device_id", &SML_PD_BASIC_INFO_S::encl_device_id);
    cls.def_readonly("slot_num", &SML_PD_BASIC_INFO_S::slot_num);
    cls.def_readonly("media_type", &SML_PD_BASIC_INFO_S::media_type);
    cls.def_readonly("interface_type", &SML_PD_BASIC_INFO_S::interface_type);
    cls.def_readonly("temperature", &SML_PD_BASIC_INFO_S::temperature);
    cls.def_readonly("boot_priority", &SML_PD_BASIC_INFO_S::boot_priority);
    cls.def_property("sas_addr1", c_func_wrap(L, get_sas_addr1));
    cls.def_property("sas_addr2", c_func_wrap(L, get_sas_addr2));
    cls.def_property("serial_num", c_func_wrap(L, get_serial_num));
    cls.def_property("model", c_func_wrap(L, get_model));
    cls.def_property("firmware_version", c_func_wrap(L, get_firmware_version));
    cls.def_property("manufacturer", c_func_wrap(L, get_manufacturer));
    cls.def_readonly("vendor_id", &SML_PD_BASIC_INFO_S::vendor_id);
    cls.def_readonly("coerced_size", &SML_PD_BASIC_INFO_S::coerced_size);
    cls.def_readonly("media_err_count", &SML_PD_BASIC_INFO_S::media_err_count);
    cls.def_readonly("other_err_count", &SML_PD_BASIC_INFO_S::other_err_count);
    cls.def_readonly("prefail_count", &SML_PD_BASIC_INFO_S::prefail_count);
    cls.def_readonly("hot_spare", &SML_PD_BASIC_INFO_S::hot_spare);
    cls.def_readonly("remnant_media_wearout", &SML_PD_BASIC_INFO_S::remnant_media_wearout);
    cls.def_property("spareblock", c_func_wrap(L, get_spareblock));
    cls.def_property("writeamp", c_func_wrap(L, get_write_amp));
    cls.def_property("hw_defined_write_amp", c_func_wrap(L, get_hwdefined_write_amp));
    cls.def_property("vendor_write_amp", c_func_wrap(L, get_vendor_write_amp));
    cls.def_readonly("rotation_speed", &SML_PD_BASIC_INFO_S::rotation_speed);
    cls.def_property("proginfo", c_func_wrap(L, get_proginfo));
    cls.def_readonly("form_factor", &SML_PD_BASIC_INFO_S::form_factor);
    cls.def_readonly("dev_not_supported", &SML_PD_BASIC_INFO_S::dev_not_supported);
    cls.def_readonly("fw_state_raw", &SML_PD_BASIC_INFO_S::fw_state_raw);
    cls.def_readonly("is_foreign", &SML_PD_BASIC_INFO_S::is_foreign);
    cls.def_readonly("coerced_size_blk", &SML_PD_BASIC_INFO_S::coerced_size_blk);
    cls.def_readonly("block_size", &SML_PD_BASIC_INFO_S::block_size);
    cls.def_readonly("power_on_hours", &SML_PD_BASIC_INFO_S::power_on_hours);
    cls.def_readonly("halflife", &SML_PD_BASIC_INFO_S::halflife);
    cls.def_readonly("last_update_timestamp", &SML_PD_BASIC_INFO_S::last_update_timestamp);
    cls.def_readonly("bootable", &SML_PD_BASIC_INFO_S::bootable);
    cls.def_readonly("last_prefail_event_seq_num", &SML_PD_BASIC_INFO_S::last_prefail_event_seq_num);
    cls.def_property("hw_defined_estimated_lifespan", c_func_wrap(L, get_hw_defined_estimated_lifespan));
    cls.def_property("vendor_estimated_lifespan", c_func_wrap(L, get_vendor_estimated_lifespan));
}

void l_pd::register_pd_info(lua_State *L, luawrap::stack_table &t)
{
    // 导出 SML_PD_PROGRESS_INFO_S 结构给 lua 使用
    t.set("SML_PD_PROGRESS_INFO_S",  // SML_PD_PROGRESS_INFO_S
          luawrap::lua_class<SML_PD_PROGRESS_INFO_S>(L)
              .def_readonly("rebuild_state", &SML_PD_PROGRESS_INFO_S::rebuild_state)
              .def_readonly("rebuild_progress", &SML_PD_PROGRESS_INFO_S::rebuild_progress)
              .def_readonly("patrol_state", &SML_PD_PROGRESS_INFO_S::patrol_state)
              .def_readonly("patrol_progress", &SML_PD_PROGRESS_INFO_S::patrol_progress))
        .pop(L, 1);

    // 导出 SML_PD_SPARE_BLOCK_INFO_S 结构给 lua 使用
    t.set("SML_PD_SPARE_BLOCK_INFO_S",  // SML_PD_SPARE_BLOCK_INFO_S
          luawrap::lua_class<SML_PD_SPARE_BLOCK_INFO_S>(L)
              .def_readonly("slc_value", &SML_PD_SPARE_BLOCK_INFO_S::slc_value)
              .def_readonly("tlc_value", &SML_PD_SPARE_BLOCK_INFO_S::tlc_value)).pop(L, 1);
    
    // 导出 SML_PD_WRITE_AMP_INFO_S 结构给 lua 使用
    t.set("SML_PD_WRITE_AMP_INFO_S",  // SML_PD_WRITE_AMP_INFO_S
          luawrap::lua_class<SML_PD_WRITE_AMP_INFO_S>(L)
              .def_readonly("update_support_flag", &SML_PD_WRITE_AMP_INFO_S::update_support_flag))
        .pop(L, 1);

    // 导出 WRITE_AMP_VENDOR_S 结构给 lua 使用
    t.set("WRITE_AMP_VENDOR_S",  // WRITE_AMP_VENDOR_S
          luawrap::lua_class<WRITE_AMP_VENDOR_S>(L)
              .def_readonly("valid_flag", &WRITE_AMP_VENDOR_S::valid_flag)
              .def_readonly("nand_write", &WRITE_AMP_VENDOR_S::nand_write)
              .def_readonly("host_write", &WRITE_AMP_VENDOR_S::host_write)).pop(L, 1);

    // 导出 WRITE_AMP_HW_DEFINED_S 结构给 lua 使用
    t.set("WRITE_AMP_HW_DEFINED_S",  // WRITE_AMP_HW_DEFINED_S
          luawrap::lua_class<WRITE_AMP_HW_DEFINED_S>(L)
              .def_readonly("valid_flag", &WRITE_AMP_HW_DEFINED_S::valid_flag)
              .def_readonly("nand_write_l", &WRITE_AMP_HW_DEFINED_S::nand_write_l)
              .def_readonly("nand_write_h", &WRITE_AMP_HW_DEFINED_S::nand_write_h)
              .def_readonly("host_write_l", &WRITE_AMP_HW_DEFINED_S::host_write_l)
              .def_readonly("host_write_h", &WRITE_AMP_HW_DEFINED_S::host_write_h)).pop(L, 1);

    // 导出 EST_LFSP_HW_DEFINED_S 结构给 lua 使用
    t.set("EST_LFSP_HW_DEFINED_S",  // EST_LFSP_HW_DEFINED_S
          luawrap::lua_class<EST_LFSP_HW_DEFINED_S>(L)
              .def_readonly("slc_pe_cycle", &EST_LFSP_HW_DEFINED_S::slc_pe_cycle)
              .def_readonly("tlc_pe_cycle", &EST_LFSP_HW_DEFINED_S::tlc_pe_cycle)
              .def_readonly("slc_avg_ec", &EST_LFSP_HW_DEFINED_S::slc_avg_ec)
              .def_readonly("tlc_avg_ec", &EST_LFSP_HW_DEFINED_S::tlc_avg_ec)
              .def_readonly("slc_poh", &EST_LFSP_HW_DEFINED_S::slc_poh)
              .def_readonly("tlc_poh", &EST_LFSP_HW_DEFINED_S::tlc_poh)
              .def_readonly("tlc_used_lifespan", &EST_LFSP_HW_DEFINED_S::tlc_used_lifespan)
              .def_readonly("slc_used_lifespan", &EST_LFSP_HW_DEFINED_S::slc_used_lifespan)
              .def_readonly("hw_valid_flag", &EST_LFSP_HW_DEFINED_S::valid_flag)).pop(L, 1);

    // 导出 EST_LFSP_VENDOR_S 结构给 lua 使用
    t.set("EST_LFSP_VENDOR_S",  // EST_LFSP_VENDOR_S
          luawrap::lua_class<EST_LFSP_VENDOR_S>(L)
              .def_readonly("remn_wearout", &EST_LFSP_VENDOR_S::remn_wearout)
              .def_readonly("power_on_hours", &EST_LFSP_VENDOR_S::power_on_hours)
              .def_readonly("vendor_valid_flag", &EST_LFSP_VENDOR_S::valid_flag)).pop(L, 1);

    // 导出 SML_PD_BASIC_INFO_S 结构给 lua 使用
    auto cls = luawrap::lua_class<SML_PD_BASIC_INFO_S>(L).ctor<>();
    cls.def("reset_zero", c_func_wrap(L, reset_zero<SML_PD_BASIC_INFO_S>));
    cls.def("update", c_func_wrap(L, update_pd_info));
    def_properties(L, cls);

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

void l_pd::def_sas_smart_properties(lua_State *L, luawrap::lua_class<SML_PD_SAS_SMART_INFO> &cls)
{
    cls.def_readonly("strip_temperature", &SML_PD_SAS_SMART_INFO::strip_temperature);
    cls.def_readonly("glist_len", &SML_PD_SAS_SMART_INFO::glist_len);
    cls.def_readonly("plist_len", &SML_PD_SAS_SMART_INFO::plist_len);
    cls.def_property("manufacture_data", c_func_wrap(L, get_manufacture_data));
    cls.def_readonly("blocks_sent", &SML_PD_SAS_SMART_INFO::blocks_sent);
    cls.def_readonly("blocks_received", &SML_PD_SAS_SMART_INFO::blocks_received);
    cls.def_readonly("minutes_left", &SML_PD_SAS_SMART_INFO::minutes_left);
}

void l_pd::def_slow_disk_properties(lua_State *L, luawrap::lua_class<SML_PD_SLOW_DATA_S> &cls)
{
    cls.def_readonly("cmd_timeout_times", &SML_PD_SLOW_DATA_S::cmd_timeout_times);
    cls.def_readonly("unexpected_sense_times", &SML_PD_SLOW_DATA_S::unexpected_sense_times);
    cls.def_readonly("reset_times", &SML_PD_SLOW_DATA_S::reset_times);
    cls.def_readonly("power_on_times", &SML_PD_SLOW_DATA_S::power_on_times);
}

void l_pd::register_pd_sas_smart_info(lua_State *L, luawrap::stack_table &t)
{
    // 导出 SML_PD_SAS_SMART_INFO 结构给 lua 使用
    auto sas_smart_cls = luawrap::lua_class<SML_PD_SAS_SMART_INFO>(L).ctor<>();
    sas_smart_cls.def("reset_zero", c_func_wrap(L, reset_zero<SML_PD_SAS_SMART_INFO>));
    sas_smart_cls.def("update", c_func_wrap(L, update_pd_sas_smart_info));
    sas_smart_cls.def("update_spec", c_func_wrap(L, update_pd_sas_smart_info_spec));
    def_sas_smart_properties(L, sas_smart_cls);

    t.set("SML_PD_SAS_SMART_INFO", sas_smart_cls);
}

void l_pd::register_slow_disk_data(lua_State *L, luawrap::stack_table &t)
{
    // 导出 SML_PD_SLOW_DATA_S 结构给 lua 使用
    auto slow_disk_cls = luawrap::lua_class<SML_PD_SLOW_DATA_S>(L).ctor<>();
    slow_disk_cls.def("reset_zero", c_func_wrap(L, reset_zero<SML_PD_SLOW_DATA_S>));
    slow_disk_cls.def("update", c_func_wrap(L, update_slow_disk_info));
    def_slow_disk_properties(L, slow_disk_cls);

    t.set("SML_PD_SLOW_DATA_S", slow_disk_cls);
}

static luawrap::n_ret pd_diag_encl_comm_error(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id,
    uint16_t encl_device_id)
{
    SML_PD_FAULT_ANALYSIS diag_info;
    reset_zero(&diag_info);
    diag_info.i_controller_index = ctrl_index;
    diag_info.i_pd_device_id = pd_device_id;
    diag_info.i_encl_device_id = encl_device_id;
    int32_t ret = l_sml_adapter::sml_adapter_diagnose_encl_comm_error(&diag_info);
    if (ret != SML_SUCCESS) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto t = luawrap::stack::new_table(L);
    t.set("encl_comm_error", (uint8_t)diag_info.o_fault_bitmap.encl_comm_error);
    return 1;
}

static luawrap::n_ret pd_diag_sense_error(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id)
{
    SML_PD_FAULT_ANALYSIS diag_info;
    reset_zero(&diag_info);
    diag_info.i_controller_index = ctrl_index;
    diag_info.i_pd_device_id = pd_device_id;
    int32_t ret = l_sml_adapter::sml_adapter_diagnose_pd_sense_error(&diag_info);
    if (ret != SML_SUCCESS) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    auto t = luawrap::stack::new_table(L);
    t.set("sense_error", (uint8_t)diag_info.o_fault_bitmap.sense_error);
    t.set("io_buf", *luawrap::void_pointer<char *>(diag_info.io_buf));
    return 1;
}

static luawrap::n_ret pd_diag_by_smart_log(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id)
{
    PD_LOG_S pd_log;
    reset_zero(&pd_log);

    // a、收集SATA硬盘的SMART Attribute
    pd_log.SATADevice.smart_attribute.log_type = PD_LOG_SATA_SMART_ATTRIBUTE;
    pd_log.SATADevice.smart_attribute.max_raw_data_size.sectors = PD_LOG_SATA_SMART_ATTR_MAX_SECTORS;
    pd_log_adapter::adapter_pd_log_collect_raw_data_from_oob(ctrl_index, pd_device_id,
        &pd_log.SATADevice.smart_attribute, PD_LOG_COLLECT_TIMEOUT);
    if (pd_log.SATADevice.smart_attribute.result == SML_ERR_PD_SCSI_TIMEOUT) {
        lua_pushinteger(L, pd_log.SATADevice.smart_attribute.result);
        lua_error(L);
    }

    // b、收集SATA硬盘的SMART Attribute Threshold
    pd_log.SATADevice.smart_attribute_threshold.log_type = PD_LOG_SATA_SMART_ATTRIBUTE_THRESHOLD;
    pd_log.SATADevice.smart_attribute_threshold.max_raw_data_size.sectors = PD_LOG_SATA_SMART_ATTR_MAX_SECTORS;
    pd_log_adapter::adapter_pd_log_collect_raw_data_from_oob(ctrl_index, pd_device_id,
        &pd_log.SATADevice.smart_attribute_threshold, PD_LOG_COLLECT_TIMEOUT);
    if (pd_log.SATADevice.smart_attribute_threshold.result == SML_ERR_PD_SCSI_TIMEOUT) {
        pd_log_adapter::adapter_pd_log_clear_data(&pd_log);
        lua_pushinteger(L, pd_log.SATADevice.smart_attribute_threshold.result);
        lua_error(L);
    }

    // c、诊断
    SML_PD_FAULT_ANALYSIS result;
    reset_zero(&result);
    pd_log_adapter::adapter_pd_log_handle_sata_smart(NULL, &pd_log, &result);

    auto t = luawrap::stack::new_table(L);
    t.set("pd_smart_log_error", (uint8_t)result.o_fault_bitmap.pd_smart_log_error);
    t.set("pd_crc_error", (uint8_t)result.o_fault_bitmap.pd_crc_error);
    t.set("io_buf", result.io_buf);
    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);
    return 1;
}

static luawrap::n_ret pd_diag_by_ext_error_log(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id)
{
    PD_LOG_S pd_log;
    reset_zero(&pd_log);

    // a、收集SATA硬盘的Extended error log
    pd_log.SATADevice.extent_error.log_type = PD_LOG_SATA_EXTENT_ERROR;
    pd_log.SATADevice.extent_error.max_raw_data_size.sectors = PD_LOG_SATA_EXTENT_ERROR_LOG_MAX_SECTORS;
    pd_log_adapter::adapter_pd_log_collect_raw_data_from_oob(ctrl_index, pd_device_id, &pd_log.SATADevice.extent_error,
        PD_LOG_COLLECT_TIMEOUT);
    if (pd_log.SATADevice.extent_error.result == SML_ERR_PD_SCSI_TIMEOUT) {
        lua_pushinteger(L, pd_log.SATADevice.extent_error.result);
        lua_error(L);
    }

    // b、诊断
    SML_PD_FAULT_ANALYSIS result;
    reset_zero(&result);
    pd_log_adapter::adapter_pd_log_handle_sata_ext_error(NULL, &pd_log, &result, NULL);

    auto t = luawrap::stack::new_table(L);
    t.set("pd_smart_log_error", (uint8_t)result.o_fault_bitmap.pd_smart_log_error);
    t.set("io_buf", result.io_buf);
    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);
    return 1;
}

static luawrap::n_ret pd_diag_by_ext_self_test_log(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id,
    uint8_t in_progress)
{
    PD_LOG_S pd_log;
    reset_zero(&pd_log);

    // a、收集SATA硬盘的extended self test log
    pd_log.SATADevice.extended_selftest.log_type = PD_LOG_SATA_EXTENDED_SELF_TEST;
    pd_log.SATADevice.extended_selftest.max_raw_data_size.sectors = PD_LOG_SATA_EXTENDED_SELF_TEST_MAX_SECTORS;
    pd_log_adapter::adapter_pd_log_collect_raw_data_from_oob(ctrl_index, pd_device_id,
        &pd_log.SATADevice.extended_selftest, PD_LOG_COLLECT_TIMEOUT);
    if (pd_log.SATADevice.extended_selftest.result == SML_ERR_PD_SCSI_TIMEOUT) {
        lua_pushinteger(L, pd_log.SATADevice.extended_selftest.result);
        lua_error(L);
    }

    // b、诊断
    SML_PD_FAULT_ANALYSIS result;
    reset_zero(&result);
    PD_SMART_TEST_ANALYSIS analysis;
    reset_zero(&analysis);

    analysis.result = &result;
    analysis.in_progress = in_progress;
    pd_log_adapter::adapter_pd_log_handle_sata_ext_self_test(NULL, &pd_log, &analysis, NULL);

    auto t = luawrap::stack::new_table(L);
    t.set("pd_short_dst_error", (uint8_t)analysis.result->o_fault_bitmap.pd_short_dst_error);
    t.set("io_buf", *luawrap::void_pointer<char *>(analysis.result->io_buf));
    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);
    return 1;
}

static luawrap::n_ret pd_diag_get_sata_self_test_status(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id)
{
    PD_LOG_S pd_log;
    reset_zero(&pd_log);

    // 获取SMART ATTRIBUTE
    pd_log.SATADevice.smart_attribute.log_type = PD_LOG_SATA_SMART_ATTRIBUTE;
    pd_log.SATADevice.smart_attribute.max_raw_data_size.sectors = PD_LOG_SATA_SMART_ATTR_MAX_SECTORS;
    pd_log_adapter::adapter_pd_log_collect_raw_data_from_oob(ctrl_index, pd_device_id,
        &pd_log.SATADevice.smart_attribute, PD_LOG_COLLECT_TIMEOUT);
    if (pd_log.SATADevice.smart_attribute.result == SML_ERR_PD_SCSI_TIMEOUT) {
        lua_pushinteger(L, pd_log.SATADevice.smart_attribute.result);
        lua_error(L);
    }

    ATA_SMART_DATA_S *smart_data = (ATA_SMART_DATA_S *)pd_log.SATADevice.smart_attribute.data;
    if (smart_data == NULL) {
        lua_pushinteger(L, SML_ERR_DATA_INVALID);
        lua_error(L);
    }

    uint32_t status;
    // 不支持short dst的执行，返回ERR
    if (!(smart_data->offLineDataCollectionCapability & 0x10)) {
        debug_log(DLOG_ERROR, "SATA drive short dst not support.\n");
        status = SML_ERR_PD_OPERATION_NOT_SUPPORT;
    } else if (0xf == (smart_data->selftestExecStatus >> 4)) {      // 4 当前有测试正在执行，返回正在测试错误码
        debug_log(DLOG_INFO, "SATA drive short dst is in progress.\n");
        status = SML_ERR_PD_IS_SMART_TEST;
    } else {
        // 没有测试正在执行，返回OK
        status = SML_SUCCESS;
    }

    auto t = luawrap::stack::new_table(L);
    t.set("status", status);
    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);
    return 1;
}

static luawrap::n_ret pd_log_get_drive_log(lua_State *L, uint8_t ctrl_index, uint8_t protocol, uint16_t pd_device_id,
    std::vector<uint8_t> u8, std::vector<uint16_t> u16, std::vector<std::string_view> string)
{
    // 7: U8类型的参数数量 1: U16参数类型的数量 5: string类型的参数数量
    if (u8.size() != 7 or u16.size() != 1 or string.size() != 5) {
        lua_error(L);
    }

    PD_LOG_S pd_log;
    reset_zero(&pd_log);
    pd_log.log_source = u8[0];  // 0: 固定位置
    pd_log.log_rotate_num = u8[1];  // 1: 固定位置
    pd_log.pd_power_state = u8[2];  // 2: 固定位置
    pd_log.pd_interface_type = u8[3];  // 3: 固定位置
    pd_log.pd_media_type = u8[4];  // 4: 固定位置
    pd_log.pd_slot_number = u8[5];  // 5: 固定位置
    pd_log.pd_remnant_media_wearout = u8[6];  // 6: 固定位置

    pd_log.pd_enclosure_id = u16[0];  // 0: 固定位置

    (void)memcpy_s(pd_log.pd_device_name, sizeof(pd_log.pd_device_name),
        const_cast<char *>(string[0].data()), string[0].size());  // 0: 固定位置
    (void)memcpy_s(pd_log.pd_interface_type_str, sizeof(pd_log.pd_interface_type_str),
        const_cast<char *>(string[1].data()), string[1].size());  // 1: 固定位置
    (void)memcpy_s(pd_log.pd_manufacturer, sizeof(pd_log.pd_manufacturer),
        const_cast<char *>(string[2].data()), string[2].size());  // 2: 固定位置
    (void)memcpy_s(pd_log.pd_serial_number, sizeof(pd_log.pd_serial_number),
        const_cast<char *>(string[3].data()), string[3].size());  // 3: 固定位置
    (void)memcpy_s(pd_log.pd_model, sizeof(pd_log.pd_model),
        const_cast<char *>(string[4].data()), string[4].size());  // 4: 固定位置
    Json *disk_log_jso = NULL;
    if (JsonObjectCreate(&disk_log_jso) != JSON_OK) {
        lua_pushstring(L, "failed to create json_object");
        lua_error(L);
    }
    if (protocol == PD_INTERFACE_TYPE_SATA) {
        gint32 retval = pd_log_adapter::adapter_pd_log_collect_sata_from_oob(ctrl_index, pd_device_id, &pd_log);
        if (retval == RET_OK) {
            pd_log_adapter::adapter_pd_log_handle_sata(&pd_log, disk_log_jso);
        }
    } else if (protocol == PD_INTERFACE_TYPE_SAS) {
        gint32 retval = pd_log_adapter::adapter_pd_log_collect_sas_from_oob(ctrl_index, pd_device_id, &pd_log);
        if (retval == RET_OK) {
            pd_log_adapter::adapter_pd_log_handle_sas(&pd_log, disk_log_jso);
        }
    }

    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);

    const char *s = dal_json_object_get_str(disk_log_jso);
    JsonObjectRelease(disk_log_jso);
    lua_pushstring(L, s);
    return 1;
}

static luawrap::n_ret pd_get_smart_info_str(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id,
    guint8 hdd_intf_type)
{
    GVariant *smart_info_var = NULL;
    gint32 ret = l_sml_adapter::sml_adapter_get_smart_data_str(ctrl_index, pd_device_id,
        hdd_intf_type, &smart_info_var);
    if (ret != RET_OK) {
        lua_pushinteger(L, ret);
        lua_error(L);
    }

    const char *s = g_variant_get_string(smart_info_var, nullptr);
    lua_pushstring(L, s);
    g_variant_unref(smart_info_var);
    return 1;
}

static luawrap::n_ret pd_get_write_io_latency_info(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id,
    uint8_t media_type, std::string_view manufacturer)
{
    PD_DIAGNOSE_INFO pd_log;
    reset_zero(&pd_log);
    pd_log.pd_media_type = media_type;
    gint32 retval = memcpy_s(pd_log.pd_manufacturer, sizeof(pd_log.pd_manufacturer),
        const_cast<char *>(manufacturer.data()), manufacturer.size());
    if (retval != RET_OK) {
        lua_pushstring(L, "failed to copy manufacture data");
        lua_error(L);
    }
    retval = pd_log_adapter::adapter_pd_log_collect_write_io_latency_from_oob(ctrl_index, pd_device_id, &pd_log);
    if (retval != RET_OK) {
        lua_pushstring(L, "failed to get io latency data");
        if (pd_log.SASDevice.write_latency_statistics.data != nullptr) {
            g_free(pd_log.SASDevice.write_latency_statistics.data);
            pd_log.SASDevice.write_latency_statistics.data = nullptr;
        }
        lua_error(L);
    }
    guint32 write_data_len = pd_log.SASDevice.write_latency_statistics.data_length;
    guint8 *write_data = pd_log.SASDevice.write_latency_statistics.data;
    lua_pushlstring(L, (const char *)write_data, write_data_len);
    if (pd_log.SASDevice.write_latency_statistics.data != nullptr) {
        g_free(pd_log.SASDevice.write_latency_statistics.data);
        pd_log.SASDevice.write_latency_statistics.data = nullptr;
    }
    return 1;
}

static luawrap::n_ret pd_get_read_io_latency_info(lua_State *L, uint8_t ctrl_index, uint16_t pd_device_id,
    uint8_t media_type, std::string_view manufacturer)
{
    PD_DIAGNOSE_INFO pd_log;
    reset_zero(&pd_log);
    pd_log.pd_media_type = media_type;
    gint32 retval = memcpy_s(pd_log.pd_manufacturer, sizeof(pd_log.pd_manufacturer),
        const_cast<char *>(manufacturer.data()), manufacturer.size());
    if (retval != RET_OK) {
        lua_pushstring(L, "failed to copy manufacture data");
        lua_error(L);
    }
    retval = pd_log_adapter::adapter_pd_log_collect_read_io_latency_from_oob(ctrl_index, pd_device_id, &pd_log);
    if (retval != RET_OK) {
        lua_pushstring(L, "failed to get io latency data");
        if (pd_log.SASDevice.read_latency_statistics.data != nullptr) {
            g_free(pd_log.SASDevice.read_latency_statistics.data);
            pd_log.SASDevice.read_latency_statistics.data = nullptr;
        }
        lua_error(L);
    }
    guint32 read_data_len = pd_log.SASDevice.read_latency_statistics.data_length;
    guint8 *read_data = pd_log.SASDevice.read_latency_statistics.data;
    lua_pushlstring(L, (const char *)read_data, read_data_len);
    if (pd_log.SASDevice.read_latency_statistics.data != nullptr) {
        g_free(pd_log.SASDevice.read_latency_statistics.data);
        pd_log.SASDevice.read_latency_statistics.data = nullptr;
    }
    return 1;
}


static luawrap::n_ret pd_get_io_diagnose_info(lua_State *L, uint8_t ctrl_index, uint8_t protocol, uint16_t pd_device_id,
    std::vector<uint8_t> u8, std::vector<uint32_t> u32, std::vector<std::string_view> string)
{
    // 7: U8类型的参数数量 3: u32类型的参数数量 5: string类型的参数数量
    if (u8.size() != 7 or u32.size() != 3 or string.size() != 5) {
        lua_error(L);
    }

    PD_LOG_S pd_log;
    reset_zero(&pd_log);
    pd_log.log_source = u8[0];  // 0: 固定位置
    pd_log.log_rotate_num = u8[1];  // 1: 固定位置
    pd_log.pd_power_state = u8[2];  // 2: 固定位置
    pd_log.pd_interface_type = u8[3];  // 3: 固定位置
    pd_log.pd_media_type = u8[4];  // 4: 固定位置
    pd_log.pd_rebuild_progress = u8[5];  // 5: 固定位置
    pd_log.pd_health = u8[6];  // 6: 固定位置

    pd_log.pd_perfail_error_count = u32[0];  // 0: 固定位置
    pd_log.pd_media_error_count = u32[1];  // 1: 固定位置
    pd_log.pd_other_error_count = u32[2];  // 2: 固定位置

    (void)memcpy_s(pd_log.pd_device_name, sizeof(pd_log.pd_device_name),
        const_cast<char *>(string[0].data()), string[0].size());  // 0: 固定位置
    (void)memcpy_s(pd_log.pd_interface_type_str, sizeof(pd_log.pd_interface_type_str),
        const_cast<char *>(string[1].data()), string[1].size());  // 1: 固定位置
    (void)memcpy_s(pd_log.pd_manufacturer, sizeof(pd_log.pd_manufacturer),
        const_cast<char *>(string[2].data()), string[2].size());  // 2: 固定位置
    (void)memcpy_s(pd_log.pd_serial_number, sizeof(pd_log.pd_serial_number),
        const_cast<char *>(string[3].data()), string[3].size());  // 3: 固定位置
    (void)memcpy_s(pd_log.pd_model, sizeof(pd_log.pd_model),
        const_cast<char *>(string[4].data()), string[4].size());  // 4: 固定位置

    Json *disk_io_info_jso = NULL;
    if (JsonObjectCreate(&disk_io_info_jso) != JSON_OK) {
        lua_pushstring(L, "failed to create json_object");
        lua_error(L);
    }

    pd_log_adapter::adapter_io_deterioration_handle_basic_data(&pd_log, disk_io_info_jso);

    gint32 retval = RET_OK;
    if (protocol == PD_INTERFACE_TYPE_SATA) {
        retval = pd_log_adapter::adapter_pd_log_collect_sata_from_oob(ctrl_index, pd_device_id, &pd_log);
    } else if (protocol == PD_INTERFACE_TYPE_SAS) {
        retval = pd_log_adapter::adapter_pd_log_collect_sas_from_oob(ctrl_index, pd_device_id, &pd_log);
    }
    if (retval == RET_OK) {
        pd_log_adapter::adapter_io_deterioration_handle_data(&pd_log, disk_io_info_jso);
    }
    pd_log_adapter::adapter_pd_log_clear_data(&pd_log);

    const char *s = dal_json_object_get_str(disk_io_info_jso);
    JsonObjectRelease(disk_io_info_jso);
    lua_pushstring(L, s);
    return 1;
}

static luawrap::n_ret pd_log_write_subhealthy_info(lua_State *L, uint32_t estimated_remaining_lifespan,
    std::vector<uint8_t> u8, std::vector<uint64_t> u64, std::vector<std::string_view> string)
{
    // 4: U8类型的参数数量 8: u64类型的参数数量 2: string类型的参数数量
    if (u8.size() != 4 or u64.size() != 8 or string.size() != 2) {
        lua_error(L);
    }
    
    SML_PD_SSD_WRITE_AMP_FACTOR_S ssd_write_amp_factor;
    WRITE_AMP_LAST_INFO_S write_amp_last_info;
    reset_zero(&ssd_write_amp_factor);
    ssd_write_amp_factor.o_write_amp_cur_info.update_support_flag = u8[0];  // 0: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.hw_defined.valid_flag = u8[1];  // 1: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.vendor.valid_flag = u8[2];  // 2: 固定位置
    write_amp_last_info.first_start_flag = u8[3];  // 3: 固定位置

    ssd_write_amp_factor.o_write_amp_cur_info.vendor.nand_write = u64[0];  // 0: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.vendor.host_write = u64[1];  // 1: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.hw_defined.nand_write_l = u64[2];  // 2: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.hw_defined.nand_write_h = u64[3];  // 3: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.hw_defined.host_write_l = u64[4];  // 4: 固定位置
    ssd_write_amp_factor.o_write_amp_cur_info.hw_defined.host_write_h = u64[5];  // 5: 固定位置
    write_amp_last_info.last_nand_written = u64[6];  // 6: 固定位置
    write_amp_last_info.last_host_written = u64[7];  // 7: 固定位置

    char device_name[32] = {0};
    (void)memcpy_s(device_name, sizeof(device_name),
        const_cast<char *>(string[0].data()), string[0].size());  // 0: 固定位置
    (void)memcpy_s(ssd_write_amp_factor.o_serial_num, sizeof(ssd_write_amp_factor.o_serial_num),
        const_cast<char *>(string[0].data()), string[1].size());  // 1: 固定位置

    Json *disk_write_amp_jso = NULL;
    if (JsonObjectCreate(&disk_write_amp_jso) != JSON_OK) {
        lua_pushstring(L, "failed to create json_object");
        lua_error(L);
    }

    pd_log_adapter::adapter_calc_and_save_write_amp_value_by_smart_info(
        device_name, estimated_remaining_lifespan, &ssd_write_amp_factor, &write_amp_last_info, disk_write_amp_jso);

    const char *s = dal_json_object_get_str(disk_write_amp_jso);
    JsonObjectRelease(disk_write_amp_jso);
    lua_pushstring(L, s);
    return 1;
}

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

    register_pd_info(L, t);
    t.set("pd_operation", c_func_wrap(L, pd_operation));

    register_pd_sas_smart_info(L, t);
    register_slow_disk_data(L, t);
    t.set("pd_diag_encl_comm_error", c_func_wrap(L, pd_diag_encl_comm_error));
    t.set("pd_diag_sense_error", c_func_wrap(L, pd_diag_sense_error));
    t.set("pd_diag_by_smart_log", c_func_wrap(L, pd_diag_by_smart_log));
    t.set("pd_diag_by_ext_error_log", c_func_wrap(L, pd_diag_by_ext_error_log));
    t.set("pd_diag_by_ext_self_test_log", c_func_wrap(L, pd_diag_by_ext_self_test_log));
    t.set("pd_diag_get_sata_self_test_status", c_func_wrap(L, pd_diag_get_sata_self_test_status));
    t.set("pd_log_get_drive_log", c_func_wrap(L, pd_log_get_drive_log));

    t.set("pd_get_smart_info_str", c_func_wrap(L, pd_get_smart_info_str));
    t.set("pd_get_io_diagnose_info", c_func_wrap(L, pd_get_io_diagnose_info));
    t.set("pd_get_write_io_latency_info", c_func_wrap(L, pd_get_write_io_latency_info));
    t.set("pd_get_read_io_latency_info", c_func_wrap(L, pd_get_read_io_latency_info));
    t.set("pd_log_write_subhealthy_info", c_func_wrap(L, pd_log_write_subhealthy_info));
}
}  // namespace sml
