-- 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 prop_def = require "macros.property_def"
local bs_util = require 'util.base_util'
local oper_strategy = require 'util.operator.operation_strategy'
local registry = require 'pojo.config.file.registry'
local table_cache = require 'mc.table_cache'
local tbl_cache = table_cache.new()

local registry_service = class()

function registry_service:ctor(registry_json, base)
    self.registry_obj = registry.new(registry_json, base)
    self.DepthValue = 0
    self.hidden_list = tbl_cache:allocate()
    self.immutable_list = tbl_cache:allocate()
    self.readonly_list = tbl_cache:allocate()
end

function registry_service:get_list()
    return self.hidden_list, self.immutable_list, self.readonly_list
end

function registry_service:destroy()
    if self.registry_obj then
        self.registry_obj:destroy()
    end
    tbl_cache:deallocate(self.hidden_list)
    tbl_cache:deallocate(self.immutable_list)
    tbl_cache:deallocate(self.readonly_list)
    self.hidden_list = nil
    self.immutable_list = nil
    self.readonly_list = nil
    self.registry_obj = nil
end

-- 对外接口:根据属性名获取属性对象
function registry_service:get_attribute_by_name(attr_name)
    local attribute = self.registry_obj:get_attibute_val(attr_name)
    return attribute
end

-- 对外接口:根据获取registry_version
function registry_service:get_registry_version()
    local registry_version = self.registry_obj:get_registry_version()
    return registry_version
end

-- 对外接口
function registry_service:get_traverse_list()
    return self.registry_obj:get_traverse_list()
end

-- 判断Depency的依赖条件是否成立
function registry_service:judge_depency_valid(map_from_list, attr_name, attr_prop)
    local len = #map_from_list
    local set_flag = false
    local change_effected = false

    for i = 1, len, 1 do
        local map_from = map_from_list[i]
        if map_from == nil then
            log:error("update_attribute_depency_attr: map_from is null.")
            return prop_def.E_FAILURE, nil, nil
        end

        local map_from_info = map_from:get_map_from_info()
        if map_from_info == nil then
            log:error("update_attribute_depency_attr: map_from_info is null.")
            return prop_def.E_FAILURE, nil, nil
        end

        local map_from_attr_val = map_from_info[prop_def.REGRIST_PROP_MAPFORMATTRIBUTE]
        local map_from_prop_val = map_from_info[prop_def.REGRIST_PROP_MAPFROMPROPERTY]
        local map_from_terms_val = map_from_info[prop_def.REGRIST_PROP_MAPTERMS]
        local map_from_condition_val = map_from_info[prop_def.REGRIST_PROP_MAPFROMCONDITION]
        local map_from_value_val = map_from_info[prop_def.REGRIST_PROP_MAPFROMVALUE]

        -- Attribute更新的属性符合Depency依赖关系的判断
        if map_from_attr_val == attr_name and map_from_prop_val == attr_prop then
            change_effected = true
        end

        local compare_attr_val = self.registry_obj:get_attibute_prop_val(map_from_attr_val, map_from_prop_val)
        if compare_attr_val == nil then
            log:info("update_attribute_depency_attr: %s.%s is null.", map_from_attr_val, map_from_prop_val)
            return prop_def.E_FAILURE, nil, nil
        end

        local strategy = oper_strategy.get_instance()
        local is_valid = strategy:deal(map_from_condition_val, map_from_value_val, compare_attr_val)

        if map_from_terms_val == prop_def.REGRIST_STRING_AND then
            if is_valid == false then
                set_flag = false
                break
            else
                set_flag = true
            end
        elseif map_from_terms_val == prop_def.REGRIST_STRING_OR then
            if is_valid == true then
                set_flag = true
                break
            else
                set_flag = false
            end
        end
    end

    return prop_def.E_OK, set_flag, change_effected
end

