-- 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 utils = require 'mc.utils'

local m = {}

local DEVICE_TYPE_PCIE_CARD<const> = 8
local DEVICE_TYPE_MEZZ<const> = 11
local DEVICE_TYPE_NIC_CARD<const> = 13
local DEVICE_TYPE_OCP<const> = 83
local NETCARD_TYPE_LOM<const> = 1
local NETCARD_TYPE_LOM2<const> = 5

-- 判断component对象是否是网卡类型
local function is_net_card(obj_type)
    if obj_type ~= DEVICE_TYPE_MEZZ and obj_type ~= DEVICE_TYPE_NIC_CARD and
        obj_type ~= DEVICE_TYPE_PCIE_CARD and obj_type ~= DEVICE_TYPE_OCP then
        return false
    end

    return true
end

local function get_net_card_component(net_card, component_list)
    local names = utils.split(net_card, '_')
    for i = 1, #component_list do
        local names_2 = utils.split(component_list[i], '_')
        if names_2[#names_2] == names[#names] then
            local obj = mdb.get_object(bus, component_list[i], "bmc.kepler.Systems.Component")
            if is_net_card(obj.Type) then
                return obj
            end
        end
    end
end

function m.get_net_card_function(net_card, component_list)
    local obj = get_net_card_component(net_card, component_list)
    if not obj then
        return ''
    end
    return obj.Function or ''
end

function m.get_net_card_type(net_card, component_list)
    local obj = get_net_card_component(net_card, component_list)
    if not obj then
        return ''
    end
    return obj.Type or ''
end

-- 删除/bmc/kepler/Systems/1/NetworkAdapters路径下的1级目录，保证路径为Ports
function m.get_net_port_list(net_card_list, net_port_list)
    local ret = {}
    for i = 1, #net_port_list do
        for j = 1, #net_card_list do
            if string.find(net_port_list[i], net_card_list[j]) and net_port_list[i] ~= net_card_list[j] then
                ret[#ret + 1] = net_port_list[i]
            end
        end
    end
    return ret
end

function m.get_ocp_card_list(net_card_list, component_list)
    local ret = {}
    for i = 1, #net_card_list do
        local type = m.get_net_card_type(net_card_list[i], component_list)
        if type == DEVICE_TYPE_OCP then
            ret[#ret + 1] = net_card_list[i]
        end
    end
    return ret
end

function m.get_list_index(input)
    for i = 1, #input do
        if input[i].netCardIndex then
            input[i].netCardIndex = i
        end
        if input[i].businessPortIndex then
            input[i].businessPortIndex = i
        end
        if input[i].oCPCardIndex then
            input[i].oCPCardIndex = i
        end
    end
    return input
end

local function get_network_obj(port_path, network_adapters)
    local network_port = mdb.get_object(bus, port_path, "bmc.kepler.Systems.NetworkPort")
    if not network_port then
        return
    end
    for _, network_adapter in pairs(network_adapters) do
        if network_adapter.ID == network_port.NetworkAdapterId then
            return network_port, network_adapter
        end
    end
end

function m.get_lom_property(ports)
    local res = {}
    if not ports then
        return res
    end
    local network_adapters = mdb.get_sub_objects(bus, '/bmc/kepler/Systems', 'bmc.kepler.Systems.NetworkAdapter', 3)
    if not network_adapters then
        return res
    end
    local index = 1
    local mac, network_port, network_adapter
    for _, port_path in pairs(ports) do
        network_port, network_adapter = get_network_obj(port_path, network_adapters)
        if not network_port or not network_adapter then
            goto continue
        end
        if network_adapter.Type == NETCARD_TYPE_LOM or network_adapter.Type == NETCARD_TYPE_LOM2 then
            mac = network_port.MACAddress ~= 'N/A' and network_port.MACAddress or '00:00:00:00:00:00'
            res[#res + 1] = {
                lomIndex = index,
                lomMACAddress = mac
            }
            index = index + 1
        end
        ::continue::
    end
    return res
end

function m.get_ocp_card_entire_status(pcie_device_paths, pcie_card_paths)
    local max_health = 0
    local fatal_error = 0
    local ocp_device_count = 0
    local ocp_card_count = 0
    local ok, ocp_device_obj, ocp_card_obj
    for _, v in pairs(pcie_device_paths) do
        ocp_device_obj = mdb.get_object(bus, v, "bmc.kepler.Systems.PCIeDevices.PCIeDevice")

        if string.find(ocp_device_obj.DeviceName, 'OCP') then
            ocp_device_count = ocp_device_count + 1
            local ras_obj = mdb.get_object(bus, v, "bmc.kepler.Systems.PCIeDevices.PCIeDevice.RAS")
            fatal_error = fatal_error | ras_obj.FatalError
        end
    end
    for _, v in pairs(pcie_card_paths) do
        ok, ocp_card_obj = pcall(mdb.get_object, bus, v, 'bmc.kepler.Systems.PCIeDevices.PCIeCard')
        if ok and string.find(ocp_card_obj.DeviceName, 'OCP') then
            ocp_card_count = ocp_card_count + 1
            max_health = ocp_card_obj.Health > max_health and ocp_card_obj.Health or max_health
        end
    end
    if ocp_device_count == 0 or ocp_card_count == 0 then
        return 5 -- 5表示不存在ocp卡
    end
    if fatal_error ~= 0 and max_health < 2 then
        max_health = 2 -- 有error则至少返回3，3表示major
    end
    return max_health + 1
end

function m.get_link_status(Input)
    local link_status = Input[1]
    local mac = Input[2]
    if Input[3] == 'NPU' then
        return link_status
    end
    if mac == "00:00:00:00:00:00" then
        return "Disable"
    else
        if link_status and (link_status == "Disconnected" or link_status == "Connected") then
            return link_status
        else
            return "N/A"
        end
    end
end

function m.get_os_link_status(Input)
    local link_status = Input[1]
    if link_status and (link_status == "LinkUp" or link_status == "LinkDown" or link_status == "NoLink") then
        return link_status
    else
        return "N/A"
    end
end

return m
