-- 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 singleton = require 'mc.singleton'
local vos = require 'utils.vos'
local process_handle = require 'unit_manager.class.logic_fw.upgrade.process'
local defs = require 'unit_manager.class.logic_fw.comm_defs'
local client = require 'general_hardware.client'
local context = require 'mc.context'
local utils = require 'mc.utils'
local cmn = require 'common'
local fpga_package = require 'unit_manager.class.logic_fw.fpga.fpga_package'
local fpga_self_test = require 'unit_manager.class.logic_fw.fpga.fpga_self_test'
local cfg = require 'unit_manager.class.logic_fw.upgrade.fw_cfgs'

local MAX_WAIT_FPGA_UPGRADE<const> = 3

local fpga_process = {}
fpga_process.__index = fpga_process

function fpga_process.new(db)
    return setmetatable({
        db = db
    }, fpga_process)
end

local function upgrade_component_fpga(fpga_signal, file_path)
    -- 开始升级fpga
    local cnt = 0
    while cnt < MAX_WAIT_FPGA_UPGRADE do
        local ret = process_handle:upgrade_component_cpld(fpga_signal.db, 1, fpga_signal.fw_list,
            fpga_signal.upg_cfg_list, file_path, {}, false)
        if ret ~= defs.RET.OK then
            log:notice('fpga upgrade fail, retry upgrade cnt:[%s]', cnt + 1)
            goto continue
        end

        cmn.skynet.sleep(300)
        log:notice('fpga upgrade success, start fpga self test')
        -- fpga自检测试
        if fpga_self_test.get_instance():get_fpga_self_test_result() then
            log:notice('fpga self test success')
            return defs.RET.OK
        end

        ::continue::
        cnt = cnt + 1
        cmn.skynet.sleep(500)
    end

    log:notice('fpga upgrade fail')
    return defs.RET.ERR
end

local function remove_upgrade_fpga_hpm(dir_path)
    if vos.get_file_accessible(dir_path) then
        utils.remove_file(dir_path)
    end
end

function fpga_process:upgrade_fpga_by_hpm(fpga_signal, hpm_path)
    -- 1. 判断缓存文件是否存在
    if not vos.get_file_accessible(hpm_path) then
        log:error('[FPGA] cache hpm upgrade file not exist')
        return defs.RET.ERR
    end

    log:notice('[FPGA] upgrade fpga by hpm package.')
    -- 2.将hpm解压,获取升级文件
    local package_info = cmn.get_package_info(hpm_path)
    if not package_info or package_info.FirmwareType ~= 'FPGA' then
        log:error('[FPGA] hpm is not fpga')
        return defs.RET.ERR
    end

    -- 3.解析update.cfg
    local dir_path = package_info.FirmwareDirectory
    local cfg_path = dir_path .. 'update.cfg'
    local ok, rsp = client:PFileFileChown(context.new(), nil, cfg_path, 104, 104)
    if not ok then
        log:error('[McuUpgrade] chown upgrade file failed, error %s', rsp)
        return defs.RET.ERR
    end
    fpga_signal.upg_cfg_list = cfg:get_cfg_list(cfg_path)
    if not next(fpga_signal.upg_cfg_list) then
        return defs.RET.ERR
    end

    -- 4.开始升级fpga
    local file_path = dir_path .. fpga_signal.upg_cfg_list[1].name
    local ret = upgrade_component_fpga(fpga_signal, file_path)

    -- 5.删除临时文件
    remove_upgrade_fpga_hpm(dir_path)
    return ret
end

function fpga_process:process_upgrade_fpga(fpga_signal, file_path)
    local fpga_pkage = fpga_package.get_instance()

    -- 开始升级fpga
    local ret = upgrade_component_fpga(fpga_signal, file_path)
    if ret == defs.RET.OK then
        -- 升级成功，保存fpga升级包到gold区
        fpga_pkage:cache_fpga_hpm(defs.UPGRADE_FPGA_TMP_PATH, fpga_pkage.UpgradeFpgaGoldDir)
        fpga_pkage:remove_tmp_fpga_hpm()
        return ret
    end

    -- 升级fpga失败，删除tmp区并尝试从gold区升级fpga
    fpga_pkage:remove_tmp_fpga_hpm()
    if self:upgrade_fpga_by_hpm(fpga_signal, defs.UPGRADE_FPGA_GOLD_PATH) ~= defs.RET.OK then
        log:error('[FPGA] upgrade fpga by gold hpm failed.')
    else
        log:notice('[FPGA] upgrade fpga by gold hpm success!')
    end
    return ret
end

function fpga_process:process_active_fpga(fpga_signal)
    local fpga_pkage = fpga_package.get_instance()

    -- 开始从tmp区升级fpga
    local ret = self:upgrade_fpga_by_hpm(fpga_signal, defs.UPGRADE_FPGA_TMP_PATH)
    if ret == defs.RET.OK then
        -- 升级成功，保存fpga升级包到gold区
        log:notice('[FPGA] upgrade fpga by tmp hpm success!')
        fpga_pkage:cache_fpga_hpm(defs.UPGRADE_FPGA_TMP_PATH, fpga_pkage.UpgradeFpgaGoldDir)
        fpga_pkage:remove_tmp_fpga_hpm()
        return ret
    end
    log:notice('[FPGA] upgrade fpga by tmp hpm failed.')

    -- 升级fpga失败，尝试从gold区升级fpga
    fpga_pkage:remove_tmp_fpga_hpm()
    if self:upgrade_fpga_by_hpm(fpga_signal, defs.UPGRADE_FPGA_GOLD_PATH) ~= defs.RET.OK then
        log:error('[FPGA] upgrade fpga by gold hpm failed.')
    else
        log:notice('[FPGA] upgrade fpga by gold hpm success!')
    end
    return ret
end

return singleton(fpga_process)
