-- 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 log = require 'mc.logging'
local c_object_manage = require 'mc.orm.object_manage'
local client = require 'network_adapter.client'
local c_network_port = require 'device.class.network_port'
local c_optical_module = require 'device.class.optical_module'
local card_resource_path = 'device.class.network_card_adapter.resource_tree'
local card_resource_init_protocol = require(card_resource_path .. '.card_resource_init_protocol')
local card_resource_update_bdf = require(card_resource_path .. '.card_resource_update_bdf')

local card_resource_listen_callback = {}

-- 由于现在光模块告警关联在网卡的health下，需要把网卡的health同步到光模块的health
local function update_op_health(resource_obj, _, value)
    local ops = c_optical_module.collection:fetch({NetworkAdapterId = resource_obj.NodeId})
    for _, op in ipairs(ops) do
        op:update_health(value)
    end
end

local function synchronize_port_bdf(resource_obj, key, value)
    log:notice('synchronize port bdf, name:%s, key:%s, value:%s', resource_obj.NodeId, key, value)
    -- 除NIC卡外，其他网卡DevBus不为0
    if value == 0 and key == 'DevBus' and resource_obj.Type ~= 1 then
        log:notice('synchronize port bdf fail, name:%s, DevBus is 0', resource_obj.NodeId)
        return
    end

    if value == 0 and key == 'Bus' and resource_obj.Type == 1 then
        log:notice('synchronize port bdf fail, name:%s, Bus is 0', resource_obj.NodeId)
        return
    end

    local ports = c_network_port.collection:fetch_by_position(resource_obj:get_position())
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    if resource_obj.SpecialPcieCard then
        resource_obj:next_tick(card_resource_update_bdf.update_1822_port_bdf, resource_obj, ports)
    else
        resource_obj:next_tick(card_resource_update_bdf.update_port_bdf, resource_obj, ports)
    end
    local ok, hardware_config = pcall(require, string.format('hardware_config.%s', resource_obj.Model))
    if not ok or not hardware_config then
        log:info('No specific hardware config file for this network adapter(model: %s)', resource_obj.Model)
        return
    end
    if hardware_config.mctp_pldm and resource_obj.SupportedMctp then
        resource_obj:next_tick(card_resource_init_protocol.init_pldm, resource_obj, hardware_config.mctp_pldm)
    end
    if hardware_config.mctp and resource_obj.SupportedMctp then
        resource_obj:next_tick(card_resource_init_protocol.init_ncsi, resource_obj, hardware_config.mctp, ports)
    end
    if hardware_config.lldp and resource_obj.SupportedLLDP then
        resource_obj:next_tick(card_resource_init_protocol.setup_vdpci_lldp_listener, resource_obj, ports)
    end
end

local function synchronize_port_node_id(resource_obj, _, value)
    local ports = c_network_port.collection:fetch_by_position(resource_obj:get_position())
    for _, port in pairs(ports) do
        port.NetworkAdapterId = value
    end
end

function card_resource_listen_callback.register_listen_callback(resource_obj)
    local switch = {
        Health = update_op_health,
        DevBus = synchronize_port_bdf,
        NodeId = synchronize_port_node_id,
        Bus = synchronize_port_bdf
    }
    resource_obj:connect_signal(resource_obj.on_property_changed, function(name, value)
        if switch[name] then
            switch[name](resource_obj, name, value)
        end
    end)

    local bus = c_object_manage.get_instance().bus
    if not bus then
        log:error(
            '[network_adapter]register listen callback: get bus failed, listen smbios status failed.')
        return
    end

    client:OnSmBiosInterfacesAdded(function (_, _, props)
        resource_obj.smbios_status = props.SmBiosStatus:value()
        log:debug('updtate smbios_status=%s', resource_obj.smbios_status)
    end)

    client:OnSmBiosPropertiesChanged(function (values, _, _)
        if not values.SmBiosStatus then
            return
        end
        resource_obj.smbios_status = values.SmBiosStatus:value()
        log:debug('updtate smbios_status=%s', resource_obj.smbios_status)
    end)
end

return card_resource_listen_callback