-- 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.

-- Description: 升级信号的回调处理
local log = require 'mc.logging'
local context = require 'mc.context'
local fw_mgmt_client = require 'general_hardware.client'
local factory = require 'factory'
local cmn = require 'common'
local cfg = require 'unit_manager.class.logic_fw.upgrade.fw_cfgs'
local process_handle = require 'unit_manager.class.logic_fw.upgrade.process'
local valid_handle = require 'unit_manager.class.logic_fw.upgrade.valid'
local defs = require 'unit_manager.class.logic_fw.comm_defs'
local iic_upgrade = require 'unit_manager.class.logic_fw.upgrade.iic_process'

local fw_upgrade = {}
local g_cpld_upgrade_list

local function generate_upgrade_list(cpld_nums)
    local result = {}
    for i = 1, cpld_nums do
        table.insert(result, i)
    end
    return result
end

local function check_hot_upgrade(cfg_list, index)
    if not cfg_list or #cfg_list == 0 then
        return defs.RET.ERR
    end
    -- 检测是否存在ColdValidList且不为空
    if #cfg_list[index].cold_valid_list ~= 0 then
        fw_upgrade.hot_upgrade = false
        g_cpld_upgrade_list = cfg_list[index].cold_valid_list
        log:notice("cpld cold upgrade, upgrade list = %s", cmn.join(g_cpld_upgrade_list, ", "))
        return defs.RET.OK
    end
    if #cfg_list[index].hot_valid_list ~= 0 then
        fw_upgrade.hot_upgrade = true
        g_cpld_upgrade_list = cfg_list[index].hot_valid_list
        log:notice("cpld hot upgrade, upgrade list = %s", cmn.join(g_cpld_upgrade_list, ", "))
        return defs.RET.OK
    end
    fw_upgrade.hot_upgrade = false
    g_cpld_upgrade_list = generate_upgrade_list(cfg_list[index].chip_num)
    log:notice("cpld cold upgrade, upgrade list = %s", cmn.join(g_cpld_upgrade_list, ", "))
    return defs.RET.OK
end

local function get_hot_ver(fw, upg_config)
    local idx = tonumber(string.match(fw.csr.Name,'BCU_CPLD(%d)'))
    if idx == upg_config.hot_valid_list[1] then
        local ok, resp = pcall(fw.get_fw_version, fw, defs.RYTRY_TIME)
        if not ok then
            log:error("get firmware version fail:%s", resp)
            return
        end
        return resp
    end
end

local function get_prepare_upgrade_version(signal, system_id, index)
    local version = ""
    for _, fw in ipairs(signal.fw_list) do
        if fw.system_id ~= system_id then
            goto continue
        end
        -- 同一个固件包中的所有Firmware可以认为是归一版本，找到匹配的一个fw即可
        if fw_upgrade.hot_upgrade then
            local hot_ver = get_hot_ver(fw, signal.upg_cfg_list[index])
            if hot_ver and hot_ver ~= "0.00" then
                version = hot_ver
                break
            end
            goto continue
        end
        if signal.upg_cfg_list[index]:check_fw_uid_exist(fw) then
            local ok, resp = pcall(fw.get_fw_version, fw, defs.RYTRY_TIME)
            if not ok then
                log:error("get firmware version fail:%s", resp)
            else
                version = resp
                break
            end
        end
        ::continue::
    end
    log:notice("[cpld]get verion[%s]", version)
    return version
end

-- 根据多固件支持情况和参数确定固件索引
local function get_firmware_index(signal, parameters)
    if signal.support_multifirmware and parameters.FirmwareIndex then
        return tonumber(string.match(parameters.FirmwareIndex, 'Firmware(%d)')) or 1
    end
    return 1
end

