/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
 * -*- coding: utf-8 -*-
 *
 * Copyright (C) 2025 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "DbusServer.h"
#include "AudioMethod.h"
#include "AudioContext.h"

namespace UkuiAudioFramwork
{
const char* AudioDBusServer::m_pIntrospectionXml = R"(
<node>
  <interface name="org.ukui.volume.control">
    <method name="getBackend">
      <arg type="s" name="backend" direction="out"/>
    </method>
    <method name="setBackend">
      <arg type="i" name="backend" direction="in"/>
    </method>
    <method name="getDesktopEnvironmentVersion">
      <arg type="i" name="version" direction="out"/>
    </method>
    <method name="loadModule">
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="param" direction="in"/>
    </method>
    <method name="unloadModule">
      <arg type="s" name="name" direction="in"/>
    </method>
    <method name="getDetailVolume">
      <arg type="i" name="type" direction="in"/>
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="card" direction="in"/>
      <arg type="i" name="volume" direction="out"/>
    </method>
    <method name="setDetailVolume">
      <arg type="i" name="type" direction="in"/>
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="card" direction="in"/>
      <arg type="i" name="volume" direction="in"/>
    </method>
    <method name="getVolume">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type="i" name="volume" direction="out"/>
    </method>
    <method name="setVolume">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type="i" name="volume" direction="in"/>
    </method>
    <method name="getBalance">
      <arg type="i" name="type" direction="in"/>
      <arg type="d" name="volume" direction="out"/>
    </method>
    <method name="setBalance">
      <arg type="i" name="type" direction="in"/>
      <arg type="d" name="volume" direction="in"/>
    </method>
    <method name="getDetailMute">
      <arg type="i" name="type" direction="in"/>
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="card" direction="in"/>
      <arg type="b" name="mute" direction="out"/>
    </method>
    <method name="setDetailMute">
      <arg type="i" name="type" direction="in"/>
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="card" direction="in"/>
      <arg type="b" name="mute" direction="in"/>
    </method>
    <method name="getMute">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type="b" name="mute" direction="out"/>
    </method>
    <method name="setMute">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type="b" name="mute" direction="in"/>
    </method>
    <method name="getDefaultDevice">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type='(issss)' name='default' direction="out"/>
    </method>
    <method name="setDefaultDevice">
      <arg type="i" name="type" direction="in"/>
      <arg type="i" name="index" direction="in"/>
      <arg type="s" name="port" direction="in"/>
      <arg type="s" name="card" direction="in"/>
    </method>
    <method name="getDeviceList">
      <arg type="i" name="type" direction="in"/>
      <arg type="s" name="devList" direction="out"/>
    </method>
    <method name="getSinkList">
      <arg type='a(issss)' name="list" direction="out"/>
    </method>
    <method name="getSourceList">
      <arg type='a(issss)' name="list" direction="out"/>
    </method>
    <method name="getAvailablePortList">
      <arg type="i" name="type" direction="in"/>
      <arg type='a(ssssiiib)' name="portList" direction="out"/>
    </method>
    <method name="getSinkInputList">
      <arg type='a(siiss)' name="list" direction="out"/>
    </method>
    <method name="getSourceOutputList">
      <arg type='a(siiss)' name="list" direction="out"/>
    </method>
    <method name="getStreamMediaList">
      <arg type='a(sssa(iiibs))' name="list" direction="out"/>
    </method>
    <method name="setEnabled">
      <arg type="s" name="card" direction="in"/>
      <arg type="s" name="port" direction="in"/>
      <arg type="b" name="enabled" direction="in"/>
    </method>
    <method name="isEnabled">
      <arg type="s" name="card" direction="in"/>
      <arg type="s" name="port" direction="in"/>
      <arg type="b" name="enabled" direction="out"/>
    </method>
    <method name="setAutoPauseStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getAutoPauseStatus">
      <arg type="b" name="enabled" direction="out"/>
    </method>
    <method name="setMultiAudioCombine">
      <arg type='i' name="type" direction="in"/>
      <arg type='b' name="status" direction="in"/>
      <arg type='as' name="devList" direction="in"/>
    </method>
    <method name="getMultiAudioCombineStatus">
      <arg type='i' name="type" direction="in"/>
      <arg type="b" name="enabled" direction="out"/>
    </method>
    <method name="isValidDevice">
      <arg type='i' name="type" direction="in"/>
      <arg type="b" name="isValid" direction="out"/>
    </method>
    <signal name="volumeChanged">
      <arg type="i" name="type"/>
      <arg type="i" name="index"/>
      <arg type="v" name="volume"/>
    </signal>
    <signal name="muteChanged">
      <arg type="i" name="type"/>
      <arg type="i" name="index"/>
      <arg type="b" name="mute"/>
    </signal>
    <signal name="deviceChanged">
      <arg type="i" name="type"/>
      <arg type="s" name="portName"/>
      <arg type="s" name="cardName"/>
    </signal>
    <signal name="deviceAdjust">
      <arg type="i" name="type"/>
    </signal>
    <signal name="portChanged">
      <arg type="i" name="type"/>
      <arg type="i" name="name"/>
    </signal>
    <signal name="cardRemoved">
      <arg type="s" name="name"/>
    </signal>
    <signal name="addStream">
      <arg type="i" name="index"/>
      <arg type="i" name="value"/>
      <arg type="s" name="iconName"/>
      <arg type="s" name="descName"/>
    </signal>
    <signal name="removeStream">
      <arg type="i" name="type"/>
    </signal>
    <signal name="addStream">
      <arg type="i" name="direction"/>
      <arg type="i" name="index"/>
      <arg type="i" name="value"/>
      <arg type="s" name="iconName"/>
      <arg type="s" name="descName"/>
    </signal>
    <signal name="removeStream">
      <arg type="i" name="direction"/>
      <arg type="i" name="index"/>
    </signal>
  </interface>
</node>
)";

