-- 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 fw_mgmt_client = require 'general_hardware.client'
local cfg = require 'unit_manager.class.logic_fw.upgrade.fw_cfgs'
local defs = require 'unit_manager.class.logic_fw.comm_defs'
local context = require 'mc.context'
local fpga_package = require 'unit_manager.class.logic_fw.fpga.fpga_package'
local fpga_process = require 'unit_manager.class.logic_fw.fpga.fpga_process'
local fructl = require 'mcu.upgrade.fructl_handler'
local cmn = require 'common'
local vos = require 'utils.vos'

local MAX_WAIT_COUNTS<const> = 60
local FRUCTL_FPGA_LOCK_TIME<const> = 300
local FRUCTL_FPGA_UNLOCK_TIME<const> = 300

local fpga_upgrade = {}

-- 加上电锁
function fpga_upgrade.lock_until_success(bus, chassis_id, reason)
    local cnt = 0
    while cnt < MAX_WAIT_COUNTS do
        local res = fructl.set_chassis_power_on_lock(bus, chassis_id, true,
            FRUCTL_FPGA_LOCK_TIME, reason, 'general_hardware')
        if res then
            log:notice('fpga lock success')
            return
        end
        cnt = cnt + 1
        cmn.skynet.sleep(50)
        log:info('fpga lock fail, res = %s', res)
    end
    log:notice('fpga lock fail')
end

-- 释放上电锁
function fpga_upgrade.unlock_until_success(bus, chassis_id, reason)
    local cnt = 0
    while cnt < MAX_WAIT_COUNTS do
        local res = fructl.set_chassis_power_on_lock(bus, chassis_id, false,
            FRUCTL_FPGA_UNLOCK_TIME, reason, 'general_hardware')
        if res then
            log:notice('fpga unlock success')
            return
        end
        cnt = cnt + 1
        cmn.skynet.sleep(50)
        log:info('fpga unlock fail, res = %s', res)
    end
    log:notice('fpga unlock fail')
end

local function get_match_fw_version(fpga_signal)
    local ret = defs.RET.ERR
    local version = ''
    -- 同一个固件包中的所有Firmware可以认为是归一版本，找到匹配的一个fw即可
    for _, fw in ipairs(fpga_signal.fw_list) do
        if not string.find(fw.name, "FPGA") then
            goto continue
        end
        if not fpga_signal.upg_cfg_list[1]:check_fw_uid_exist(fw) then
            goto continue
        end

        local ok, resp = pcall(fw.get_fw_version, fw, defs.RYTRY_TIME)
        if ok then
            ret = defs.RET.OK
            version = resp
            break
        end
        log:error("get firmware version fail:%s", resp)
        ::continue::
    end
    return ret, version
end

function fpga_upgrade.prepare_upgrade(fpga_signal, system_id, firmware_type, cfg_path, hpm_path, parameters)
    local ret = defs.RET.ERR
    local version = ''
    local fpga_pkage = fpga_package.get_instance()

    -- 解析update.cfg
    fpga_signal.upg_cfg_list = cfg:get_cfg_list(cfg_path)
    if next(fpga_signal.upg_cfg_list) then
        -- 获取匹配的固件版本号
        ret, version = get_match_fw_version(fpga_signal)
        if ret == defs.RET.OK then
            -- 缓存fpga升级包到tmp区
            fpga_pkage:cache_fpga_hpm(hpm_path, fpga_pkage.UpgradeFpgaTmpDir)
            if not vos.get_file_accessible(defs.UPGRADE_FPGA_GOLD_PATH) then
                fpga_pkage:cache_fpga_hpm(hpm_path, fpga_pkage.UpgradeFpgaGoldDir)
            end
        end
    end
    log:notice("[fpga]___________ver[%s] ret[%s]", version, ret)
    fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, version, ret,
        parameters)
end

function fpga_upgrade.process_upgrade(fpga_signal, system_id, firmware_type, file_path, parameters)
    defs.UPGRADING_FPGA = 1
    if fpga_signal.fpga_upgrade_type == defs.UPGRADE_FPGA_BY_JTAG then
        -- 上电锁
        fpga_upgrade.lock_until_success(fpga_signal.bus, 1, 'fpga')
        -- 开始升级, 成功则保存升级配置信息
        local ret = fpga_process.get_instance():process_upgrade_fpga(fpga_signal, file_path)
        -- 解上电锁
        fpga_upgrade.unlock_until_success(fpga_signal.bus, 1, 'fpga')
        -- 升级响应
        fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret, parameters)
    elseif fpga_signal.fpga_upgrade_type == defs.UPGRADE_FPGA_BY_SFC then
        local ret = fpga_process.get_instance():process_upgrade_fpga(fpga_signal, file_path)
        -- 升级响应
        fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret, parameters)
    end
    defs.UPGRADING_FPGA = 0
end

function fpga_upgrade.finish_upgrade(system_id, firmware_type, parameters)
    fw_mgmt_client:UpdateServiceUpdateServiceFinishReply(context.new(), system_id, firmware_type, 0, parameters)
end

function fpga_upgrade.active_process(fpga_signal)
    log:notice("enter fpga active process")
    defs.UPGRADING_FPGA = 1
    -- 生效fpga, 成功则保存升级配置信息
    local ret = fpga_process.get_instance():process_active_fpga(fpga_signal)
    defs.UPGRADING_FPGA = 0
    return ret
end

return fpga_upgrade