-- 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 db_util = require "util.db_util"
local boot_def = require "macros.boot_def"
local log = require "mc.logging"
local ipmi = require 'ipmi'
local skynet = require 'skynet'

local abstract_boot_option = class()

function abstract_boot_option:init_db_info(option_db_obj, prop, log_prop, system_id)
    local data = {}
    local db_has_value = false
    if option_db_obj then
        local db_val = option_db_obj[prop]
        if not db_val or #db_val == 0 then
            -- 如果数据库没有数据,则初始化数据
            db_val = db_util.construc_init_data(boot_def.BOOT_LEN_TBL[prop])
        else
            db_has_value = true
        end
        data = db_util.decode_db_info(db_val)
    end
    self.data = data
    self.db_obj = option_db_obj
    self.prop = prop
    self.log_prop = log_prop
    self.system_id = system_id
    return db_has_value
end

function abstract_boot_option:get()
end

function abstract_boot_option:get_data_string()
    local origin_data = self.origin_data
    if not origin_data then
        return ""
    end

    local text = {}
    local len = boot_def.BOOT_LEN_TBL[self.prop]
    if self.prop == boot_def.BOOT_INITIATOR_MAILBOX then
        len = boot_def.BIOS_BOOT_VALID_SEG_TOTAL_NUM
    end
    if len > #origin_data then
        len = #origin_data
    end
    for i = 1, len - 1 do
        local bin_byte = string.sub(origin_data, i, i):byte()
        local option_data = string.format('0x%02x-', bin_byte)
        text[i] = option_data
    end

    local last_bin_byte = string.sub(origin_data, len, len):byte()
    local last_option_data = string.format('0x%02x', last_bin_byte)
    text[len] = last_option_data
    return table.concat(text)
end

function abstract_boot_option:save_operator_log(ctx)
    local option_bin = self:get_data_string()
    ipmi.ipmi_operation_log(ctx, 'BIOS', "Set boot option %s to (RAW:%s) successfully",
        self.log_prop, option_bin)
end

function abstract_boot_option:log_dump()
    local prop = self.log_prop
    if not prop then
        return nil
    end
    local log_data = ""
    for k, v in pairs(self.data) do
        log_data = log_data .. prop .. "[" .. k .. "]" .. ":" .. v .. "\r\n"
        skynet.sleep(10)
    end
    return log_data
end

function abstract_boot_option:set_info(config_data, ctx)
    self.origin_data = config_data
    local data = self.data
    local prop = self.prop
    if not config_data or not data or not prop then
        log:error("boot_option: set_info param invalid.")
        return boot_def.COMP_CODE_INVALID_FIELD
    end

    if #config_data < boot_def.BOOT_LEN_TBL[prop] then
        log:error("boot_option[%s]: data len invalid.", prop)
        return boot_def.COMP_CODE_LEN_INVALID
    end

    local comp_code = self:operate_info(config_data, ctx)
    if comp_code ~= boot_def.COMP_CODE_SUCCESS then
        log:error("boot_option[%s]: operate_info fail.", prop)
        return comp_code
    end

    local res = self:update_db_info()
    if not res then
        log:error("boot_option[%s]: update_db_info fail.", prop)
        return boot_def.COMP_CODE_INVALID_FIELD
    end

    self:save_operator_log(ctx)
    return boot_def.COMP_CODE_SUCCESS
end

function abstract_boot_option:operate_info(config_data, ctx)
end

function abstract_boot_option:config(cfg)
end

function abstract_boot_option:dump()
end

function abstract_boot_option:show()
end

-- 更新数据库
function abstract_boot_option:update_db_info()
    local db_obj = self.db_obj
    local data = self.data
    local prop = self.prop
    if not db_obj or not data or not prop or not db_obj[prop] then
        log:error("boot_option: update_db_info invalid param")
        return false
    end

    db_obj[prop] = db_util.encode_db_info(data)
    db_obj:save()
    return true
end

-- 获取启动参数
function abstract_boot_option:get_info(config_data)
    local data = self.data
    if not data then
        return nil
    end

    return self.data
end

return abstract_boot_option