const char* AudioDBusServer::m_pAudioSettingXml = R"(
<node>
  <interface name="org.ukui.audio.settings">
    <method name="getVolumeBoostStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setVolumeBoostStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getVolumeBoostValue">
      <arg type="i" name="value" direction="out"/>
    </method>
    <method name="setVolumeBoostValue">
      <arg type="i" name="value" direction="in"/>
    </method>
    <method name="getMonoStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setMonoStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getEchoCancelStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setEchoCancelStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getLoopbackStatus">
      <arg type="b" name="value" direction="out"/>
    </method>
    <method name="setLoopbackStatus">
      <arg type="b" name="value" direction="in"/>
    </method>
    <method name="getAlertStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setAlertStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getStartupStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setStartupStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getPoweroffStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setPoweroffStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getLogoutStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setLogoutStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getWakeupStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setWakeupStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getGlobalThemeName">
      <arg type="s" name="name" direction="out"/>
    </method>
    <method name="setGlobalThemeName">
      <arg type="s" name="name" direction="in"/>
    </method>
    <method name="getSoundThemeName">
      <arg type="s" name="name" direction="out"/>
    </method>
    <method name="setSoundThemeName">
      <arg type="s" name="name" direction="in"/>
    </method>
    <method name="getCustomThemeStatus">
      <arg type="b" name="status" direction="out"/>
    </method>
    <method name="setCustomThemeStatus">
      <arg type="b" name="status" direction="in"/>
    </method>
    <method name="getNotifyGeneralName">
      <arg type="s" name="name" direction="out"/>
    </method>
    <method name="setNotifyGeneralName">
      <arg type="s" name="name" direction="in"/>
    </method>
    <method name="getVolumeChangedName">
      <arg type="s" name="name" direction="out"/>
    </method>
    <method name="setVolumeChangedName">
      <arg type="s" name="name" direction="in"/>
    </method>
    <signal name="changed">
      <arg type="s" name="key"/>
      <arg type="v" name="value"/>
    </signal>
  </interface>
</node>
)";

const char* AudioDBusServer::m_pSoundThemeXml = R"(
<node>
  <interface name="org.ukui.soundTheme">
    <method name="getSoundThemeList">
      <arg type='a(ss)' name='list' direction='out'/>
    </method>
    <method name="getSoundEffectFileList">
      <arg type='s' name='name' direction='in'/>
      <arg type='a(ss)' name='list' direction='out'/>
    </method>
  </interface>
