-- 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 client = require 'network_adapter.client'
local c_network_port = require 'device.class.network_port'
local c_optical_module = require 'device.class.optical_module'
local skynet = require 'skynet'

local card_npu = {}

local OPTICALMODULE_TEMP_NA_VALUE<const> = 255

local function update_npu_max_sfp_temp(orm_obj, ops)
    local max_temp, opt_temp
    max_temp = 0
    for _, op in ipairs(ops) do
        -- 光模块温度不为非法值，则参与最值比较
        opt_temp = (op.ReadingCelsius < OPTICALMODULE_TEMP_NA_VALUE) and op.ReadingCelsius or 0
        max_temp = opt_temp > max_temp and opt_temp or max_temp
    end
    orm_obj.SFPMaxTemperatureCelsius = max_temp
    -- 与npu光模块信息获取频率保持一致
    orm_obj:sleep_ms(15000)
end

local function set_npu_max_sfp_temp(orm_obj)
    orm_obj:sleep_ms(15000)  -- 等待光模块上树开始获取npu信息
    local ops = c_optical_module.collection:fetch({NetworkAdapterId = orm_obj.NodeId})
    orm_obj:next_tick(function()
        while true do
            update_npu_max_sfp_temp(orm_obj, ops)
        end
    end)
end

local function update_max_npu_cdr_temp_from_imu(orm_obj, ports)
    local port_temp, cdr_manuf
    local max_temp = 0
    for _, port in ipairs(ports) do
        port_temp, cdr_manuf = port:get_npu_cdr_temp_from_imu()
        max_temp = (max_temp > port_temp) and max_temp or port_temp
    end
    orm_obj.TemperatureCelsius = max_temp
    if cdr_manuf ~= "" and orm_obj.ChipManufacturer == "" then
        orm_obj.ChipManufacturer = cdr_manuf
    end
end

local function get_max_npu_cdr_temp_from_imu(orm_obj)
    orm_obj:sleep_ms(15000) -- 等待NPU网口对象全部上树后开始启动更新cdr温度线程
    local ports = orm_obj.children
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    log:notice('start to update max npu cdr temp')
    -- 每15s轮询从所有npu获取cdr温度并比对最大值，将最值赋值上树
    skynet.fork_loop({count = 0}, function()
        while true do
            update_max_npu_cdr_temp_from_imu(orm_obj, ports)
            orm_obj:sleep_ms(15000)
        end
    end)
end

local function update_link_status_to_default()
    c_network_port.collection:fold(
        function (_ , obj)
            local parent = obj.parent
            if parent and parent.Model and string.match(parent.Model, 'NPU') then
                obj.LinkStatusNumeric = 255
            end
        end
    )
end

local function register_npu_listen_callback()
    -- 监听到系统复位或者下电时，将网卡状态恢复为初始值（255）
    client:OnFruCtrlPropertiesChanged(
        function (values, _, _)
            if values.SysResetDetected then
                local sys_reset_detected = values.SysResetDetected:value()
                if sys_reset_detected == 1 then
                    log:notice('system reset,npu port link status update to default(255)')
                    update_link_status_to_default()
                end
            end
            if values.PowerState then
                local power_state = values.PowerState:value()
                if power_state == 'OFF' then
                    log:notice('system power off,npu port link status update to default(255)')
                    update_link_status_to_default()
                end
            end
        end
    )
end

function card_npu.init_npu_proc(orm_obj)
    orm_obj:next_tick(function ()
        get_max_npu_cdr_temp_from_imu(orm_obj)
    end)
    -- npu虚拟网卡开启监听信号函数
    register_npu_listen_callback()
    orm_obj:next_tick(set_npu_max_sfp_temp, orm_obj)
end

return card_npu