-- 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_QLOGIC_SENSOR_STATE_CONNECT <const> = 0x01

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_wwnn_wwpn(data)
    local res = ''
    for i = 1, 8, 1 do
        res = res .. string.format('%02x', data:sub(i, i):byte()) .. ':'
    end
    return #res > 0 and res:sub(1, -2) or ''
end

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_QLOGIC_SENSOR_STATE_CONNECT then
        return 'Disconnected'
    end
    return 'Connected'
end

local Qlogic = {
    protocol_dependencies = {
        pldm_sensor = { endpoint = nil },
        pldm_qlogic_fru = { endpoint = nil }
    },
    properties = {
        ChipTemp = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { command_code = 0x11, sensor_id = 0x12C },
            response = function(data)
                local rsp = pldm_sensor_rsp:unpack(data, true)
                if rsp.operational_state ~=
                    MCTP_PLDM_SENSOR_OPERATION_STATE_ENABLE then
                    return 0xffff
                end
                if rsp.sensor_data_size ~= 2 then -- qlogic温度为2个字节
                    return 0xffff
                end
                local r = bs.new([[<<temp:16>>]]):unpack(
                    rsp.present_reading)
                return r and r.temp or 0xffff
            end
        },
        Port0LinkSpeed = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { command_code = 0x11, sensor_id = 0x64 },
            response = function(data)
                return get_link_speed(data)
            end
        },
        Port1LinkSpeed = {
            protocol = 'pldm_sensor',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { command_code = 0x11, sensor_id = 0x65 },
            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 = 0xc8 },
            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 = 0xc9 },
            response = function(data)
                return get_link_status(data)
            end
        },
        FirmwareVersion = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0xfe, field_type = 0x06 },
            response = function(data)
                if data == nil then return 'N/A' end
                local fw_version = string.format('%d.%02d.%02d',
                    data:sub(4, 4):byte(),
                    data:sub(3, 3):byte(),
                    data:sub(2, 2):byte())
                return fw_version and fw_version or 'N/A'
            end
        },
        PartNumber = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0x01, field_type = 0x03 },
            response = function(data)
                return data and data or 'N/A'
            end
        },
        SerialNumber = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0x01, field_type = 0x04 },
            response = function(data)
                return data and data or 'N/A'
            end
        },
        Port0WorkWWNN = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0xfe, field_type = 0x81 },
            response = function(data)
                if data == nil then return '' end
                return get_wwnn_wwpn(data)
            end
        },
        Port1WorkWWNN = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0xfe, field_type = 0xA1 },
            response = function(data)
                if data == nil then return '' end
                return get_wwnn_wwpn(data)
            end
        },
        Port0WorkWWPN = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0xfe, field_type = 0x80 },
            response = function(data)
                if data == nil then return '' end
                return get_wwnn_wwpn(data)
            end
        },
        Port1WorkWWPN = {
            protocol = 'pldm_qlogic_fru',
            action = 'on_schedule',
            period_in_sec = 60,
            request = { record_type = 0xfe, field_type = 0xA0 },
            response = function(data)
                if data == nil then return '' end
                return get_wwnn_wwpn(data)
            end
        }
    }
}

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