-- 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 org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local interface_del = org_freedesktop_dbus.ObjMgrInterfacesRemoved
local interface_add = org_freedesktop_dbus.ObjMgrInterfacesAdded
local match_rule = org_freedesktop_dbus.MatchRule
local log = require 'mc.logging'
local comm_defs = require 'device_mgmt.comm_defs'
local mdb = require 'mc.mdb'
local ctx = require 'mc.context'


local comm_fun = {}

-- 通用的获取所有设备路径函数
function comm_fun.get_all_device_paths(bus, path_pattern, depth, interface_name)
    return 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})
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.OBJECT_PROPERTIES_INTERFACE})
    local _, interfaces = next(ret)
    if not interfaces then
        return
    end
    local _, interface = next(interfaces)
    if not interface then
        return
    end
    mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
    local device_obj = mdb.get_object(bus, device_path, interface)
    if not device_obj then
        log:notice('[device_mgmt]get_parent_path failed, device_path is %s', device_path)
        return
    end
    local parent_path = comm_fun.get_prop(device_obj, 'ParentPath')
    return parent_path
end

-- 根据路径获取设备树对象
function comm_fun.get_device_obj_by_path(bus, device_path, intf_tbl)
    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, intf_tbl)
    local _, interfaces = next(ret)
    if not interfaces then
        return
    end
    local _, interface = next(interfaces)
    if not interface then
        return
    end
    log:notice('[device_mgmt]get_device_obj_by_path device_path=%s, interface=%s', device_path, interface)
    mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
    local device_obj = mdb.get_object(bus, device_path, interface)
    if not device_obj then
        log:notice('[device_mgmt]get_device_obj_by_path failed, device_path is %s', device_path)
        return
    end
    return device_obj
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
            log:notice('[device_mgmt]device obj added, path is %s', path)
            cb(path)
        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

function comm_fun.get_position_by_object_name(str)
    local result = {}
    for word in string.gmatch(str, "[^_]+") do
        table.insert(result, word)
    end
    return result[#result]
end

function comm_fun.get_device_name_by_object_name(str, class_name)
    local result = {}
    for word in string.gmatch(str, "[^_]+") do
        table.insert(result, word)
    end
    result[1] = class_name
    return table.concat(result, "_")
end

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.OBJECT_PROPERTIES_INTERFACE })
    local _, interfaces = next(ret)
    if not interfaces then
        return
    end
    local _, interface = next(interfaces)
    if not interface then
        return
    end
    mdb.register_interface(interface, comm_defs.INTERFACE_REGISTER_TABLE[interface] or {}, {}, {})
    local card_device_obj = mdb.get_object(bus, device_path, interface)
    if not card_device_obj then
        log:notice('[device_mgmt]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('[device_mgmt]get_object_identifier_by_device_path failed, device_path is %s', device_path)
        return
    end
    return object_identifier
end

function comm_fun.get_prop(dev_obj, name)
    if not dev_obj or not name then
        return nil
    end
    local ok, rsp_code, val = pcall(function()
        return dev_obj:get_property(name)
    end)
    if not ok or rsp_code ~= 0 then
        log:error('[device_mgmt]get_prop failed, rsp_code=%s, val=%s', rsp_code, val)
        return nil
    else
        return val
    end
end

return comm_fun