function fw_upgrade.prepare_upgrade(signal, system_id, firmware_type, cfg_path, parameters)
    local version = ""
    local ret = defs.RET.OK
    local index = 1

    -- 检测是否FPAG正在升级
    if defs.UPGRADING_FPGA == 1 then
        ret = defs.RET.OTHERS_UPGRADING
        log:error('[cpld] fpga is already Upgrading')
        fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, version, ret,
            parameters)
        return
    end

    if defs.UPGRADING_CPLD == 1 then
        log:error('[cpld] another cpld is in upgrading')
        fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type,
            version, defs.RET.IN_UPGRADING, parameters)
        return
    end
    --  设置正在升级CPLD
    defs.UPGRADING_CPLD = 1

    -- 天池CPLD升级的链路选择在Process里做，完成框架下发的update.cfg解析即可
    signal.upg_cfg_list = cfg:get_cfg_list(cfg_path)
    if not next(signal.upg_cfg_list) then
        ret = defs.RET.ERR
        goto exit
    end

    log:notice('[cpld]get cpld_valid :%s', fw_upgrade.cpld_valid)
    if fw_upgrade.cpld_valid then
        log:error('[cpld] another cpld is valid, please wait')
        ret = defs.RET.ERR
        goto exit
    end
    -- 单厂家跳过热升级检查
    fw_upgrade.hot_upgrade = false
    g_cpld_upgrade_list = nil
    signal.support_multifirmware = cmn.is_support_multifirmware(cfg_path)
    index = get_firmware_index(signal, parameters)
    if signal.upg_cfg_list[index].supplier_mode > defs.CPLD_SUPPLIER_MODE.SINGLE then
        ret = check_hot_upgrade(signal.upg_cfg_list, index)
    end

    -- 框架新增需求：在prepare阶段返回版本号
    version = get_prepare_upgrade_version(signal, system_id, index)
    ::exit::

    if ret == defs.RET.ERR then
        defs.UPGRADING_CPLD = 0
    end
    parameters.MultipleSupported = 'Support' -- 支持多firmware标志
    fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, version, ret,
        parameters)
end

function fw_upgrade.process_upgrade(signal, system_id, firmware_type, file_path, parameters)
    local ret, is_need_valid
    local index = get_firmware_index(signal, parameters)
    if signal.upg_cfg_list[index].update_link and signal.upg_cfg_list[index].update_link == '1' then
        log:notice('[CPLD] start i2c cpld upgrade')
        local ok
        ok, ret, is_need_valid = pcall(function ()
            return iic_upgrade:iic_upgrade_cpld(signal.db, signal.fw_list, signal.upg_cfg_list,
                    file_path, system_id, firmware_type, parameters)
        end)
        if not ok then
            log:error('[CPLD] i2c upgrade failed, error:%s', ret)
            ret = defs.RET.ERR
        end
    else
        ret, is_need_valid = process_handle:upgrade_component_cpld(signal.db, system_id, signal.fw_list,
            signal.upg_cfg_list, file_path, g_cpld_upgrade_list, fw_upgrade.hot_upgrade)
    end
    -- 升级失败清CPLD升级标记
    if ret ~= defs.RET.OK then
        defs.UPGRADING_CPLD = 0
    end
    -- 如果至少有一个cpld升级成功，说明需要生效，process阶段不能返回失败，否则无法进入finish阶段的生效流程
    -- 如果至少有一个cpld升级失败，需要给用户返回升级失败，在finish阶段整合process阶段的结果
    fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type,
        is_need_valid and defs.RET.OK or ret, parameters)
    return ret
end

local function finish_hot_upgrade(signal, system_id)
    local manager = factory.get_obj("unit_manager")
    valid_handle.cpld_hot_valid(system_id, signal)
    for _, v in pairs(manager.cpu_boards) do
        if v:get_system_id() == system_id then
            v:task_update_logic_version()
        end
    end
    for _, v in pairs(manager.exp_boards) do
        if v:get_system_id() == system_id then
            v:task_update_logic_version()
        end
    end
    if manager.logic_fw and type(manager.logic_fw) == "table" then
        for _, v in pairs(manager.logic_fw) do
            if v.system_id == system_id then
                v:check_update_ver()
            end
        end
    end
    return true
end

function fw_upgrade.finish_upgrade(signal, system_id, firmware_type)
    log:notice('[cpld] finish upgrade system_id[%s] firmware_type[%s]', system_id, firmware_type)
    -- 无感升级需要拉低管脚复位cpld
    if fw_upgrade.hot_upgrade then
        return finish_hot_upgrade(signal, system_id)
    end
    -- 生效期间禁止其它CPLD进行升级
    fw_upgrade.cpld_valid = true
    log:notice('[cpld] set valid true')
    local ret = valid_handle.cpld_valid_task(signal, system_id)
    fw_upgrade.cpld_valid = false
    log:notice('[cpld] set valid false')
    return ret
end

return fw_upgrade