-- 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 class = require 'mc.class'
local utils = require 'mc.utils'
local json = require 'cjson'
local log = require 'mc.logging'
local bios_enum = require 'domain.bios_firmware.defs'
local component_info = require 'domain.mapping.component_info'
local bios_factory = require 'factory.bios_factory'
local base_messages = require 'messages.base'
local bs_util = require 'util.base_util'

local ComponentVersion = class(component_info)
local BIOS_COMPONENT_JSON_FILE_DIR<const> = '/data/opt/bmc/conf/bios/'

-- 管理版本
-- 1、固件版本信息管理
-- 2、固件版本存储的文件管理
function ComponentVersion:ctor(system_id)
    local real_system_id = system_id or 1
    self.file_path = bs_util.get_conf_path(real_system_id, 'ComponentVersion.json')
    self.type = bios_enum.ReadImuFileType.ComponentVersion
    utils.mkdir_with_parents(BIOS_COMPONENT_JSON_FILE_DIR, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    ComponentVersion.super.init_info(self, real_system_id)
    self.system_id = real_system_id
end

function ComponentVersion:init()
end

local function get_basic_version(version)
    local basic_version = version
    local v1, v2 = string.match(version, '(%d+).(%d+)')
    if v1 and v2 then
        basic_version = v1 .. '.' .. v2
    end
    return basic_version
end

-- 更新资源树上的版本号
function ComponentVersion:update_cfg_version()
    local ok, err = pcall(function()
        local cfg = bios_factory.get_service('bios_service')
        -- 重启bmc可能自发现对象还未分发，导致这里是空
        local version = cfg:get_prop('Version', self.system_id)
        local basic_version = get_basic_version(version)
        local version_json = json.decode(self.data)
        if version_json.PatchVersion and #version_json.PatchVersion ~= 0 then
            version = string.format('%s.%s', basic_version, version_json.PatchVersion)
        end
        cfg:update_patch_version(version, self.system_id)
    end)
    if not ok then
        log:error('[bios]update_cfg_version fail, %s', err)
    end
end

function ComponentVersion:get_patch_version()
    local ok, version_json = pcall(function()
        return json.decode(self.data)
    end)
    if not ok then
        return
    end

    return version_json.PatchVersion
end

function ComponentVersion:is_hotfix(attributes)
    for _, v in pairs(attributes) do
        if v == 'Hotfix' then
            return true
        end
    end
    return false
end

function ComponentVersion:get_component_bitmaps()
    local component_bitmaps = {}
    local ok, version_json = pcall(function()
        return json.decode(self.data)
    end)
    if not ok then
        return component_bitmaps
    end
    local componet_info = version_json.ComponentInfo
    if not componet_info then
        return component_bitmaps
    end
    for _, v in pairs(componet_info) do
        if self:is_hotfix(v.Attribute) then
            component_bitmaps[v.Name] = v
        end
    end
    return component_bitmaps
end

-- 1、activate_mode为NULL：冷升级
-- 2、activate_mode为'None'：热升级，只升级不生效
-- 3、activate_mode为'imu'、'm7'：热设计，设计并且生效imu、m7
function ComponentVersion:get_bitmap_by_name(component_name)
    if not component_name then
        error('[bios]ComponentInfo: component_name nil')
    end
    local components_bitmap = self:get_component_bitmaps()
    if not components_bitmap[component_name] or
        not components_bitmap[component_name].BitMap then
        error(string.format('[bios]ComponentInfo: get componet(%s) bitmap fail'),
            component_name)
    end
    return components_bitmap[component_name].BitMap
end

function ComponentVersion:get_componet_bitmap(component_names)
    local bitmap = 0
    for _, component_name in pairs(component_names) do
        local componet_bitmap = self:get_bitmap_by_name(component_name)
        bitmap = bitmap | (1 << componet_bitmap)
    end
    return bitmap
end

function ComponentVersion:get_componet_bitmap_all(components_bitmap)
    local bitmap_list= {}
    for _, v in pairs(components_bitmap) do
        table.insert(bitmap_list, v.BitMap)
    end
    return bitmap_list
end

-- 查询固件的BitMap，返回值为List
function ComponentVersion:get_componet_bitmap_list(component_names)
    local components_bitmap = self:get_component_bitmaps()
    if not component_names then
        return components_bitmap
    end
    local _, name = next(component_names)
    -- 返回全部组件ID
    if name == 'All' then
        return components_bitmap
    end
    local bitmap_list= {}
    for _, component_name in pairs(component_names) do
        if not components_bitmap[component_name] or
            not components_bitmap[component_name].BitMap then
            error({Code = base_messages.PropertyValueNotInList(component_name, 'BiosActivatedScope')})
        end
        table.insert(bitmap_list, components_bitmap[component_name]) 
    end
    return bitmap_list
end

return ComponentVersion