-- 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.

-- Description: PCIe带外管理lib库入口
local log = require 'mc.logging'
local def = require 'device.lib_pcie_oob_mgmt.def'
local smbus = require 'device.lib_pcie_oob_mgmt.smbus'

local M = {}

local IDENTIFY_CODE <const> = 0x1eee

-- 底层协议调用入口
local protocol_entrance <const> = {
    [def.e_protocol.I2C] = function (...) return false end,
    [def.e_protocol.SMBUS] = smbus.get_info,
    [def.e_protocol.STD_SMBUS] = function (...) return false end
}

local function is_opcode_support(opcodes, opcode)
    for _, item in pairs(opcodes) do
        if item == opcode then
            return true
        end
    end
    return false
end

-- 在发送底层协议命令之前进行处理
-- 如果不同协议存在差异化处理，优先使用差异化处理函数，否则使用COMMON
local pre_process <const> = {
    ['COMMON'] = function (chip_info, opcode)
        -- 根据opcode判断是否支持该能力
        if not is_opcode_support(chip_info.opcodes, opcode) then
            return false
        end
        return true
    end,
    [def.e_protocol.I2C] = nil,
    [def.e_protocol.SMBUS] = nil,
    [def.e_protocol.STD_SMBUS] = nil
}

-- 对协议底层返回的响应进行处理
-- 如果不同协议存在差异化处理，优先使用差异化处理函数，否则使用COMMON
local post_process <const> = {
    [def.e_opcode.CAPABILITY] = {
        ['COMMON'] = function (resp)
            if resp.id ~= IDENTIFY_CODE then
                log:error('Identification code matching failed, Id=%d', resp.id)
                return false, {err_code = def.e_err_code.IDENTIFY_CODE_ERROR}
            end
            local opcodes_bin = string.sub(resp.opcodes, 1, resp.opcode_num * 2)
            local format = ''
            for _ = 1, resp.opcode_num do
                format = format .. 'H'
            end
            local opcodes = {string.unpack(format, opcodes_bin)}
            return true, opcodes
        end,
        [def.e_protocol.I2C] = nil,
        [def.e_protocol.SMBUS] = nil,
        [def.e_protocol.STD_SMBUS] = nil,
    },
    [def.e_opcode.GET_FIRMWARE_VERSION] = {
        ['COMMON'] = function (resp)
            local version_str = string.format('%d.%d.%d.%d', resp.major, resp.minor // 10, resp.minor % 10,
                                              resp.revision)
            return true, version_str
        end
    }
}

-- 信息获取统一入口
---@param chip_info ChipInfo
function M.get_info(chip_info, opcode)
    -- 预处理
    local ok
    if pre_process[chip_info.protocol] then
        ok = pre_process[chip_info.protocol](chip_info, opcode)
    else
        ok = pre_process['COMMON'](chip_info, opcode)
    end
    if not ok then
        return false
    end

    -- 底层命令调用
    local ok, resp = protocol_entrance[chip_info.protocol](opcode, chip_info.io, chip_info.delay_time,
                                                           chip_info.max_frame_len)
    if not ok then
        return false
    end

    -- 后处理
    if post_process[opcode][chip_info.protocol] then
        ok, resp = post_process[opcode][chip_info.protocol](resp)
    else
        ok, resp = post_process[opcode]['COMMON'](resp)
    end

    return ok, resp
end

return M