-- 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 class = require 'mc.class'
local log = require 'mc.logging'
local mcu_object_base = require 'mcu.mcu_object'
local subcomp = require 'mcu.upgrade.sub_component'
local MCU_ENUMS = require 'mcu.enum.mcu_enums'
local cmn = require 'common'

local smc_mcu_object = class(mcu_object_base)

function smc_mcu_object:init()
    self.LibMaxSize = 0
    self.BinMaxSize = 0
    self.vrd_dump = {}
end

function smc_mcu_object:set_lib_max_size(lib_max_size)
    self.LibMaxSize = lib_max_size
end

function smc_mcu_object:set_bin_max_size(bin_max_size)
    self.BinMaxSize = bin_max_size
end

function smc_mcu_object:get_lib_max_size()
    return self.LibMaxSize
end

function smc_mcu_object:get_bin_max_size()
    return self.BinMaxSize
end

function smc_mcu_object:get_firmeware_info(index)
    return self.interface:get_upgrade_detail(index)
end

function smc_mcu_object:listen_vrd_abnormal()
    local abnormal_flag = false
    local retries = 0
    local value
    while true do
        value = self.interface:get_system_event()
        if not value or type(value) ~= 'number' then
            retries = retries + 1
        else
            retries = 0
            -- 第四位为基础板异常掉电事件
            if value & 8 ~= 0 and not abnormal_flag then
                log:notice('[Vrd]vrd abnormal appear, start to get dump')
                cmn.skynet.sleep(200) --硬件建议等待2s收集完成
                self.vrd_dump = self.interface:get_vrd_dump()
                abnormal_flag = true
            -- 硬件确认故障后收集一次，直到故障消失
            elseif value & 8 == 0 and abnormal_flag then
                log:notice('[Vrd]vrd abnormal disappear, restart to listen')
                abnormal_flag = false
            end
        end
        if retries == 10 then
            -- 连续10次获取不到认为命令不通，停止轮询
            break
        end
        cmn.skynet.sleep(500) -- 每5s检测一次
    end
end

function smc_mcu_object:get_vrd_dump_info()
    -- 没有触发异常则在一键收集时收一次
    if #self.vrd_dump == 0 then
        self.vrd_dump = self.interface:get_vrd_dump()
    end
    return self.vrd_dump
end

function smc_mcu_object:register_firmware_inventory(bus)
    -- 查询子组件数量
    local cnt, lib_max_size, bin_max_size = self.interface:get_upgrade_cnt()
    log:notice('[McuUpgrade]get smc firmware cnt: %s', cnt)
    if not cnt or not lib_max_size or not bin_max_size then
        log:warn('get SMC sub-component count fail')
    end

    self:set_lib_max_size(lib_max_size)
    self:set_bin_max_size(bin_max_size)
    if cnt then
        -- 索引从0开始,查询子件详细信息并向FirmwareInventory注册, 不包括MCU和VRD子组件
        self.sub_component_info_list = {}
        for i = 0, cnt - 1 do
            local detail = self.interface:get_upgrade_detail(i)
            if not detail then
                goto continue
            end
            table.insert(self.sub_component_info_list, detail)
            local component = subcomp.new(i, detail)

            if not component or next(component) == nil then
                goto continue
            end

            component:set_ref_mcu(self)
            self.super.insert_subcomp_detail(self, component)
            -- 将支持的MCU子件对象注册到Firmware
            if component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_MCU and
                component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_VRD and
                MCU_ENUMS.SUB_COMPONENT_TYPE_TABLE[component.Type] then
                component:register_component_firmware_info(bus)
            end

            ::continue::
        end
        self.vrd_info_changed:emit(self.sub_component_info_list)
    end
    -- 将vrd子件对象注册到Firmware
    subcomp.register_vrd_firmware_info(self, bus)

    local component = self.mcu.SubCompList[1]
    if component and next(component) ~= nil then
        if component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_MCU or component.No ~= 0 then
            log:warn('the first sub-component is not MCU')
        else
            self.super.set_mcu_major_version(self, component.MajorVersion)
            self.super.set_mcu_minor_version(self, component.MinorVersion)
            self.super.set_mcu_revision(self, component.Revision)
            self.super.register_mcu_firmware_inventory(self, bus)
        end
    end
    self.vrd_load = true
    log:notice('[McuUpgrade] (%s)register SMC version to FirmwareInventory done', self.mcu.BoardType)
end

return smc_mcu_object