-- Copyright (c) 2025 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 log = require 'mc.logging'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local interface_add = org_freedesktop_dbus.ObjMgrInterfacesAdded
local interface_del = org_freedesktop_dbus.ObjMgrInterfacesRemoved
local match_rule = org_freedesktop_dbus.MatchRule
local comm_defs = require 'device.class.nic_mgmt.comm_defs'
local ctx = require 'mc.context'
local mdb = require 'mc.mdb'
local skynet = require 'skynet'
local queue = skynet.queue()

local comm_fun = {}

-- 通用的获取所有设备路径函数
function comm_fun.get_all_device_paths(bus, path_pattern, depth, interface_name)
    local paths = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH, comm_defs.MDB_INTERFACE, 'GetSubPaths',
        'a{ss}sias', ctx.get_context_or_default(), path_pattern, depth, {interface_name})
    return paths
end

-- 通用的获取父对象路径函数
function comm_fun.get_parent_path(bus, device_path)
    local ret = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH, comm_defs.MDB_INTERFACE, 'GetObject', 'a{ss}sas',
        ctx.get_context_or_default(), device_path, {comm_defs.COMMON_PROPERTIES_INTERFACE})
    local device_obj
    for _, interfaces in pairs(ret) do
        for _, interface in pairs(interfaces) do
            mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
            device_obj = mdb.get_object(bus, device_path, interface)
            goto continue
        end
    end
    ::continue::
    if not device_obj then
        log:notice('get_parent_path failed, device_path is %s', device_path)
        return
    end
    local parent_path
    ret, parent_path = device_obj:get_property('ParentPath')
    if ret ~= 0 then
        log:error('get_property failed, prop=ParentPath, device_path=%s', device_path)
        return
    end
    return parent_path
end

-- 通用的获取对象名称函数
function comm_fun.get_object_name_by_device_path(bus, device_path, replace_pattern)
    local ret = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH,
        comm_defs.MDB_INTERFACE, 'GetObject',
        'a{ss}sas', ctx.get_context_or_default(),
        device_path, { comm_defs.COMMON_PROPERTIES_INTERFACE })
    local card_device_obj
    for _, interfaces in pairs(ret) do
        for _, interface in pairs(interfaces) do
            mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
            card_device_obj = mdb.get_object(bus, device_path, interface)
            goto continue
        end
    end
    ::continue::
    if not card_device_obj then
        log:notice('get_object_name_by_device_path failed, device_path is %s', device_path)
        return
    end
    local object_name
    ret, object_name = card_device_obj:get_property('ObjectName')
    if ret ~= 0 or not object_name then
        log:notice('get_object_name_by_device_path failed, device_path is %s', device_path)
        return
    end
    if replace_pattern then
        return string.gsub(object_name, replace_pattern.from, replace_pattern.to)
    end
    return object_name
end

-- 获取对象ObjectIdentifier函数
function comm_fun.get_object_identifier_by_device_path(bus, device_path)
    local ret = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH,
        comm_defs.MDB_INTERFACE, 'GetObject',
        'a{ss}sas', ctx.get_context_or_default(),
        device_path, { comm_defs.COMMON_PROPERTIES_INTERFACE })
    local card_device_obj
    for _, interfaces in pairs(ret) do
        for _, interface in pairs(interfaces) do
            mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
            card_device_obj = mdb.get_object(bus, device_path, interface)
            goto continue
        end
    end
    ::continue::
    if not card_device_obj then
        log:notice('get_object_identifier_by_device_path failed, device_path is %s', device_path)
        return
    end
    local res, object_identifier = card_device_obj:get_property('ObjectIdentifier')
    if res ~= 0 or not object_identifier then
        log:notice('get_object_identifier_by_device_path failed, device_path is %s', device_path)
        return
    end
    return object_identifier
end

-- 获取对象的positon
function comm_fun.get_position_by_object_name(object_name)
    -- 以下划线分割对象名称，返回最后一段为posiotn
    local result = {}
    for word in string.gmatch(object_name, "[^_]+") do
        table.insert(result, word)
    end
    return result[#result]
end

-- 通用的设置信号监听函数
function comm_fun.set_interface_add_signal(bus, sig_slot, path_pattern, interface_name, cb)
    local interface_add_sig = match_rule.signal(interface_add.name, interface_add.interface):with_path_namespace(
        path_pattern)
    sig_slot[#sig_slot + 1] = bus:match(interface_add_sig, function(msg)
        local path, interfaces_and_props = msg:read('oa{sa{sv}}')
        if interfaces_and_props[interface_name] then
            queue(function()
                log:notice('device obj added, path is %s', path)
                cb(path)
            end)
        end
    end)
end

function comm_fun.set_interface_del_signal(bus, sig_slot, path_pattern, interface_name, cb)
    local interface_del_sig = match_rule.signal(interface_del.name, interface_del.interface):with_path_namespace(
        path_pattern)
    sig_slot[#sig_slot + 1] = bus:match(interface_del_sig, function(msg)
        local path, _ = msg:read('oas')
        cb(path)
    end)
end

-- 通过path和interface获取设备对象函数
function comm_fun.get_device_obj_by_path_interface(bus, path, ori_interface)
    local ret = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH, comm_defs.MDB_INTERFACE, 'GetObject', 'a{ss}sas',
        ctx.get_context_or_default(), path, {ori_interface})
    local device_obj
    for _, interfaces in pairs(ret) do
        for _, interface in pairs(interfaces) do
            mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
            device_obj = mdb.get_object(bus, path, interface)
            goto continue
        end
    end
    ::continue::
    if not device_obj then
        log:notice('get_device_obj_by_path_interface failed, device_path is %s, interface is %s', path, interface)
        return nil
    end
    return device_obj
end

return comm_fun