</node>
)";

GDBusInterfaceVTable AudioDBusServer::m_interfaceVTable = {
    &AudioDBusServer::handleMethodCall,
    nullptr,
    nullptr
};

std::string getProcessName(pid_t pid)
{
    std::ostringstream oss;
    oss << "/proc/" << pid << "/exe";
    std::string procPath = oss.str();

    try {
        std::filesystem::path targetPath = std::filesystem::read_symlink(procPath);
        std::string path = targetPath.string();

        // 提取进程名（路径的最后一部分）
        size_t pos = path.rfind('/');
        if (pos != std::string::npos) {
            return path.substr(pos + 1);
        } else {
            return path;
        }
    } catch (const std::filesystem::filesystem_error& e) {
        return "Unknown";
    }
}


bool AudioDBusServer::initialize() {
    GError* error = nullptr;

    m_pConnection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error);
    if (!m_pConnection) {
        AUDIO_ERROR("Failed to connect to DBus: {}.", error->message);
        g_error_free(error);
        return false;
    }

    auto proxy = g_dbus_proxy_new_sync(
        m_pConnection,
        G_DBUS_PROXY_FLAGS_NONE,
        nullptr,
        "org.freedesktop.DBus",
        "/org/freedesktop/DBus",
        "org.freedesktop.DBus",
        nullptr,
        &error);
    if (!proxy) {
        AUDIO_ERROR("Failed to create DBus proxy: {}.", error->message);
        g_error_free(error);
        return false;
    }
    auto result = g_dbus_proxy_call_sync(
        proxy,
        "RequestName",
        g_variant_new("(su)", AUDIO_OBJECT_SERVICE, 0),
        G_DBUS_CALL_FLAGS_NONE,
        -1,
        nullptr,
        &error);
    g_object_unref(proxy);
    if (!result) {
        AUDIO_ERROR("Failed to request service name: {}.", error->message);
        g_error_free(error);
        return false;
    }
    g_variant_unref(result);

    // Register object
    auto introspectionData = g_dbus_node_info_new_for_xml(m_pIntrospectionXml, &error);
    if (!introspectionData) {
        AUDIO_ERROR("Failed to parse introspection XML: {}.", error->message);
        g_error_free(error);
        return false;
    }

    g_dbus_connection_register_object(
        m_pConnection,
        AUDIO_OBJECT_PATH,
        introspectionData->interfaces[0],
        &m_interfaceVTable,
        this,
        nullptr,
        &error);

    if (error) {
        AUDIO_ERROR("Failed to register object: {}.", error->message);
        g_error_free(error);
        return false;
    }

    // Register object
    auto audioSettingData = g_dbus_node_info_new_for_xml(m_pAudioSettingXml, &error);
    if (!audioSettingData) {
        AUDIO_ERROR("Failed to parse audioSetting XML: {}.", error->message);
        g_error_free(error);
        return false;
    }

    g_dbus_connection_register_object(
        m_pConnection,
        AUDIO_OBJECT_PATH,
        audioSettingData->interfaces[0],
        &m_interfaceVTable,
        this,
        nullptr,
        &error);

    if (error) {
        AUDIO_ERROR("Failed to register object: {}.", error->message);
        g_error_free(error);
        return false;
    }

    // Register object
    auto soundThemeData = g_dbus_node_info_new_for_xml(m_pSoundThemeXml, &error);
    if (!soundThemeData) {
        AUDIO_ERROR("Failed to parse audioSetting XML: {}", error->message);
        g_error_free(error);
        return false;
    }

    g_dbus_connection_register_object(
        m_pConnection,
        AUDIO_OBJECT_PATH,
        soundThemeData->interfaces[0],
        &m_interfaceVTable,
        this,
        nullptr,
        &error);

    if (error) {
        AUDIO_ERROR("Failed to register object: {}.", error->message);
        g_error_free(error);
        return false;
    }

    m_pMainLoop = g_main_loop_new(nullptr, false);
    return true;
}

void AudioDBusServer::run() {
    g_main_loop_run(m_pMainLoop);
}

