-- 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.
local json = require('cjson')
local m = {}

local json_format_firmware_info = {
    ['VrdVersion'] = true,
    ['CsrVersion'] = true,
    ['McuVersion'] = true,
    ['BiosVersion'] = true,
}

function m.get_all_firmware_info(infos)
    local res = {}
    for _, value in pairs(infos) do
        res[value.FirmwareId] = value.FirmwareInfo
    end
    for key, _ in pairs(json_format_firmware_info) do
        if res[key] then
            res[key] = json.decode(res[key].Value)
        else
            res[key] = {} -- 避免回显模板频繁判空
        end
    end
    return res
end

function m.get_product_unique_id(unique_id, vendor_id)
    local id = nil
    -- 防止product_mgmt组件没有上树
    if unique_id == nil or vendor_id == nil then
        return nil
    end

    -- 资源树上的unique_id和vendor_id应为'0x'+8位十六进制数字,共10位
    if string.len(unique_id) == 10 and string.len(vendor_id) == 10 then
        if string.upper(vendor_id) ~= '0XFFFFFFFF' then
            -- 若vendor_id为0x12345678, unique_id为0x87654321, 则拼接结果为0x1234567887654321
            id = string.format(
                '0x%08X%08X',
                tonumber(string.sub(vendor_id, 3), 16),
                tonumber(string.sub(unique_id, 3), 16)
            )
        else
            id = string.format('0x%08X', tonumber(string.sub(unique_id, 3), 16))
        end
    end

    return id
end

local board_types = {
    'CpuBoard',
    'RiserCard',
    'HddBackplane',
    'ExpBoard',
    'FanBoard',
    'NpuBoard',
    'PsuBoard',
    'PeuBoard',
    'M2TransferCard',
}

local function build_board_obj(board_obj, unit_obj)
    return {
        DeviceName = board_obj.DeviceName,
        Name = board_obj.Name,
        BoardID = board_obj.BoardID,
        PCBVersion = board_obj.PcbVersion,
        LogicUnit = board_obj.LogicUnit,
        LogicVersion = board_obj.LogicVersion,
        Slot = board_obj.Slot,
        MCUVersion = board_obj.MCUVersion,
        SRVersion = board_obj.SRVersion,
        MultiLogicUnit = board_obj.MultiLogicUnit,
        MultiLogicVersion = board_obj.MultiLogicVersion,
        UID = unit_obj and unit_obj.UID or nil,
    }
end

function m.get_all_boards()
    -- 多host场景下,CpuBoard可能挂在Systems/1、Systems/2等不同System节点下,因此从 /bmc/kepler/Systems 开始搜索
    local board_objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems', 'bmc.kepler.Systems.Board', 4)
    local unit_objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems', 'bmc.kepler.Systems.Board.Unit', 4)

    local unit_map = {}
    for _, unit_obj in pairs(unit_objs) do
        unit_map[unit_obj.path] = unit_obj
    end

    local grouped = {}
    for _, bt in ipairs(board_types) do
        grouped[bt] = {}
    end

    for _, board_obj in pairs(board_objs) do
        local bt = string.match(board_obj.path, 'Boards/(%w+)/')
        if bt and grouped[bt] then
            local obj = build_board_obj(board_obj, unit_map[board_obj.path])
            table.insert(grouped[bt], obj)
        end
    end

    for _, bt in ipairs(board_types) do
        table.sort(grouped[bt], function(a, b)
            return tonumber(a.Slot) < tonumber(b.Slot)
        end)
    end

    local result = {}
    for i, bt in ipairs(board_types) do
        result[i] = grouped[bt]
    end
    return result
end
function m.get_all_network_adapters()
    local network_adapter_objs = {}
    local mds_objs =
        mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/NetworkAdapters', 'bmc.kepler.Systems.NetworkAdapter')
    for _, mds_obj in pairs(mds_objs) do
        -- 仅保存板载网卡
        if mds_obj.Type == 1 then
            table.insert(network_adapter_objs, {
                Type = mds_obj.Type,
                DeviceLocator = mds_obj.DeviceLocator,
                BoardID = mds_obj.BoardID,
                ComponentUniqueID = mds_obj.ComponentUniqueID,
                PCBVersion = mds_obj.PCBVersion,
                Name = mds_obj.Name,
            })
        end
    end
    return network_adapter_objs
end

function m.get_all_psus()
    local psu_objs = {}
    local mds_objs =
        mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/PowerMgmt', 'bmc.kepler.Systems.PowerMgmt.OnePower')
    for _, mds_obj in pairs(mds_objs) do
        table.insert(psu_objs, {
            DeviceLocator = mds_obj.DeviceLocator,
            FirmwareVersion = mds_obj.FirmwareVersion,
            SlotNumber = mds_obj.SlotNumber,
        })
    end
    return psu_objs
end

function m.get_all_pcie_cards()
    local pcie_objs = {}
    local mds_objs = mdb.get_sub_objects(
        bus,
        '/bmc/kepler/Systems/1/PCIeDevices/PCIeCards',
        'bmc.kepler.Systems.PCIeDevices.PCIeCard',
        2
    )
    local dpu_objs =
        mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/PCIeDevices/PCIeCards', 'bmc.kepler.Systems.DPUCard', 2)
    local dpu_map = {}
    for _, dpu_obj in pairs(dpu_objs) do
        dpu_map[dpu_obj.path] = dpu_obj
    end
    for _, mds_obj in pairs(mds_objs) do
        local card = {
            BoardID = mds_obj.BoardID,
            PCBVersion = mds_obj.PcbVersion,
            Slot = mds_obj.SlotID,
            Name = mds_obj.Name,
        }
        local dpu_obj = dpu_map[mds_obj.path]
        if dpu_obj then
            card.SRVersion = dpu_obj.SRVersion
            card.UID = dpu_obj.UID
            card.MCUVersion = dpu_obj.MCUVersion
            card.VrdVersion = dpu_obj.VrdVersion
            card.CPLDVersion = dpu_obj.LogicVersion
            card.CPLDCount = dpu_obj.CPLDCount
            card.MultiLogicVersion = dpu_obj.MultiLogicVersion
        end
        table.insert(pcie_objs, card)
    end
    return pcie_objs
end

function m.get_all_security_modules()
    local sm_objs = {}
    local mds_objs =
        mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/SecurityModule', 'bmc.kepler.Systems.SecurityModule')
    for _, mds_obj in pairs(mds_objs) do
        table.insert(sm_objs, {
            FirmwareVersion = mds_obj.FirmwareVersion,
            Manufacturer = mds_obj.Manufacturer,
            Protocol = mds_obj.Protocol,
            ProtocolVersion = mds_obj.ProtocolVersion,
        })
    end
    return sm_objs
end

function m.get_retimer_version()
    local retimer_objs = {}
    local mds_objs =
        mdb.get_sub_objects(bus, '/bmc/kepler/UpdateService/FirmwareInventory', 'bmc.kepler.UpdateService.FirmwareInfo')
    for _, mds_obj in pairs(mds_objs) do
        if string.find(mds_obj.path, 'Retimer') then
            table.insert(retimer_objs, {
                Id = mds_obj.Id,
                Version = mds_obj.Version,
                Name = mds_obj.Name,
            })
        end
    end
    return retimer_objs
end

return m