-- 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 skynet = require 'skynet'
local class = require 'mc.class'
local log = require 'mc.logging'
local c_bcu = require 'unit_manager.class.unit.bcu.bcu'
local cmn = require 'common'

---@class CpuBoard: BCU @CPU板
local c_cpu_board = class(c_bcu)

local UNAVAILABLE<const> = 'N/A'
local START_FINISH = 1

function c_cpu_board:get_bmc_start_flag()
    local ok, start_flag = pcall(function()
        return self:get_prop('BmcStartFlag')
    end)

    if not ok or not start_flag then
        log:info('[BCU]cannot get BMC startup completed flag from CPLD')
        return nil
    end
    log:info('[BCU]get BMC startup completed flag %s', start_flag)
    return start_flag
end

function c_cpu_board:update_bmc_start_flag()
    skynet.fork_loop({count = 0}, function()
        local start_flag = self:get_bmc_start_flag()
        if start_flag == START_FINISH then
            return
        end

        while true do
            local ok = pcall(function()
                -- 需要通过硬件访问代理服务设置BMC启动的标志给天池组件BCU的CPLD, 因此使用pcall处理
                self:set_prop('BmcStartFlag', START_FINISH)
            end)

            if ok then
                -- 再读一次确认设置是否生效
                start_flag = self:get_bmc_start_flag()
                if start_flag == START_FINISH then
                    log:notice("[BCU]set BMC startup completed flag to CPLD successfully")
                    return
                end
            end

            skynet.sleep(200)
        end
    end)
end

function c_cpu_board:task_update()
    self:task_update_mcu_version()
end

local function logic_version_id_to_version(logic_version_id)
    logic_version_id = logic_version_id & 0xff
    return string.format('%u.%02u', logic_version_id >> 4, logic_version_id & 0xf) -- 高4位和低4位拼装成版本号
end

function c_cpu_board:update_logic_version(multi_logic_version)
    if not multi_logic_version.CPLD1 then
        local ok, version_number = pcall(function()
            return self:get_prop('LogicVersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local cpld1_version_str = logic_version_id_to_version(version_number)
            self:set_prop('LogicVersion', cpld1_version_str)
            multi_logic_version.CPLD1 = cpld1_version_str
        end
    end

    if not multi_logic_version.CPLD2 then
        local ok, version_number = pcall(function()
            return self:get_prop('CPLD2VersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local cpld2_version_str = logic_version_id_to_version(version_number)
            multi_logic_version.CPLD2 = cpld2_version_str
        end
    end
    
    if not multi_logic_version.CPLD3 then
        local ok, version_number = pcall(function()
            return self:get_prop('CPLD3VersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local cpld3_version_str = logic_version_id_to_version(version_number)
            multi_logic_version.CPLD3 = cpld3_version_str
        end
    end
end

local function record_get_version_log(self, cpld1_ver, cpld2_ver, cpld3_ver)
    local mds_obj = self:get_mds_obj()
    if mds_obj then
        log:notice("get board type is %s%s", mds_obj.BoardType, mds_obj.Slot)
    end
    log:notice("get version CPLD1Version = %s, CPLD2Version = %s, CPLD3Version = %s", cpld1_ver, cpld2_ver, cpld3_ver)
end

function c_cpu_board:task_update_logic_version()
    if self:get_prop('LogicVersion') == UNAVAILABLE then
        return
    end
    cmn.skynet.fork(function()
        local last_multi_logic_version = {}
        local multi_logic_version = {}
        local timeout = 360
        while timeout > 0 do
            cmn.skynet.sleep(500)
            self:update_logic_version(multi_logic_version)
            self:set_prop('MultiLogicVersion', multi_logic_version)
            if last_multi_logic_version.CPLD1 ~= multi_logic_version.CPLD1 or
                last_multi_logic_version.CPLD2 ~= multi_logic_version.CPLD2 or
                last_multi_logic_version.CPLD3 ~= multi_logic_version.CPLD3 then
                last_multi_logic_version.CPLD1 = multi_logic_version.CPLD1
                last_multi_logic_version.CPLD2 = multi_logic_version.CPLD2
                last_multi_logic_version.CPLD3 = multi_logic_version.CPLD3
                record_get_version_log(self, multi_logic_version.CPLD1,
                multi_logic_version.CPLD2, multi_logic_version.CPLD3)
            end

            if multi_logic_version.CPLD1 and multi_logic_version.CPLD2 and
                multi_logic_version.CPLD3 then
                break
            end

            timeout = timeout - 5
        end
    end)
end

return c_cpu_board