void AudioDBusServer::sendSignal(const char* objectPath, const char* interfaceName, const char* signalName, GVariant* param)
{
    g_dbus_connection_emit_signal(
        m_pConnection,
        nullptr,
        objectPath,
        interfaceName,
        signalName,
        param,
        nullptr);
}

void AudioDBusServer::handleMethodCall(GDBusConnection* connection,
                             const char* sender,
                             const char* objectPath,
                             const char* interfaceName,
                             const char* methodName,
                             GVariant* parameters,
                             GDBusMethodInvocation* invocation,
                             gpointer userData) {
    AudioDBusServer* self = static_cast<AudioDBusServer*>(userData);

    GError *err = nullptr;
    guint pid = 0;

    GVariant *result = g_dbus_connection_call_sync(connection,
                                                   "org.freedesktop.DBus",
                                                   "/org/freedesktop/DBus",
                                                   "org.freedesktop.DBus",
                                                   "GetConnectionUnixProcessID",
                                                   g_variant_new("(s)", sender),
                                                   G_VARIANT_TYPE("(u)"),
                                                   G_DBUS_CALL_FLAGS_NONE,
                                                   -1,
                                                   nullptr,
                                                   &err);
    if (result) {
        g_variant_get(result, "(u)", &pid);
        g_variant_unref(result);
    }

    if (err) {
        AUDIO_ERROR("Unable to get the caller process, error: {}", err->message);
        g_error_free(err);
    } else {
        AUDIO_DEBUG("{} calling the {} method.", getProcessName(pid), methodName);
    }

    if (g_strcmp0(methodName, "getBackend") == 0) {
        std::string backend = AudioMethod::getInstance().getBackend();
        g_dbus_method_invocation_return_value(invocation,
                                              g_variant_new("(s)", backend.c_str()));
    }
    else if (g_strcmp0(methodName, "setBackend") == 0) {
        uint32_t type;

        g_variant_get(parameters, "(i)", &type);
        AudioMethod::getInstance().setBackend(IntToEnum<BackendType>(type));
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getDesktopEnvironmentVersion") == 0) {
        auto version = AudioMethod::getInstance().getDesktopEnvironmentVersion();

        g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", version));
    }
    else if (g_strcmp0(methodName, "loadModule") == 0) {
        const gchar* name, *param;

        g_variant_get(parameters, "(ss)", &name, &param);
        AudioMethod::getInstance().loadModule(name, param);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "unloadModule") == 0) {
        const gchar* name;

        g_variant_get(parameters, "(ss)", &name);
        AudioMethod::getInstance().unloadModule(name);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getDetailVolume") == 0) {
        uint32_t type;
        const gchar* name;
        const gchar* dev;

        g_variant_get(parameters, "(iss)", &type, &name, &dev);
        auto volume = AudioMethod::getInstance().getDetailVolume(IntToEnum<NodeType>(type), name, dev);
        g_dbus_method_invocation_return_value(invocation,
                                               g_variant_new("(i)", volume));
    }
    else if (g_strcmp0(methodName, "setDetailVolume") == 0) {
        uint32_t type, volume;
        const gchar* name;
        const gchar* dev;

        g_variant_get(parameters, "(issi)", &type, &name, &dev, &volume);
        AudioMethod::getInstance().setDetailVolume(static_cast<NodeType>(type), name, dev, volume);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getVolume") == 0) {
        uint32_t type, idx;

        g_variant_get(parameters, "(ii)", &type, &idx);
        auto volume = AudioMethod::getInstance().getVolume(IntToEnum<NodeType>(type), idx);
        g_dbus_method_invocation_return_value(invocation,
                                               g_variant_new("(i)", volume));
    }
    else if (g_strcmp0(methodName, "setVolume") == 0) {
        uint32_t type, idx, volume;

        g_variant_get(parameters, "(iii)", &type, &idx, &volume);
        AudioMethod::getInstance().setVolume(static_cast<NodeType>(type), idx, volume);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }

    else if (g_strcmp0(methodName, "getBalance") == 0) {
        uint32_t type;

        g_variant_get(parameters, "(i)", &type);
        auto volume = AudioMethod::getInstance().getBalance(IntToEnum<NodeType>(type));
        g_dbus_method_invocation_return_value(invocation,
                                              g_variant_new("(d)", volume));
    }
    else if (g_strcmp0(methodName, "setBalance") == 0) {
        uint32_t type;
        double volume;

        g_variant_get(parameters, "(id)", &type, &volume);
        AudioMethod::getInstance().setBalance(static_cast<NodeType>(type), volume);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getDetailMute") == 0) {
        uint32_t type;
        const gchar* name;
        const gchar* dev;

        g_variant_get(parameters, "(iss)", &type, &name, &dev);
        auto mute = AudioMethod::getInstance().getDetailMute(IntToEnum<NodeType>(type), name, dev);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", mute));
    }
    else if (g_strcmp0(methodName, "setDetailMute") == 0) {
        uint32_t type;
        bool mute = false;
        const gchar* name;
        const gchar* dev;

        g_variant_get(parameters, "(issb)", &type, &name, &dev, &mute);
        AudioMethod::getInstance().setDetailMute(IntToEnum<NodeType>(type), name, dev, mute);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getMute") == 0) {
        uint32_t type, idx;

        g_variant_get(parameters, "(ii)", &type, &idx);
        auto mute = AudioMethod::getInstance().getMute(IntToEnum<NodeType>(type), idx);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", mute));
    }
    else if (g_strcmp0(methodName, "setMute") == 0) {
        uint32_t type, idx;
        bool mute = false;

        g_variant_get(parameters, "(iib)", &type, &idx, &mute);
        AudioMethod::getInstance().setMute(IntToEnum<NodeType>(type), idx, mute);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getDefaultDevice") == 0) {
        uint32_t type, idx;

        g_variant_get(parameters, "(ii)", &type, &idx);
        auto info = AudioMethod::getInstance().getDefaultDevice(IntToEnum<NodeType>(type), idx);

        g_dbus_method_invocation_return_value(invocation, g_variant_new("((issss))", info.idx, info.name.c_str(), info.cardName.c_str(),
                                              info.activePortName.c_str(), info.activePortLabel.c_str()));
    }
    else if (g_strcmp0(methodName, "setDefaultDevice") == 0) {
        uint32_t type, index;
        const gchar* dev;
        const gchar* name;

        g_variant_get(parameters, "(iiss)", &type, &index, &name, &dev);
        AudioMethod::getInstance().setDefaultDevice(IntToEnum<NodeType>(type), index, name, dev);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getDeviceList") == 0) {
        uint32_t type;
        const gchar** dev;

        g_variant_get(parameters, "(i)", &type);
//        dev = AudioMethod::getInstance().getDeviceList(IntToEnum<NodeType>(type));
//        g_dbus_method_invocation_return_value(invocation, g_variant_new("(as)", dev));
    }
    else if (g_strcmp0(methodName, "getSinkList") == 0) {
        auto list = AudioMethod::getInstance().getSinkList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(issss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(issss)"));
        for (const auto& s : list) {
            g_variant_builder_add(&ret, "(issss)", s.idx, s.name.c_str(), s.cardName.c_str(),
                                  s.activePortName.c_str(), s.activePortLabel.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(issss))", &ret));
    }
    else if (g_strcmp0(methodName, "getSourceList") == 0) {
        auto list = AudioMethod::getInstance().getSourceList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(issss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(issss)"));
        for (const auto& s : list) {
            g_variant_builder_add(&ret, "(issss)", s.idx, s.name.c_str(), s.cardName.c_str(), s.activePortName.c_str(),
                                  s.activePortLabel.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(issss))", &ret));
    }
    else if (g_strcmp0(methodName, "getAvailablePortList") == 0) {
        uint32_t type;
        g_variant_get(parameters, "(i)", &type);

        auto devList = AudioMethod::getInstance().getAvailablePortList(IntToEnum<NodeType>(type));

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(ssssiiib)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(ssssiiib)"));
        for (const auto& d : devList) {
            g_variant_builder_add(&ret, "(ssssiiib)", d->cardName.c_str(), d->cardDesc.c_str(), d->portName.c_str(), d->portLabel.c_str(),
                                  d->priority, d->direction, d->available, d->enabled);

        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(ssssiiib))", &ret));
    }
    else if (g_strcmp0(methodName, "getSinkInputList") == 0) {
        auto siList = AudioMethod::getInstance().getSinkInputList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(siiss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(siiss)"));
        for (const auto& si : siList) {
            g_variant_builder_add(&ret, "(siiss)", si.name.c_str(), si.index, si.volume, si.mediaRole.c_str(), si.iconName.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(siiss))", &ret));
    }
    else if (g_strcmp0(methodName, "getSourceOutputList") == 0) {
        auto soList = AudioMethod::getInstance().getSourceOutputList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(siiss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(siiss)"));
        for (const auto& so : soList) {
            g_variant_builder_add(&ret, "(siiss)", so.name.c_str(), so.index, so.volume, so.mediaRole.c_str(), so.iconName.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(siiss))", &ret));
    }
    else if (g_strcmp0(methodName, "getStreamMediaList") == 0) {
        auto streamList = AudioMethod::getInstance().getStreamMediaList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(sssa(iiibs))"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE("a(sssa(iiibs))"));

        for (const auto& s : streamList) {
            GVariantBuilder ret1;
            g_variant_builder_init(&ret1, G_VARIANT_TYPE("a(iiibs)"));
            g_variant_builder_open(&ret1, G_VARIANT_TYPE ("a(iiibs)"));
            for (const auto& media : s.mediaInfoList) {
                g_variant_builder_add(&ret1, "(iiibs)", media.direction, media.index, media.volume, media.mute, media.name.c_str());
            }
            g_variant_builder_close(&ret1);
            g_variant_builder_add(&ret, "(sssa(iiibs))", s.mediaRole.c_str(), s.iconName.c_str(), s.binary.c_str(), &ret1);
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(sssa(iiibs)))", &ret));
    }
    else if (g_strcmp0(methodName, "setEnabled") == 0) {
        bool enable;
        const gchar* port;
        const gchar* card;

        g_variant_get(parameters, "(ssb)", &card, &port, &enable);
        AudioMethod::getInstance().setEnabled(card, port, enable);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "isEnabled") == 0) {
        const gchar* port;
        const gchar* card;

        g_variant_get(parameters, "(ss)", &card, &port);
        auto status = AudioMethod::getInstance().isEnabled(card, port);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setAutoPauseStatus") == 0) {
        bool status;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setAutoPauseStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getAutoPauseStatus") == 0) {

        auto status = AudioMethod::getInstance().getAutoPauseStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setMultiAudioCombine") == 0) {
        int type;
        gboolean status;
        const gchar* s;
        GVariantIter* iter;
        std::vector<std::string> devList{};
        g_variant_get(parameters, "(ibas)", &type, &status, &iter);

        while (g_variant_iter_loop (iter, "s", &s))
            devList.push_back(s);
        g_variant_iter_free (iter);

        AudioMethod::getInstance().setMultiAudioCombine(IntToEnum<NodeType>(type), status, devList);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getMultiAudioCombineStatus") == 0) {
        int type;

        g_variant_get(parameters, "(i)", &type);
        auto status = AudioMethod::getInstance().getMultiAudioCombineStatus(IntToEnum<NodeType>(type));
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "isValidDevice") == 0) {
        int type;

        g_variant_get(parameters, "(i)", &type);
        auto status = AudioMethod::getInstance().isValidDevice(IntToEnum<NodeType>(type));

        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "getVolumeBoostStatus") == 0) {

        auto status = AudioMethod::getInstance().getVolumeBoostStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setVolumeBoostStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setVolumeBoostStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getVolumeBoostValue") == 0) {

        auto value = AudioMethod::getInstance().getVolumeBoostValue();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", value));
    }
    else if (g_strcmp0(methodName, "setVolumeBoostValue") == 0) {
        uint32_t value;

        g_variant_get(parameters, "(i)", &value);
        AudioMethod::getInstance().setVolumeBoostValue(value);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getMonoStatus") == 0) {

        auto status = AudioMethod::getInstance().getMonoStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setMonoStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setMonoStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getEchoCancelStatus") == 0) {

        auto status = AudioMethod::getInstance().getEchoCancelStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setEchoCancelStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setEchoCancelStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getLoopbackStatus") == 0) {

        auto status = AudioMethod::getInstance().getLoopbackStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setLoopbackStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setLoopbackStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getAlertStatus") == 0) {

        auto status = AudioMethod::getInstance().getAlertStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setAlertStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setAlertStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getStartupStatus") == 0) {

        auto status = AudioMethod::getInstance().getStartupStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setStartupStatus") == 0) {
        auto status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setStartupStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getPoweroffStatus") == 0) {

        auto status = AudioMethod::getInstance().getPoweroffStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setPoweroffStatus") == 0) {
        auto status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setPoweroffStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getLogoutStatus") == 0) {

        auto status = AudioMethod::getInstance().getLogoutStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setLogoutStatus") == 0) {
        bool status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setLogoutStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getWakeupStatus") == 0) {

        auto status = AudioMethod::getInstance().getWakeupStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setWakeupStatus") == 0) {
        auto status = false;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setWakeupStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getGlobalThemeName") == 0) {

        auto name = AudioMethod::getInstance().getGlobalThemeName();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", name.c_str()));
    }
    else if (g_strcmp0(methodName, "setGlobalThemeName") == 0) {
        std::string name;

        g_variant_get(parameters, "(s)", &name);
        AudioMethod::getInstance().setGlobalThemeName(name);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getSoundThemeName") == 0) {

        auto name = AudioMethod::getInstance().getSoundThemeName();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", name.c_str()));
    }
    else if (g_strcmp0(methodName, "setSoundThemeName") == 0) {
        auto name = "";

        g_variant_get(parameters, "(s)", &name);
        AudioMethod::getInstance().setSoundThemeName(name);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getCustomThemeStatus") == 0) {

        auto status = AudioMethod::getInstance().getCustomThemeStatus();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", status));
    }
    else if (g_strcmp0(methodName, "setCustomThemeStatus") == 0) {
        bool status;

        g_variant_get(parameters, "(b)", &status);
        AudioMethod::getInstance().setCustomThemeStatus(status);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getNotifyGeneralName") == 0) {

        auto name = AudioMethod::getInstance().getNotifyGeneralName();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", name.c_str()));
    }
    else if (g_strcmp0(methodName, "setNotifyGeneralName") == 0) {
        auto name = "";

        g_variant_get(parameters, "(s)", &name);
        AudioMethod::getInstance().setNotifyGeneralName(name);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getVolumeChangedName") == 0) {

        auto name = AudioMethod::getInstance().getVolumeChangedName();
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", name.c_str()));
    }
    else if (g_strcmp0(methodName, "setVolumeChangedName") == 0) {
        auto name = "";

        g_variant_get(parameters, "(s)", &name);
        AudioMethod::getInstance().setVolumeChangedName(name);
        g_dbus_method_invocation_return_value(invocation, nullptr);
    }
    else if (g_strcmp0(methodName, "getSoundThemeList") == 0) {
        auto themeList = AudioMethod::getInstance().getSoundThemeList();

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(ss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(ss)"));
        for (const auto& m : themeList) {
            g_variant_builder_add(&ret, "(ss)", m.name.c_str(), m.description.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(ss))", &ret));
    }
    else if (g_strcmp0(methodName, "getSoundEffectFileList") == 0) {
        const gchar* name;

        g_variant_get(parameters, "(s)", &name);
        std::list<SoundEffectFileInfo> fileList = AudioMethod::getInstance().getSoundEffectFileList(name);

        GVariantBuilder ret;
        g_variant_builder_init(&ret, G_VARIANT_TYPE("a(ss)"));
        g_variant_builder_open(&ret, G_VARIANT_TYPE ("a(ss)"));
        for (const auto& f : fileList) {
            g_variant_builder_add(&ret, "(ss)", f.name.c_str(), f.description.c_str());
        }
        g_variant_builder_close(&ret);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(ss))", &ret));
    }
}

AudioDBusServer::~AudioDBusServer() {
    if (m_pMainLoop) {
        g_main_loop_unref(m_pMainLoop);
    }
}
}
