-- 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 cfg = require "pojo.config.basic.configure"
local class = require 'mc.class'
local prop_def = require 'macros.property_def'
local log = require 'mc.logging'
local table_cache = require 'mc.table_cache'
local tbl_cache = table_cache.new()

local registry = class()

local function json_to_attr_list(self, attrs)
    if attrs == nil then
        return nil
    end

    local attr_list = tbl_cache:allocate()
    local traverse_list = tbl_cache:allocate()
    local len = #attrs
    for i = 1, len, 1 do
        local attr_obj = cfg.Attribute.new(attrs[i])
        local att_name = attrs[i][prop_def.REGRIST_PROP_ATTRIBUTENAME]
        attr_list[att_name] = attr_obj
        traverse_list[i] = att_name
    end

    self.AttributeList = attr_list
    self.TraverseList = traverse_list
    return
end

local function json_to_menu_list(menus)
    if menus == nil then
        return nil
    end

    local menu_list = tbl_cache:allocate()
    local len = #menus
    for i = 1, len, 1 do
        local menu_obj = cfg.Menu.new(menus[i])
        local menu_name = menus[i][prop_def.REGRIST_PROP_MEUNNAME]
        menu_list[menu_name] = menu_obj
    end

    return menu_list
end

local function add_val_to_list(list, val)
    if not list then
        list = {}
    end
    local list_len = #list
    list[list_len + 1] = val
    return list
end

local function json_to_dependency_list(dependencies)
    if dependencies == nil then
        return
    end

    local dependency_list = tbl_cache:allocate()
    local dependency_to_list = tbl_cache:allocate()
    local len = #dependencies
    for i = 1, len, 1 do
        local dependency_obj = cfg.Dependency.new(dependencies[i])
        local dependency_to_name = dependencies[i]
            [prop_def.REGRIST_PROP_DEPENDENCY][prop_def.REGRIST_PROP_MAPTOATTRIBUTE]
        local dependency_from_list = dependencies[i]
            [prop_def.REGRIST_PROP_DEPENDENCY][prop_def.REGRIST_PROP_MAPFORM]
        -- 所有MapFrom属性都要触发依赖关系判断
        for _, v in pairs(dependency_from_list) do
            local map_from_attr = v[prop_def.REGRIST_PROP_MAPFORMATTRIBUTE]
            local depency_for_list = add_val_to_list(dependency_list[map_from_attr], dependency_obj)
            dependency_list[map_from_attr] = depency_for_list
        end
        local depency_to_list = add_val_to_list(dependency_to_list[dependency_to_name], dependency_obj)
        dependency_to_list[dependency_to_name] = depency_to_list
    end

    return dependency_list, dependency_to_list
end

function registry:ctor(json_data, base)
    self.base = base
    if not base then
        local menus = json_data[prop_def.REGRIST_PROP_REGISTRYENTRIES][prop_def.REGRIST_PROP_MENUS]
        self.MenuList = json_to_menu_list(menus)
        self.RegistryVersion = json_data[prop_def.REGRIST_PROP_REGISTRYVERSIOHN]
        self.Language = json_data[prop_def.REGRIST_PROP_LANGUAGE]
    end
    local attrs = json_data[prop_def.REGRIST_PROP_REGISTRYENTRIES][prop_def.REGRIST_PROP_ATTRIBUTES]
    local dependencies = json_data[prop_def.REGRIST_PROP_REGISTRYENTRIES][prop_def.REGRIST_PROP_DEPENDENCIES]
    json_to_attr_list(self, attrs)
    local dependency_list,
        dependency_to_list = json_to_dependency_list(dependencies)
    self.DependencyList = dependency_list
    self.DependencyToList = dependency_to_list
    self.DepthValue = 0
end