function registry_service:update_list(map_to_attr_val, map_to_prop_val, map_to_value_val)
    local name_map = {
        ['Hidden'] = self.hidden_list,
        ['Immutable'] = self.immutable_list,
        ['ReadOnly'] = self.readonly_list
    }
    local list = name_map[map_to_prop_val]
    if not list then
        return
    end
    list[map_to_attr_val] = map_to_value_val
end

-- 更新响应属性的hiden、gray、readonly属性
function registry_service:update_attribute_depency_attr(depency, attr_name, attr_prop, flag)
    local map_from_list = depency:get_map_from_list()
    if not map_from_list then
        log:error("update_attribute_depency_attr: map_from_list is null.")
        return prop_def.E_FAILURE
    end

    -- 判断Depency的依赖条件是否成立
    local err_code, set_flag, change_effected = self:judge_depency_valid(map_from_list, attr_name, attr_prop)
    if err_code ~= prop_def.E_OK then
        log:info("update_attribute_depency_attr: judge_depency_valid fail.")
        return prop_def.E_FAILURE
    end

    -- 匹配成功,更新map_to_attr的map_to_prop属性为map_to_val(hidden、gray、readonly属性)
    if set_flag == true and change_effected == true then
        local map_to_attr_info = depency:get_map_to_info()
        if map_to_attr_info == nil then
            log:error("update_attribute_depency_attr: map_to_attr_info is null.")
            return prop_def.E_FAILURE
        end

        local map_to_attr_val = map_to_attr_info[prop_def.REGRIST_PROP_MAPTOATTRIBUTE]
        local map_to_prop_val = map_to_attr_info[prop_def.REGRIST_PROP_MAPTOPROPERTY]
        local map_to_value_val = map_to_attr_info[prop_def.REGRIST_PROP_MAPTOVALUE]

        local set_res = self.registry_obj:set_attibute_prop_val(map_to_attr_val, map_to_prop_val, map_to_value_val)
        pcall(function()
            self:update_list(map_to_attr_val, map_to_prop_val, map_to_value_val)
        end)
        if set_res == prop_def.E_FAILURE then
            log:error("update_attribute_depency_attr: set %s.%s fail.", map_to_attr_val, map_to_prop_val)
            return prop_def.E_FAILURE
        end

        if flag == true then
            -- 更新mapto的依赖关系
            self:update_attribute_dependency(map_to_attr_val, map_to_prop_val, true)
        end
    else
        log:debug("update_attribute_depency_attr: %s.%s is changed but has no effect.", attr_name, attr_prop)
    end

    return prop_def.E_OK
end

-- 对外接口:更新某个属性的依赖关系
-- 在Dependency中,如果AttibuteName的AttibuteProp值等于Attibute的CurrentValue
-- 则更新AttibuteName影响属性的hiden、gray、readonly属性
function registry_service:update_attribute_dependency(attr_name, attr_prop, flag)
    local depency_list = self.registry_obj:get_dependeny_list()
    if not depency_list then
        return prop_def.E_FAILURE
    end

    self.DepthValue = self.DepthValue + 1
    if self.DepthValue > 10 or self.DepthValue < 0 then
        self.DepthValue = self.DepthValue - 1
        log:error("update_attribute_dependency: depth_valueerror.")
        return prop_def.E_FAILURE
    end

    if not attr_name or not attr_prop then
        self.DepthValue = self.DepthValue - 1
        log:error("update_attribute_dependency: parameter error.")
        return prop_def.E_FAILURE
    end

    local for_name_depency_list = depency_list[attr_name]
    if not for_name_depency_list then
        self.DepthValue = self.DepthValue - 1
        return prop_def.E_FAILURE
    end

    local len = #for_name_depency_list
    for i = 1, len, 1 do
        local depency = for_name_depency_list[i]
        if not depency then
            self.DepthValue = self.DepthValue - 1
            log:error("update_attribute_dependency: get for_name_depency_list[%d] fail.", i)
            return prop_def.E_FAILURE
        end

        -- 由于Attribute的attr_prop(currentvalue)属性变化,根据deoency更新Attribute的依赖关系
        self:update_attribute_depency_attr(depency, attr_name, attr_prop, flag)
    end

    self.DepthValue = self.DepthValue - 1
