-- Copyright (c) 2025 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.
local bs = require 'mc.bitstring'
local utils = require 'mc.utils'

local MCTP_PLDM_SENSOR_OPERATION_STATE_ENABLE<const> = 0x00
local MCTP_EMULEX_SENSOR_STATE_CONNECT<const> = 0x01
local FW_VERSION_TLV_TYPE = 0x02
local PART_NUMBER_TLV_TYPE = 0x03
local SERIAL_NUMBER_TLV_TYPE = 0x04
local WWNN_TLV_TYPE = 0x83
local WWPN_TLV_TYPE = 0x84

local pldm_sensor_rsp = bs.new([[<<
    sensor_data_size:8,
    operational_state:8,
    event_message_enable:8,
    present_state:8,
    previous_state:8,
    event_state:8,
    present_reading/string
>>]])

local pldm_state_sensor_rsp = bs.new([[<<
    composite_sensor_count:8,
    operational_state:8,
    present_state:8,
    previous_state:8,
    event_state:8
>>]])

local function get_link_speed(data)
    local rsp = pldm_sensor_rsp:unpack(data, true)
    if rsp.operational_state ~=
        MCTP_PLDM_SENSOR_OPERATION_STATE_ENABLE then
        return 0
    end
    if rsp.sensor_data_size ~= 2 then -- 网口速率为2个字节 
        return 0
    end
    local r = bs.new([[<<speed:16>>]]):unpack(
                  rsp.present_reading)
    return r and r.speed or 0
end

local function get_link_status(data)
    local rsp = pldm_state_sensor_rsp:unpack(data, true)
    if rsp.operational_state ~=
        MCTP_PLDM_SENSOR_OPERATION_STATE_ENABLE then
        return 'N/A'
    end
    if rsp.present_state ~= MCTP_EMULEX_SENSOR_STATE_CONNECT then
        return 'Disconnected'
    end
    return 'Connected'
end

local function get_wwnn_wwpn(input_str, prefix_type)
    -- 检查输入是否为空
    if not input_str or not prefix_type then
        return nil
    end

    -- 构建用于匹配的完整前缀字符串
    local expected_prefix = prefix_type .. ":"

    -- 检查输入字符串是否以期望的前缀开头
    if string.sub(input_str, 1, #expected_prefix) ~= expected_prefix then
        return nil -- 前缀不匹配
    end

    -- 提取冒号后面的部分（16个十六进制字符）
    local hex_part = string.sub(input_str, #expected_prefix + 1)

    -- 检查长度是否为16
    if #hex_part ~= 16 then
        return nil -- 长度不符合要求
    end

    -- 验证是否全部为十六进制字符
    if not string.match(hex_part, "^[0-9A-F]+$") then
        return nil -- 包含非十六进制字符
    end

    -- 按每两个字符插入冒号进行格式化
    local formatted_output = ""
    for i = 1, #hex_part, 2 do
        if i > 1 then
            formatted_output = formatted_output .. ":"
        end
        formatted_output = formatted_output .. string.sub(hex_part, i, i + 1)
    end

    return formatted_output
end

local Emulex = {
    protocol_dependencies = {
        pldm_sensor = { endpoint = nil },
        pldm_emulex_fru = { endpoint = nil }
    },
    properties = {
        ChipTemp = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {command_code = 0x11, sensor_id = 0x21},
            response = function(data)
                local rsp = pldm_sensor_rsp:unpack(data, true)
                if rsp.operational_state ~=
                    MCTP_PLDM_SENSOR_OPERATION_STATE_ENABLE then
                    return 0xff
                end
                if rsp.sensor_data_size ~= 0 then -- emulex温度为s8
                    return 0xff
                end
                local r = bs.new([[<<temp:8>>]]):unpack(rsp.present_reading)
                return r and r.temp or 0xff
            end
        },
        Port0LinkSpeed = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 10,
            request = {command_code = 0x11, sensor_id = 0x31},
            response = function(data)
                return get_link_speed(data)
            end
        },
        Port1LinkSpeed = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 10,
            request = {command_code = 0x11, sensor_id = 0x32},
            response = function(data)
                return get_link_speed(data)
            end
        },
        Port0LinkStatus = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 2,
            request = {command_code = 0x21, sensor_id = 0x81},
            response = function(data)
                return get_link_status(data)
            end
        },
        Port1LinkStatus = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 2,
            request = {command_code = 0x21, sensor_id = 0x82},
            response = function(data)
                return get_link_status(data)
            end
        },
        FirmwareVersion = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x03},
            response = function(data)
                if data == nil then return 'N/A' end
                for _, tlv in pairs(data) do
                    if tlv.type == FW_VERSION_TLV_TYPE then
                        return string.match(tlv.value,
                                            'FW Version:(%d+%.?%d*%.?%d*%.?%d*)')
                    end
                end
                return 'N/A'
            end
        },
        PartNumber = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x01},
            response = function(data)
                if data == nil then return 'N/A' end
                for _, tlv in pairs(data) do
                    if tlv.type == PART_NUMBER_TLV_TYPE then
                        return tlv.value
                    end
                end
                return 'N/A'
            end
        },
        SerialNumber = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x01},
            response = function(data)
                if data == nil then return 'N/A' end
                for _, tlv in pairs(data) do
                    if tlv.type == SERIAL_NUMBER_TLV_TYPE then
                        return tlv.value
                    end
                end
                return 'N/A'
            end
        },
        Port0WorkWWNN = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x04},
            response = function(data)
                if data == nil then return '' end
                for _, tlv in pairs(data) do
                    if tlv.type == WWNN_TLV_TYPE then
                        return get_wwnn_wwpn(tlv.value, 'WWNN')
                    end
                end
                return nil
            end
        },
        Port1WorkWWNN = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x05},
            response = function(data)
                if data == nil then return ''end
                for _, tlv in pairs(data) do
                    if tlv.type == WWNN_TLV_TYPE then
                        return get_wwnn_wwpn(tlv.value, 'WWNN')
                    end
                end
                return nil
            end
        },
        Port0WorkWWPN = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x04},
            response = function(data)
                if data == nil then return '' end
                for _, tlv in pairs(data) do
                    if tlv.type == WWPN_TLV_TYPE then
                        return get_wwnn_wwpn(tlv.value, 'WWPN')
                    end
                end
                return nil
            end
        },
        AllInfo = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0xff},
            response = function()
            end
        },
        Port1WorkWWPN = {
            protocol = 'pldm_emulex_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {record_id = 0x05},
            response = function(data)
                if data == nil then return '' end
                for _, tlv in pairs(data) do
                    if tlv.type == WWPN_TLV_TYPE then
                        return get_wwnn_wwpn(tlv.value, 'WWPN')
                    end
                end
                return nil
            end
        }
    }
}

return {
    smbus = nil,
    mctp = nil,
    mctp_pldm = function(endpoint)
        local obj = utils.table_copy(Emulex)
        obj.protocol_dependencies.pldm_sensor.endpoint = endpoint
        obj.protocol_dependencies.pldm_emulex_fru.endpoint = endpoint
        return obj
    end
}