function registry:destroy()
    for _, dependencys in pairs(self.DependencyList) do
        for _, dependency in pairs(dependencys) do
            dependency:destroy()
        end
    end
    for _, attribute in pairs(self.AttributeList) do
        attribute:destroy()
    end
    tbl_cache:deallocate(self.DependencyList)
    tbl_cache:deallocate(self.DependencyToList)
    tbl_cache:deallocate(self.AttributeList)
    tbl_cache:deallocate(self.TraverseList)
    self.DependencyList = nil
    self.DependencyToList = nil
    self.AttributeList = nil
    self.TraverseList = nil

    if not self.base then
        for _, menu in pairs(self.MenuList) do
            menu:destroy()
        end
        tbl_cache:deallocate(self.MenuList)
        self.MenuList = nil
        self.RegistryVersion = nil
        self.Language = nil
    end
end

function registry:get_registry_version()
    return self.RegistryVersion
end

function registry:get_attribute_list()
    return self.AttributeList
end

function registry:get_traverse_list()
    return self.TraverseList
end

function registry:set_attribute_list(attrs_json)
    self:json_to_attr_list(self, attrs_json)
end

function registry:get_menu_list()
    return self.MenuList
end

function registry:set_menu_list(menus_json)
    self.MenuList = json_to_menu_list(menus_json)
end

function registry:get_dependeny_list()
    return self.DependencyList
end

function registry:get_dependeny_to_list()
    return self.DependencyToList
end

function registry:get_dependeny_to_list_by_name(attr_name)
    return self.DependencyToList[attr_name]
end

function registry:set_dependencie_list(dependencies_json)
    local dependency_list,
        dependency_to_list = json_to_dependency_list(dependencies_json)
    self.DependencyList = dependency_list
    self.DependencyToList = dependency_to_list
end

-- 获取某个Attribute(attr_name)
function registry:get_attibute_val(attribute_name)
    if attribute_name == nil then
        log:error('get_attibute_val: parameter error')
        return nil
    end

    local attr_list = self.AttributeList
    if not attr_list then
        log:error('get_attibute_val: attr_list is null.')
        return nil
    end

    local attribute = attr_list[attribute_name]
    return attribute
end

-- 获取某个Attribute(attr_name)的attr_prop属性值
function registry:get_attibute_prop_val(attribute_name, attr_prop)
    if attribute_name == nil or attr_prop == nil then
        log:error('get_attibute_prop_val: parameter error')
        return nil
    end

    local attribute = self:get_attibute_val(attribute_name)
    if attribute == nil then
        log:info('get_attibute_prop_val: %s has not attribute.', attribute_name)
        return nil
    end

    return attribute:get_val_by_name(attr_prop)
end

-- 设置Attribute(attr_name)的attr_prop属性值为attr_val
function registry:set_attibute_prop_val(attribute_name, attr_prop, attr_val)
    if attribute_name == nil or attr_prop == nil then
        log:error('set_attibute_prop_val: parameter error')
        return nil
    end

    local attribute = self:get_attibute_val(attribute_name)
    if attribute == nil then
        return nil
    end

    return attribute:set_val_by_name(attr_prop, attr_val)
end

function registry:is_match(registry_ser, dependency, attribute_name)
    local ok, res = pcall(function()
        local err_code, set_flag = registry_ser:judge_depency_valid(
            dependency:get_map_from_list(), attribute_name, prop_def.REGRIST_PROP_CURRENTVALUE
        )
        if err_code ~= prop_def.E_OK then
            return false
        end
        return set_flag
    end)
    if not ok then
        return false
    end
    return res
end

function registry:get_map_to_string(type, attribute_name, registry_ser)
    local dependeny_to_list = self:get_dependeny_to_list_by_name(attribute_name)
    if dependeny_to_list == nil then
        return nil
    end

    for i = 1, #dependeny_to_list do
        local dependency = dependeny_to_list[i]
        local real_type = dependency:get_map_to_attr(prop_def.REGRIST_PROP_MAPTOPROPERTY)
        local val = dependency:get_map_to_attr(prop_def.REGRIST_PROP_MAPTOVALUE)
        if real_type == type and val then
            -- 可能存在多个依赖关系，需要匹配到准确的依赖关系
            if self:is_match(registry_ser, dependency, attribute_name) then
                return dependency:map_to_string()
            end
        end
    end
    return nil
end

return registry