-- 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 class = require 'mc.class'
local log = require 'mc.logging'
local block_info = require 'domain.system_info.block_info'
local chara_info = require 'domain.system_info.character_info'
local defs = require 'domain.system_info.defs'
local prop_def = require 'macros.property_def'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local s_pack = string.pack

local system_info_object = class()

local SET_IN_PROGRESS = 0x81

local TypeToObj = {
    [1] = block_info,
    [2] = chara_info
}

function system_info_object:get_boot_info_tbl(db, system_id)
    local db_info = db:select(db.SystemInfoTable)
        :where(db.SystemInfoTable.SystemId:eq(system_id)):first()
    if db_info then
        return db_info
    end
    local new_db_info = db.SystemInfoTable({SystemId = system_id,
        SystemFirmwareVersion = '',
        SystemName = '',
        PrimaryOperatingSystemName = '',
        OperatingSystemName = '',
        PresentOSVersionNumber = '',
        BMCUrl = '',
        BaseOsUrlForManageability = ''})
    new_db_info:save()
    return new_db_info
end

function system_info_object:ctor(db, system_id)
    self.db = db
    self.system_id = system_id
end

function system_info_object:init()
    local db_obj = self:get_boot_info_tbl(self.db, self.system_id)

    self.system_info_objects = {}
    for prop, _ in pairs(defs.ParameterSelectorType) do
        local cfg = {
            db = db_obj,
            prop = prop,
            system_id = self.system_id,
            type = defs.ParameterSelectorType[prop],
            volidate = defs.ParameterSelectorVolidate[prop]
        }
        if TypeToObj[cfg.type] then
            self.system_info_objects[prop] = TypeToObj[cfg.type].new(cfg)
        else
            log:error('system info obj: ctor obj failed, prop: %s, type: %s', cfg.prop, cfg.type)
        end
    end
    self.db_obj = db_obj
    self.set_in_progress = 0
end

function system_info_object:get_info(para_selector, block_selector)
    if para_selector == 0 then
        if self.set_in_progress then
            return comp_code.Success, prop_def.ParameterRevision, s_pack('I1', self.set_in_progress)
        else
            return comp_code.DataNotAvailable
        end
    end
    local system_info_obj = self.system_info_objects[defs.ParameterSelector[para_selector]]
    if not system_info_obj then
        log:error('get system info failed, obj is nil')
        return comp_code.DataNotAvailable, prop_def.ParameterRevision, ''
    end
    return system_info_obj:get_info(block_selector)
end

function system_info_object:set_info(para_selector, info, ctx)
    if para_selector == 0 then
        if #info ~= 1 then
            return comp_code.ReqDataLenInvalid
        end
        local data = info:byte()
        if data ~= 0 and data ~= 1 then
            return comp_code.InvalidFieldRequest
        end
        if data == 1 and self.set_in_progress == 1 then
            return SET_IN_PROGRESS
        end
        self.set_in_progress = data
        return comp_code.Success
    end
    local system_info_obj = self.system_info_objects[defs.ParameterSelector[para_selector]]
    if not system_info_obj then
        log:error('get system info failed, obj is nil')
        return comp_code.DataNotAvailable
    end
    return system_info_obj:set_info(info, ctx)
end

function system_info_object:clear_system_info()
    for prop, _ in pairs(defs.ParameterSelectorType) do
        local obj = self.system_info_objects[prop]
        if obj and obj.volidate then
            log:notice('system info: obj type: %s, need clear', obj.type)
            obj:reallocate()
        end
    end
end

return system_info_object