-- 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 cmn = require 'common'
local init_parser = require 'mc.v2_persistence'
local log = require 'mc.logging'
local mc_utils = require 'mc.utils'

local DBUS_CALL_CODE_RET_OK <const> = 0
local DBUS_CALL_CODE_RET_ERR <const> = -1

local STD_SMBUS_UPGRADE_STATUS_IDLE <const> = 0
local STD_SMBUS_UPGRADE_STATUS_INPROCESS <const> = 1
local STD_SMBUS_UPGRADE_STATUS_INSYNC <const> = 2
local STD_SMBUS_UPGRADE_STATUS_FAIL <const> = 3


local utils = setmetatable(require 'mc.utils', {})

utils.DBUS_CALL_CODE = {
    RET_OK = DBUS_CALL_CODE_RET_OK,
    RET_ERR = DBUS_CALL_CODE_RET_ERR
}

utils.STD_SMBUS_UPGRADE_STATUS = {
    IDLE = STD_SMBUS_UPGRADE_STATUS_IDLE,
    INPROCESS = STD_SMBUS_UPGRADE_STATUS_INPROCESS,
    INSYNC = STD_SMBUS_UPGRADE_STATUS_INSYNC,
    FAIL = STD_SMBUS_UPGRADE_STATUS_FAIL
}

utils.FIRMWARE_TYPE_COMPONENT_ID = {
    ['Mcu'] = 0x1d,
    ['DPUCpld'] = 0x1d,
    ['DPUVrd'] = 0x1d
}

utils.FWINVENTORY_PATH  = '/bmc/kepler/UpdateService/FirmwareInventory/'
utils.FIRMWARE_INFO_INTERFACE = 'bmc.kepler.UpdateService.FirmwareInfo'

local function parse_cfg_uid(uid_str)
    local uids = {}
    for uid in string.gmatch(uid_str, '%w+') do
        table.insert(uids, uid)
    end
    return uids
end

local function cfg_new(data, name)
    local cfg = {
        name = name,
        component_id = tonumber(data.ComponentID),
        component_idex = tonumber(data.ComponentIDEx),
        uids = data.UID and parse_cfg_uid(data.UID) or {}
    }
    return cfg
end

-- @brief: parse the firmware config file with predefined format
-- @path string: firmware config file path
-- @return table: a table representing all firmware package configurations
function utils.get_cfgs(path)
    local cfgs = {}
    if not path then
        return cfgs
    end

    local payload = init_parser.load_file(path)
    if not payload.Basic or not payload.Basic.Version then
        return cfgs
    end

    local update_file_num = tonumber(payload.Firmware.FileNum)
    if not update_file_num then
        return cfgs
    end
    for i = 1, update_file_num - 1 do
        local name = 'Firmware' .. i
        if payload[name] ~= nil then
            log:notice('[DPU] Get Firmware, name=%s', name)
            cfgs[name] = cfg_new(payload[name], name)
        end
    end
    return cfgs
end

function utils.uncompress_firmware(path)
    local dir = string.match(path, '/.+/')
    if dir ~= nil then
        mc_utils.secure_tar_unzip(
            path,
            dir,
            0x6400000, -- 最大解压限制100M
            1024
        )
    end
    return dir
end

-- @brief: retry the callback function
-- @param wait int: waiting interval between retries
-- @param retries int: retry count
-- @param cb function: call function passed in
-- @return boolean: a boolean representing function call success
-- @return any: response of the function call
function utils.retry_func(wait, retries, cb, ...)
    local ok = false
    local rsp = nil
    for try = 1, retries do
        ok, rsp = cb(...)
        if ok then
            return ok, rsp
        end
        cmn.skynet.sleep(wait)
    end
    return ok, rsp
end

function utils.get_firmware_uid(cfg_path, firmware_name, uid_name)
    local payload = init_parser.load_file(cfg_path)
    return payload[firmware_name][uid_name]
end

-- 检查CPLD的升级文件名，如果使四段式则返回第几片CPLD，非四段式返回1
function utils.check_CPLD_upgrade_name(file_name)
    if not file_name then
        return 1
    end
    local last_number = string.match(file_name, "^cpld_[^_]+_[^_]+_([0-9]+)%.vme$")
    if not last_number then
        return 1
    end
    return tonumber(last_number) + 1
end

return utils
