-- Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
--
-- this file licensed under the 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 client = require 'thermal_mgmt.client'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local match_rule = org_freedesktop_dbus.MatchRule
local interfaces_added = org_freedesktop_dbus.ObjMgrInterfacesAdded
local interfaces_removed = org_freedesktop_dbus.ObjMgrInterfacesRemoved

local base = class()

function base:ctor(bus, data)
    self.bus = bus
    self.data = data
    self.obj_name = ''
    -- 监听的对象信息
    self.objs = nil
    -- 监听的资源树路径
    self.path = nil
    -- 监听的接口
    self.interfaces_table = {}
    -- 监听的属性
    self.props_table = {}
    -- 需要获取的方法
    self.functions_table = {}
end

function base:init()
    self.register_slot = self:register_listen_callback()
end

function base:register_listen_callback()
    if not self.path then
        return
    end
    local interfaces_added_sig = match_rule.signal(interfaces_added.name,
        interfaces_added.interface):with_path_namespace(self.path)
    local interfaces_removed_sig = match_rule.signal(interfaces_removed.name,
        interfaces_removed.interface):with_path_namespace(self.path)

    return {
        self.bus:match(interfaces_added_sig, function(msg)
            self:interface_added_callback(msg:sender(), msg:read('oa{sa{sv}}'))
        end),
        self.bus:match(interfaces_removed_sig, function(msg)
            self:interface_removed_callback(msg:sender(), msg:read('oas'))
        end),
        self:properties_changed()
    }
end

function base:properties_changed()

end

-- 防止组件重启获取不到数据或者获取数据不全
function base:get_infos()
    log:notice('Start to obtain %s information', self.obj_name)
    for interface, client_func in pairs(self.interfaces_table) do
        client_func(client, function(obj)
            if not self.objs then
                self.objs = {}
            end
            if not self.objs[obj.path] then
                self.objs[obj.path] = {}
                self:sync_obj_info(self.objs[obj.path], obj)
                self:action_after_obj_added(obj.path, interface)
            else
                -- 对象存在，再重新更新下内存对象属性值
                self:sync_obj_info(self.objs[obj.path], obj)
            end
            log:notice('Get %s(%s) info successfully', self.obj_name, obj.path)
        end)
    end
end

-- 同步资源树对象数据
function base:sync_obj_info(targ_obj, sync_obj)
    for prop, _ in pairs(self.props_table) do
        if sync_obj[prop] ~= nil then
            targ_obj[prop] = sync_obj[prop]
            log:notice('Sync object prop(%s): %s', prop, tostring(sync_obj[prop]))
        end
    end
    for method, _ in pairs(self.functions_table) do
        if sync_obj[method] ~= nil then
            targ_obj[method] = sync_obj[method]
            log:notice('Sync object method: %s', method)
        end
    end
end

function base:get_obj_info_by_interface(obj_path, interface)
    if not self.interfaces_table[interface] then
        return
    end
    if not self.objs then
        self.objs = {}
    end
    if not self.objs[obj_path] then
        self.objs[obj_path] = {}
    end
    local obj
    self.interfaces_table[interface](client, function(o)
        if o.path == obj_path then
            obj = o
        end
    end)
    if not obj then
        log:error("Get object(%s) failed, interface(%s)", obj_path, interface)
        return
    end
    self:sync_obj_info(self.objs[obj_path], obj)
    log:notice('Add object(%s) completed, interface: %s', obj_path, interface)
end

function base:interface_added_callback(sender, path, interfaces_and_properties)
    for interface, _ in pairs(interfaces_and_properties) do
        if self.interfaces_table[interface] then
            log:notice('Add %s(%s), interface: %s', self.obj_name, path, interface)
            self:get_obj_info_by_interface(path, interface)
            self:action_after_obj_added(path, interface)
        end
    end
end

function base:interface_removed_callback(sender, path, interfaces)
    for _, interface in pairs(interfaces) do
        if self.objs[path] and self.interfaces_table[interface] then
            log:notice('Remove %s(%s), interface: %s', self.obj_name, path, interface)
            self.objs[path] = nil
            self:action_after_obj_removed(path, interface)
            return
        end
    end
end

function base:property_changed_callback(path, interface, changed_props, invalidated_props)
    if not self.interfaces_table[interface] then
        return
    end
    if not self.objs or not self.objs[path] then
        self:get_obj_info_by_interface(path, interface)
        self:action_after_obj_added(path, interface)
        return
    end
    for prop, value in pairs(changed_props) do
        if self.props_table[prop] then
            self.objs[path][prop] = value:value()
            log:debug('Object(%s) prop(%s) changed: %s', path, prop, tostring(value:value()))
            self:action_after_property_changed(path, interface, prop, value:value())
        end
    end
end

-- 用于实现对象添加后的动作，由子类实现
function base:action_after_obj_added(path, interface)
end

-- 用于实现对象删除后的动作，由子类实现
function base:action_after_obj_removed(path, interface)
end

-- 用于实现特定属性变更后的动作，由子类实现
function base:action_after_property_changed(path, interface, prop, value)
end

function base:get_system_id_by_path(path)
    local system_id = string.match(path, "/Systems/(%d+)/")
    if system_id then
        return tonumber(system_id)
    end
    return nil
end

return base