end

-- 对外接口:更新属性列表的依赖关系
-- 从配置文件中获取currentValue,然后更新Attibute的CurrentValue
function registry_service:update_attribute_current_value(setting_obj)
    local attr_list = self.registry_obj:get_attribute_list()
    local traverse_list = self.registry_obj:get_traverse_list()
    if not attr_list or not setting_obj or not traverse_list then
        log:error('update_attribute_current_value: param invalid.')
        return prop_def.E_FAILURE
    end

    local att_current_value = prop_def.REGRIST_PROP_CURRENTVALUE

    local setting_cfg = setting_obj:get_cfgs()
    if not setting_cfg then
        log:error('update_attribute_current_value: setting_cfg is null.')
        return prop_def.E_FAILURE
    end

    local traverse_len = #traverse_list
    for i = 1, traverse_len, 1 do
        local setting_name = traverse_list[i]
        local setting_prop = setting_obj:get_val_by_name(setting_name)
        local attribute = attr_list[setting_name]
        if attribute and setting_prop then
            -- 更新attribute的currentvalue
            attribute:set_val_by_name(att_current_value, setting_prop)
            log:debug("update_attribute_current_value: name %s value %s", setting_name, setting_prop)
            -- 由于attribute的currentvalue变化,则需要更新attribute的依赖关系
            self:update_attribute_dependency(setting_name, att_current_value, false)
        end
    end

    return prop_def.E_OK
end

-- 对外接口:更新registry文件依赖关系
-- 1、更新配置值(currentvalue)
-- 2、更新依赖关系(hidden、immutable、readonly)
function registry_service:update_registry(setting_obj)
    if not self.registry_obj then
        log:error("update_registry: registry_obj is null.")
        return bs_util.err_msg(prop_def.RESPONSE_REGISTRY_NULL, prop_def.RESPONSE_INNER_ERROR)
    end

    -- 更新Attibute的CurrentValue
    local res = self:update_attribute_current_value(setting_obj)
    if res == prop_def.E_FAILURE then
        log:error("update_registry: update_attribute_current_value fail.")
        return bs_util.err_msg(prop_def.RESPONSE_UPDATE_ATTRIBUTE_FAIL, prop_def.RESPONSE_INNER_ERROR)
    end

    return bs_util.err_msg(prop_def.E_OK, nil)
end

-- 对外接口:判断registry_json的合法性
function registry_service.judge_registry_json_valid(registry_json)
    if registry_json == nil then
        return false
    end

    local registry_entrys = registry_json[prop_def.REGRIST_PROP_REGISTRYENTRIES]
    local registryVersion = registry_json[prop_def.REGRIST_PROP_REGISTRYVERSIOHN]
    local language = registry_json[prop_def.REGRIST_PROP_LANGUAGE]
    if registry_entrys == nil or registryVersion == nil or language == nil then
        return false
    end

    local attrs = registry_entrys[prop_def.REGRIST_PROP_ATTRIBUTES]
    local menus = registry_entrys[prop_def.REGRIST_PROP_MENUS]
    local dependencies = registry_entrys[prop_def.REGRIST_PROP_DEPENDENCIES]
    if attrs == nil or menus == nil or dependencies == nil then
        return false
    end

    return true
end

-- 对外接口:提供生成错误信息的接口
-- 没有依赖;多个依赖;一个依赖
function registry_service:get_dependency_msg(type, attribute_name)
    if type == nil or attribute_name == nil then
        return nil
    end
    local registry_object = self.registry_obj
    return registry_object:get_map_to_string(type, attribute_name, self)
end

return registry_service