-- 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 prop_def = require "macros.property_def"
local setting = require 'pojo.config.file.setting'
local bs_util = require 'util.base_util'
local boot_def = require "macros.boot_def"
local log = require 'mc.logging'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local setting_service = class()

function setting_service:ctor(setting_json)
    local key_tbl = {"BootTypeOrder0", "BootTypeOrder1", "BootTypeOrder2", "BootTypeOrder3"}
    self.key_tbl = key_tbl
    if not setting_json then
        return
    end

    self.setting_obj = setting.new(setting_json)
end

function setting_service:get_prop_val(prop)
    local val = self.setting_obj:get_val_by_name(prop)
    return val
end

function setting_service:build_with_path(file_path)
    local file_json = bs_util.get_file_json(file_path)
    if not file_json then
        self.setting_obj = nil
        return
    end

    self.setting_obj = setting.new(file_json)
end

function setting_service:get_setting_obj()
    return self.setting_obj
end

function setting_service:check_order_valid(prop, order_map, index)
    local boot_order = self:get_prop_val(prop)
    if (self.is_set_order and not boot_order) or (not self.is_set_order and boot_order) or
        (boot_order and order_map[boot_order]) then
        return false
    end

    if boot_order then
        order_map[boot_order] = index
    end

    return true
end

-- 判断启动顺序的设置:要么0个、要么4个，并且不能重复
function setting_service:check_boot_order()
    local order_map = {}
    local is_set_order = false
    local boot_order0 = self:get_prop_val(prop_def.REGRIST_PROP_BOOTTYPEORDER0)
    if boot_order0 then
        is_set_order = true
        order_map[boot_order0] = 0
    end

    self.is_set_order = is_set_order
    -- 非法情况:
    -- 1、如果前面设置了,当前没设置(违反要么4个)
    -- 2、如果前面没设置,当前设置了(违反要么0个)
    -- 3、如果当前设置了,但是和前面重复了
    local valid = self:check_order_valid(prop_def.REGRIST_PROP_BOOTTYPEORDER1, order_map, 1)
    if not valid then
        error(custom_messages.PropertyItemDuplicate('Oem/Huawei/BootupSequence/' ..
            order_map[self:get_prop_val(prop_def.REGRIST_PROP_BOOTTYPEORDER1)]))
    end

    valid = self:check_order_valid(prop_def.REGRIST_PROP_BOOTTYPEORDER2, order_map, 2)
    if not valid then
        error(custom_messages.PropertyItemDuplicate('Oem/Huawei/BootupSequence/' ..
            order_map[self:get_prop_val(prop_def.REGRIST_PROP_BOOTTYPEORDER2)]))
    end

    valid = self:check_order_valid(prop_def.REGRIST_PROP_BOOTTYPEORDER3, order_map, 3)
    if not valid then
        error(custom_messages.PropertyItemDuplicate('Oem/Huawei/BootupSequence/' ..
            order_map[self:get_prop_val(prop_def.REGRIST_PROP_BOOTTYPEORDER3)]))
    end

    return true
end

local function boot_order_is_valid(cfg)
    local order_table = {}

    order_table[boot_def.BOOT_ORDER_DVD] = 1
    order_table[boot_def.BOOT_ORDER_HDD] = 1
    order_table[boot_def.BOOT_ORDER_PXE] = 1
    order_table[boot_def.BOOT_ORDER_OTHERS] = 1

    for _, v in pairs(cfg) do
        if not order_table[v] then
            error(custom_messages.PropertyItemNotInList(v, 'Oem/Huawei/BootupSequence'))
        end
    end

    return true
end

-- 校验启动顺序是否合法
function setting_service:check_boot_order_format()
    local obj = self.setting_obj
    if not obj then
        log:error("[bios]check_boot_order_format: setting obj is nil")
        error(base_messages.InternalError())
    end

    local cfg = obj:get_cfgs()
    boot_order_is_valid(cfg)
    local len = bs_util.cal_table_len(cfg)
    if len ~= 4 then
        log:error("[bios]check_boot_order_format: order len(%s) invalid", len)
        error(custom_messages.WrongArrayLength("4"))
    end

    local boot_order0 = self:get_prop_val(prop_def.REGRIST_PROP_BOOTTYPEORDER0)
    if not boot_order0 then
        log:error("[bios]check_boot_order_format: has no boot order 0")
        error(base_messages.MalformedJSON())
    end

    return self:check_boot_order()
end

function setting_service:compare_setting(key, value)
    local obj = self.setting_obj
    if not obj then
        return prop_def.E_FAILURE
    end

    local cmp_value = obj:get_val_by_name(key)
    if cmp_value ~= value then
        return prop_def.E_FAILURE
    end

    return prop_def.E_OK
end

local function regist_boot_order(key_tbl, boot_order_json)
    if not key_tbl then
        return nil
    end
    local ret_tbl = {}
    for _, value in pairs(key_tbl) do
        table.insert(ret_tbl, boot_order_json[value])
    end

    return ret_tbl
end

function setting_service:get_boot_order()
    local obj = self.setting_obj
    if not obj then
        return nil
    end

    local cfg = obj:get_cfgs()
    if not cfg then
        return nil
    end

    return regist_boot_order(self.key_tbl, cfg)
end

return setting_service