-- 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 abstract_cfg = require "pojo.boot.abstract_boot_option"
local log = require "mc.logging"
local ipmi = require 'ipmi'
local boot_def = require "macros.boot_def"
local bs_util = require 'util.base_util'
local _, client = pcall(require, 'bios.client')

local poweron_delay = class(abstract_cfg)

local function get_delayed_poweron_policy_obejct(sys_id)
    local obj = nil
    local objects = client:GetDelayedPowerOnPolicyObjects()
    for _, v in pairs(objects) do
        if bs_util.get_obj_id(v.path) == sys_id then
            obj = v
            break
        end
    end
    if not obj then
        log:error('[bios] Bios cannot get DelayedPowerOnPolicy object!')
    end

    return obj
end

local mode_map = {
    [0] = 'DefaultDelay',
    [1] = 'HalfDelay',
    [2] = 'FixedDelay',
    [3] = 'RandomDelay'
}

function poweron_delay:set_info(config_data, ctx)
    local sys_id = bs_util.get_system_id(ctx)
    local obj = get_delayed_poweron_policy_obejct(sys_id)
    if not obj then
        return boot_def.COMP_CODE_UNKNOWN
    end

    if #config_data ~= 8 then
        log:error("[bios] Set boot_option[poweron_delay]: data len invalid.")
        return boot_def.COMP_CODE_LEN_INVALID
    end

    local data = {}
    for i = 1, 8 do
        data[i] = string.sub(config_data, i, i):byte()
    end

    local manu_id = (data[3] << 16) + (data[2] << 8) + data[1]
    if manu_id ~= 0x0007db then
        ipmi.ipmi_operation_log(ctx, 'BIOS', "Set power up delay failed")
        log:error("[bios] Invalid manu_id=%u", manu_id)
        return boot_def.COMP_CODE_INVALID_FIELD
    end

    if data[4] >= 4 then
        ipmi.ipmi_operation_log(ctx, 'BIOS', "Set power up delay failed")
        log:error("[bios] Invalid mode, pwr_on_delay_mode=%u", data[4])
        return boot_def.COMP_CODE_INVALID_FIELD
    end

    obj.Mode = mode_map[data[4]]
    -- 为了兼容老的SMM板,ipmi上来的消息的单位还是按100ms处理，所以这里要除以10
    obj.Seconds = ((data[8] << 24) + (data[7] << 16) + (data[6] << 8) + data[5]) / 10

    return boot_def.COMP_CODE_SUCCESS
end

function poweron_delay:get_info(config_data, sys_id)
    local obj = get_delayed_poweron_policy_obejct(sys_id)
    if obj == nil then
        return nil
    end

    if #config_data ~= 2 then
        log:error("[bios] Get boot_option[poweron_delay]: data len invalid.")
        return nil
    end

    local set_selector = string.sub(config_data, 1, 1):byte()
    local block_selector = string.sub(config_data, 2, 2):byte()
    if set_selector ~= 0x00 or block_selector ~= 0x00 then
        return nil, 0xcc
    end

   local delay_mode = 0xff
    for k, v in pairs(mode_map) do
        if obj.Mode == v then
            delay_mode = k
        end
    end

    -- 为了兼容老的SMM板,ipmi上来的消息的单位还是按100ms处理，所以这里要乘以10
    local delay_count = obj.Seconds * 10
    local rsp = {}
    rsp[0] = 0xdb
    rsp[1] = 0x07
    rsp[2] = 0x00
    rsp[3] = delay_mode
    rsp[4] = delay_count & 0xff
    rsp[5] = (delay_count >> 8) & 0xff
    rsp[6] = (delay_count >> 16) & 0xff
    rsp[7] = (delay_count >> 24) & 0xff
    return rsp
end

return poweron_delay