 #include "MainWidget.h"

#include <QDebug>
#include <QHBoxLayout>
#include <QMessageBox>

#include "ukui2.0/Ukcc2MainWidget.h"
#include "Ukui4MainWidget.h"
#include "Ukui5MainWidget.h"
#include "ClientMethod.h"
#include "ConcreteStrategy.h"
#include "DeviceManagerMainWidget.h"


MainWidget::MainWidget(QWidget* parent)
    : QWidget(parent)
{
    getDesktopEnvironmentVersion();

    initUi();

    initSlots();

    initClientManager();

    initData();
}

void MainWidget::initUi()
{
    switch (m_version) {
    case DesktopEnvironmentVersion::DESKTOP_ENVIRONMENT_VERSION_UKUI2:
//        m_pMainWid = new Ukcc2MainWidget(m_switchBtnItemMap, m_volumeSliderItemMap, m_selectComboxItemMap, this);
//        break;
    case DesktopEnvironmentVersion::DESKTOP_ENVIRONMENT_VERSION_UKUI3:
    case DesktopEnvironmentVersion::DESKTOP_ENVIRONMENT_VERSION_UKUI4: {
        m_pStrategy = std::make_shared<Ukui5ConcreteStrategy>();
        break;
    }
    case DesktopEnvironmentVersion::DESKTOP_ENVIRONMENT_VERSION_UKUI5: {
        m_pStrategy = std::make_shared<Ukui5ConcreteStrategy>();
        break;
    }
    default:
        m_pStrategy = std::make_shared<Ukui5ConcreteStrategy>();
        break;
    }

    if (m_pStrategy) {
        m_pMainWid = m_pStrategy->createMainWindow(m_titleLabelItemMap, m_switchBtnItemMap, m_volumeSliderItemMap, m_selectComboxItemMap,
                                                   m_detailSettingsItemMap, this);
        m_pMainWid->initUi();
        m_pMainWid->initSlots();
    }
    else {
        QMessageBox::critical(this, tr("Audio"),
                              tr("Unable to obtain system version information, please check the system version!"),
                              QMessageBox::Ok);
        return;
    }

    QHBoxLayout* hLayout = new QHBoxLayout();
    hLayout->addWidget(m_pMainWid->getWidget());
    setLayout(hLayout);
    hLayout->setContentsMargins(0, 0, 0, 0);
}

void MainWidget::initSystemDeviceList(const NodeType& type)
{
    auto deviceList = getAvailablePortList(type);
    auto list = m_pMainWid->getDevicePortList(EnumToInt(type));

    int direction = (type == NodeType::OUTPUT) ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
    auto dev = std::find_if(deviceList.begin(), deviceList.end(), [direction](const ClientDeviceInfo& info) {
        return direction == info.direction && info.enabled;
    });

    if (0 == deviceList.size() || dev == deviceList.end()) {
        ClientMethod::getInstance().clear(EnumToInt(type));
        ClientMethod::getInstance().addItem(EnumToInt(type), tr("None"), QVariant(std::initializer_list<QString> {"none", "none"}));

        // 31268 系统未识别到声卡输出设备时，音量调节置灰不可调
        ClientMethod::getInstance().setValue(EnumToInt(type), 0);
        ClientMethod::getInstance().setEnabled(EnumToInt(type), false);
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(type), 0);

        if (NodeType::INPUT == type)
            m_pMainWid->updateUIByDevice(EnumToInt(NodeType::INPUT), false);

        return;
    }

    // 添加组合音频输出设备
    bool isExitMultiAudioDevice = false;
    if (NodeType::OUTPUT == type) {
        auto status = getMultiAudioCombineStatus(type);
        if (status) {
            auto sinkList = getSinkList();
            auto it = std::find_if(sinkList.begin(), sinkList.end(), [](const ClientDefaultDeviceInfo& info) {
                return info.name.contains("combine");
            });

            if (it != sinkList.end()) {
                isExitMultiAudioDevice = true;
                qDebug() << "Add Multiple Bluetooth outputs, (*it).activePortName " << (*it).activePortName <<
                          "(*it).cardName" << (*it).cardName;
                auto at = std::find_if(list.begin(), list.end(), [](const DevicePortInfo& info) {
                    return info.cardName == "combine-device";
                });
                if (at == list.end()) {
                    ClientMethod::getInstance().addItem(EnumToInt(NodeType::OUTPUT), tr("Multi Bluetooth Output"),
                                                        QVariant(std::initializer_list<QString> {(*it).cardName, (*it).activePortName}));
                }
            }
        }
    }

    // 如果有可用的设备端口，查找界面中是否存在无端口，存在则删除
    auto it = std::find_if(list.begin(), list.end(), [](const DevicePortInfo& info) {
        return "none" == info.cardName && "none" == info.portName;
    });
    if (it != list.end()) {
        // 31268 系统未识别到声卡输出设备时，音量调节置灰不可调
        ClientMethod::getInstance().setEnabled(EnumToInt(type), true);
        ClientMethod::getInstance().removeItem(EnumToInt(type), (*it).index);
    }

    for (const auto& dev : deviceList) {
        // 禁用的设备不显示在设备列表里面
        if (!dev.enabled)
            continue;

        auto it = std::find_if(list.begin(), list.end(), [dev](const DevicePortInfo& info) {
            return dev.cardName == info.cardName && dev.portName == info.portName;
        });

        // 没找到则添加
        if (it == list.end())
            ClientMethod::getInstance().addItem(EnumToInt(type), QString(dev.portLabel + " (" + dev.cardDesc + ")"),
                                                QVariant(std::initializer_list<QString> {dev.cardName, dev.portName}));
    }

    // 更新list
    list = m_pMainWid->getDevicePortList(EnumToInt(type));

    for (auto it = list.rbegin(); it != list.rend(); ++it) {
        auto deviceIt = std::find_if(deviceList.begin(), deviceList.end(), [it, isExitMultiAudioDevice](const ClientDeviceInfo& info) {
            if (isExitMultiAudioDevice && "combine-device" == (*it).cardName)
                return true;
            else
                return info.enabled && (*it).cardName == info.cardName && (*it).portName == info.portName;
        });

        // 没找到或者设备被禁用则移除
        if (deviceIt == deviceList.end()) {
            qDebug() << QString("can not found device, remove card %1 port %2").arg((*it).cardName).arg((*it).portName);
            ClientMethod::getInstance().removeItem(EnumToInt(type), (*it).index);
        }
    }

    initCurrentDefaultDevice(type);
}

void MainWidget::initCurrentDefaultDevice(const NodeType& type)
{
    auto devInfo = getDefaultDevice(EnumToInt(type), -1);

    auto idx = ClientMethod::getInstance().findData(EnumToInt(type), QVariant(std::initializer_list<QString> {devInfo.cardName, devInfo.activePortName}));

    if (idx == -1) {
        qDebug() << QString("Can not found card: %1 port: %2 in %3 type combox.").arg(devInfo.cardName).arg(devInfo.activePortName).arg(EnumToInt(type));
    }

    qDebug() << QString("Set current default device, find card: %1 port: %2 on index: %3").arg(devInfo.cardName).arg(devInfo.activePortName).arg(idx);
    ClientMethod::getInstance().setCurrentIndex(EnumToInt(type), idx);

    if (NodeType::INPUT == type && !devInfo.name.contains(".monitor")) {
        m_pMainWid->startMonitor(devInfo.idx);
    }

    initSystemVolumeSlider();

    // When default output is bluetooth device, need to hide balance and mono frame
    if (NodeType::OUTPUT == type && devInfo.cardName.contains("blue")) {
        m_pMainWid->updateUIByDevice(EnumToInt(type), false);
    }
    else if (NodeType::INPUT == type && (devInfo.name.contains("auto_null") || devInfo.activePortName.contains("internal") || devInfo.activePortDesc.contains("Digital"))) {
        m_pMainWid->updateUIByDevice(EnumToInt(type), false);
    }
}

void MainWidget::initDeviceManagerList(const NodeType& type)
{
    auto deviceList = getAvailablePortList(type);
    auto list = m_pMainWid->getDeviceManagetPortList(EnumToInt(type));

    for (const auto& dev : deviceList) {

        auto it = std::find_if(list.begin(), list.end(), [dev](const DevicePortInfo info) {
            return dev.cardName == info.cardName && dev.portName == info.portName;
        });
        // 没找到则添加
        if (it == list.end()) {
            m_pMainWid->addDeviceManagerItem(EnumToInt(type), QString(dev.portLabel + " (" + dev.cardDesc + ")"),
                                             QVariant(std::initializer_list<QString> {dev.cardName, dev.portName}));
            auto enabled = isEnabled(dev.cardName, dev.portName);
            list = m_pMainWid->getDeviceManagetPortList(EnumToInt(type));
            qDebug() << QString("Set card: %1 port: %2 enable: %3.").arg(dev.cardName).arg(dev.portName).arg(enabled);
            m_pMainWid->setDeviceManagerItemStatus(EnumToInt(type), list.size() - 1, enabled);
        }
    }

    for (auto it = list.rbegin(); it != list.rend(); ++it) {
        auto deviceIt = std::find_if(deviceList.begin(), deviceList.end(),
                                     [it](const ClientDeviceInfo& info) {
                                         return (*it).cardName == info.cardName && (*it).portName == info.portName;
                                     });

        // 没找到则移除
        if (deviceIt == deviceList.end()) {
            qDebug() << QString("can not found device in device manager widget, remove card %1 port %2").arg((*it).cardName).arg((*it).portName);
            m_pMainWid->removeDeviceManagerItem(EnumToInt(type), (*it).index);
        }
    }
}

void MainWidget::updateAppDeviceList(QList<ClientDeviceInfo>& list1, const QList<ClientDefaultDeviceInfo>& list2)
{
    for (auto it = list1.begin(); it != list1.end(); ) {
        auto matchIt = std::find_if(list2.begin(), list2.end(), [it](const ClientDefaultDeviceInfo& info) {
            return info.activePortName == it->portName;
        });

        if (matchIt == list2.end()) {
            it = list1.erase(it);
        } else {
            ++it;
        }
    }
}

void MainWidget::initAppManagerList()
{
    auto siList = getSinkInputList();

    auto outputDevList = getAvailablePortList(NodeType::OUTPUT);
    auto inputDevList = getAvailablePortList(NodeType::INPUT);

    for (const auto& [k, v] : m_appKeys) {
        auto volume = getVolume(EnumToInt(NodeType::OUTPUT), -1);
        auto mute = getMute(EnumToInt(NodeType::OUTPUT), -1);
        m_pMainWid->addAppManagerItem(MAX_INDEX, volume, mute, v.at(0), v.at(1), outputDevList, inputDevList);
        m_pMainWid->setCurrentAppItem(0);
        initAppCurrentDefaultDevice(MAX_INDEX, NodeType::OUTPUT);
        initAppCurrentDefaultDevice(MAX_INDEX, NodeType::INPUT);
    }

    for (const auto& si : siList) {
        if ((si.role.compare("filter") == 0) ||
            (si.role.compare("abstract") == 0))
            continue;

        auto sinkList = getSinkList();
        auto sourceList = getSourceList();

        updateAppDeviceList(outputDevList, sinkList);
        updateAppDeviceList(inputDevList, sourceList);

        auto mute = getMute(EnumToInt(NodeType::SINK_INPUT), si.index);
        m_pMainWid->addAppManagerItem(si.index, si.volume, mute, si.iconName, si.name, outputDevList, inputDevList);
        initAppCurrentDefaultDevice(si.index, NodeType::OUTPUT);
        initAppCurrentDefaultDevice(si.index, NodeType::INPUT);
    }
}

void MainWidget::initAppCurrentDefaultDevice(int idx, const NodeType& type)
{
    ClientDefaultDeviceInfo devInfo;

    auto appItemList = m_pMainWid->getAppItemList();
    if (appItemList.empty()) {
       return;
    }

    if (idx == -1) {
        devInfo = getDefaultDevice(EnumToInt(type), idx);
    }
    else {
        devInfo = getDefaultDevice(EnumToInt(type == NodeType::OUTPUT ? NodeType::SINK_INPUT : NodeType::SOURCE_OUTPUT), idx);
    }

    auto it = appItemList.find(idx);
    if (it != appItemList.end()) {
        auto cbx = it->second->getSelectComboxItems()[type];
        auto index = cbx->findData(QVariant(std::initializer_list<QString> {devInfo.cardName, devInfo.activePortName}));
        m_pMainWid->setAppCurrentIndex(idx, type, index);
    }
    else {
        qDebug() << QString("Invaild %1 index in app manager, set current index failed.").arg(idx);
    }
}

void MainWidget::initSwitchButtonStatus()
{
    for (const auto& it : m_switchBtnItemMap) {

        QString method;
        switch (it.first) {
        case SwitchButtonType::SWITCH_BUTTON_VOLUMEBOOST: {
            method = "getVolumeBoostStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_MONO: {
            method = "getMonoStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_ECHOCANCEL: {
            method = "getEchoCancelStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_LOOPBACK: {
            method = "getLoopbackStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_STARTUP: {
            method = "getStartupStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_POWEROFF: {
            method = "getPoweroffStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_LOGOUT: {
            method = "getLogoutStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_WAKEUP: {
            method = "getWakeupStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_ALERT: {
            method = "getAlertStatus";
            break;
        }
        case SwitchButtonType::SWITCH_BUTTON_AUTO_PAUSE: {
            ClientMethod::getInstance().setChecked(EnumToInt(it.first), getAutoPauseStatus());
            break;
        }
        default:
            break;
        }

        if ("" != method) {
            auto status = getValue(method).toBool();
            ClientMethod::getInstance().setChecked(EnumToInt(it.first), status);
            m_pMainWid->updateUIByButtonStatus(EnumToInt(it.first), status);

            if (status && SwitchButtonType::SWITCH_BUTTON_VOLUMEBOOST == it.first) {
                auto val = getValue("getVolumeBoostValue").toInt();
                ClientMethod::getInstance().setRange(EnumToInt(VolumeSliderType::VOLUME_SLIDER_OUTPUT), 0, val);
            }

            else if (status && SwitchButtonType::SWITCH_BUTTON_MONO == it.first) {
                ClientMethod::getInstance().setBalanceEnabled(false);
            }
        }
    }
}

void MainWidget::initSoundThemeList()
{
    auto list = getSoundThemeList();

    for (const auto& it : list) {
        ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT), it.description, QVariant(it.name));
    }
    // 添加自定义音效
    ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT), tr("Custom"), QVariant("custom"));

    updateSoundTheme();
}

/**
void MainWidget::initAppVolumeList()
{
    QDBusMessage replyList = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "getSinkInputList");

    if (replyList.arguments().isEmpty()) {
        qDebug() << "arguments is nullptr...";
        return;
    }

    const QDBusArgument& siArgs = replyList.arguments().at(0).value<QDBusArgument>();

    QList<StreamInfo> streamList;
    siArgs.beginArray();
    while (!siArgs.atEnd()) {
        StreamInfo info;
        siArgs >> info;
        streamList.push_back(info);
    }
    siArgs.endArray();


    replyList = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                         AUDIO_OBJECT_PATH,
                                                         AUDIO_INTERFACE,
                                                         "getSourceOutputList");

    if (replyList.arguments().isEmpty()) {
        qDebug() << "arguments is nullptr...";
        return;
    }

    const QDBusArgument& soArgs = replyList.arguments().at(0).value<QDBusArgument>();
    soArgs.beginArray();
    while (!siArgs.atEnd()) {
        StreamInfo info;
        soArgs >> info;
        streamList.push_back(info);
    }
    soArgs.endArray();

    for (const auto& s : streamList) {

        if ("filter" != s.role) {
            TrayClientMethod::getInstance().addAppItem(s.index, s.iconName, s.name);
        }
    }
}
*/

void MainWidget::initSystemVolumeSlider()
{
    for (const auto& [k, v] : m_volumeSliderItemMap) {
        switch (k) {
        case VolumeSliderType::VOLUME_SLIDER_OUTPUT:
        case VolumeSliderType::VOLUME_SLIDER_INPUT: {
            auto muted = getMute(EnumToInt(k), -1);
            auto volume = getVolume(EnumToInt(k), -1);
            ClientMethod::getInstance().setValue(EnumToInt(k), volume);
            ClientMethod::getInstance().setMute(EnumToInt(k), muted);
            break;
        }
        case VolumeSliderType::VOLUME_SLIDER_BALANCE: {
            double v = getBalance(EnumToInt(NodeType::OUTPUT));
            auto balanceValue = v * 100;
            ClientMethod::getInstance().setValue(EnumToInt(k), balanceValue);
            break;
        }
        default:
            break;
        }
    }
}

/**
void MainWidget::initAppVolumeSlider()
{
    for (const auto& [k, v] : m_appVolumeSliderItemMap) {
        QDBusReply<int> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                         AUDIO_OBJECT_PATH,
                                                                         AUDIO_INTERFACE,
                                                                         "getVolume",
                                                                         EnumToInt(NodeType::SINK_INPUT), "");
        TrayClientMethod::getInstance().setValue(EnumToInt(NodeType::SINK_INPUT), k, reply);
    }

}
*/

void MainWidget::initData()
{
    initSystemDeviceList(NodeType::OUTPUT);
    initSystemDeviceList(NodeType::INPUT);
    initSwitchButtonStatus();
    initSoundThemeList();
    initSystemVolumeSlider();
//    initAppVolumeSlider();
}

void MainWidget::initSlots()
{
    if (m_pMainWid) {
        m_pMainWid->addVolumeObserver([this](int type, int idx, int value) {
            // 如果是应用音量的系统对应的滑动条的值发生更改需要设置默认输出设备音量
            if (type == 2 && idx == -1) {
                setMute(1, idx, false);
                setVolume(1, idx, value);
            }
            else {
                setMute(type, idx, false);
                setVolume(type, idx, value);
            }

            qDebug() << QString("Set %1 type volume, index: %2 volume changed to %3").arg(type).arg(idx).arg(value);
            // 暂时不记录应用管理的埋点数据
            if (type > 1)
                return;

            auto slider = m_volumeSliderItemMap[IntToEnum<VolumeSliderType>(type)]->getSlider()->getWidget();
            ukcc::UkccCommon::buriedSettings("Audio", slider->objectName(), QString("slider"), QString::number(value));
        });

        m_pMainWid->addBalanceVolumeObserver([this](int type, float value) {
            setBalance(1, value);

            qDebug() << QString("Set %1 type balance, balance changed to %2.").arg(type).arg(value);

            // 暂时不记录应用管理的埋点数据
            if (type > 1)
                return;

            auto slider = m_volumeSliderItemMap[IntToEnum<VolumeSliderType>(type)]->getSlider()->getWidget();
            ukcc::UkccCommon::buriedSettings("Audio", slider->objectName(), QString("slider"), QString::number(value));
        });

        m_pMainWid->addMuteObserver([this](int type, int idx) {
            // 如果是应用音量的系统对应的静音值发生更改需要设置默认输出设备静音状态
            bool mute;
            if (type == 2 && idx == -1) {
                mute = getMute(1, idx);
                setMute(1, idx, !mute);
            }
            else {
                mute = getMute(type, idx);
                setMute(type, idx, !mute);
            }

            qDebug() << QString("Set %1 type mute, index: %2 mute changed to %3").arg(type).arg(idx).arg(mute);

            // 暂时不记录应用管理的埋点数据
            if (type > 1)
                return;

//            m_volumeSliderItemMap[IntToEnum<VolumeSliderType>(type)]->setMute(!muteStatus);
            auto muteBtn = m_volumeSliderItemMap[IntToEnum<VolumeSliderType>(type)]->getMuteBtn();
            ukcc::UkccCommon::buriedSettings("Audio", muteBtn->objectName(), QString("settings"),
                                             !mute ? "true" : "false");
        });

        m_pMainWid->addDeviceObserver([this](int type, int idx, const QString& portName, const QString& cardName) {
            // 如果是应用音量的系统对应的静音值发生更改需要设置默认输出设备静音状态
            if (type == 2 && idx == -1) {
                setDefaultDevice(1, idx, portName, cardName);
            }
            else {
                setDefaultDevice(type, idx, portName, cardName);
            }

            qDebug() << QString("Set %1 type default device, index: %2 default device changed to card: %3 port %4").arg(type).arg(idx).arg(cardName).arg(portName);
            // 暂时不记录应用管理的埋点数据
            if (type > 1)
                return;

            auto selectCbx = static_cast<QListWidget*>(m_selectComboxItemMap[IntToEnum<SelectComboxType>(type)]->getSelectBox()->getWidget());
            ukcc::UkccCommon::buriedSettings("Audio", selectCbx->objectName(), "select", selectCbx->currentItem()->text());
        });

        m_pMainWid->addSettingsObserver([this](const QString& key, const QVariant& value) {
            setValue(key, value);

            if (0 == strcmp(key.toLatin1().data(), "setSoundThemeName")) {
                updateSoundTheme();
            }
            else if (0 == strcmp(key.toLatin1().data(), "setAutoPauseStatus")) {
                setAutoPauseStatus(value.toBool());
            }
            qDebug() << QString("%1 setting key changed to %2").arg(key).arg(value.toString());
        });

        m_pMainWid->addDetailSettingsObserver([this](int type) {
            switch (IntToEnum<DetailSettingType>(type)) {
            case DetailSettingType::DETAIL_SETTING_DEVMANAGER: {
                initDeviceManagerList(NodeType::INPUT);
                initDeviceManagerList(NodeType::OUTPUT);
                break;
            }
            case DetailSettingType::DETAIL_SETTING_APPMANAGER: {
                initAppManagerList();
                break;
            }
            default:
                break;
            }

            qDebug() << QString("Click %1 detail settings.").arg(type);
        });

        m_pMainWid->addEnableDeviceObserver([this](const QString& card, const QString& port, bool enable) {
            setEnabled(card, port, enable);
            qDebug() << QString("Set card: %1 port: %2 enabled to %3").arg(card).arg(port).arg(enable ? "enable" : "disable");
        });
    }

    connect(&DBusClient::getInstance(), SIGNAL(volumeChangedSignal(int, int, const QDBusVariant&)), this, SLOT(volumeChangedSlots(int, int, const QDBusVariant&)));
    connect(&DBusClient::getInstance(), SIGNAL(muteChangedSignal(int, int, bool)), this, SLOT(muteChangedSlots(int, int, bool)));
    connect(&DBusClient::getInstance(), SIGNAL(deviceChangedSignal(int, const QString&, const QString&)), this, SLOT(deviceChangedSlots(int, const QString&, const QString&)));
    connect(&DBusClient::getInstance(), SIGNAL(deviceAdjustSignal(int)), this, SLOT(deviceAdjustSlots(int)));
    connect(&DBusClient::getInstance(), SIGNAL(settingsChangedSignal(const QString&, const QDBusVariant&)), this, SLOT(settingsChangedSlots(const QString&, const QDBusVariant&)));
    connect(&DBusClient::getInstance(), SIGNAL(addStreamSignal(int, int, const QString&, const QString&)), this, SLOT(addStreamSlots(int, int, const QString&, const QString&)));
    connect(&DBusClient::getInstance(), SIGNAL(removeStreamSignal(int)), this, SLOT(removeStreamSlots(int)));
}

void MainWidget::initClientManager()
{
    m_pClientManager = std::make_shared<ClientManager>(m_switchBtnItemMap, m_volumeSliderItemMap, m_selectComboxItemMap);
    DBusClient::getInstance().initDbusConnect();
    ClientMethod::getInstance().setManager(m_pClientManager);
}

void MainWidget::volumeChangedSlots(int type, int idx, const QDBusVariant& v)
{
    // 输入输出设备音量更改
    if (type <= 1) {
        ClientMethod::getInstance().setValue(type, v.variant().toInt());

        // 同步输出声道平衡音量
        if (type == 1) {
            m_pMainWid->setAppValue(-1, v.variant().toInt());

            double v = getBalance(type);
            auto balanceValue = v * 100;
            ClientMethod::getInstance().setValue(EnumToInt(VolumeSliderType::VOLUME_SLIDER_BALANCE), balanceValue);
        }
    }
    else if (type == 2) {
        m_pMainWid->setAppValue(idx, v.variant().toInt());
    }

    qDebug() << QString("Type %1 index: %2 volume changed to %3.").arg(type).arg(idx).arg(v.variant().toInt());
}

void MainWidget::muteChangedSlots(int type, int idx, bool mute)
{
    // 输入输出设备音量更改
    if (type <= 1) {
        ClientMethod::getInstance().setMute(type, mute);
        m_pMainWid->setAppMute(-1, mute);
    }
    else if (type = 2) {
        m_pMainWid->setAppMute(idx, mute);
    }
    qDebug() << QString("Type %1 index %2  mute changed to %3.").arg(type).arg(idx).arg(mute);
}

void MainWidget::deviceChangedSlots(int type, const QString& portName, const QString& cardName)
{
    qDebug() << QString("%1 type device changed, card: %2 port %3").arg(type).arg(cardName).arg(portName);

    switch (IntToEnum<NodeType>(type)) {
    case NodeType::INPUT:
    case NodeType::OUTPUT: {
        int idx = ClientMethod::getInstance().findData(type, QVariant(std::initializer_list<QString>{cardName, portName}));
        auto volume = getVolume(type, -1);
        auto muted = getMute(type, -1);

        ClientMethod::getInstance().setCurrentIndex(type, idx);
        ClientMethod::getInstance().setValue(type, volume);
        ClientMethod::getInstance().setMute(type, muted);

        if (IntToEnum<NodeType>(type) == NodeType::OUTPUT) {
            double v = getBalance(type);
            int balanceValue = v * 100;
            ClientMethod::getInstance().setValue(EnumToInt(VolumeSliderType::VOLUME_SLIDER_BALANCE), balanceValue);
            m_pMainWid->setAppValue(-1, volume);

            // When default output is bluetooth device, need to hide balance and mono frame
            const bool isBluetoothDevice = cardName.contains("blue");
            m_pMainWid->updateUIByDevice(EnumToInt(NodeType::OUTPUT), !isBluetoothDevice);
        }
        else {
            auto devInfo = getDefaultDevice(EnumToInt(NodeType::INPUT), -1);
            if (devInfo.name.contains("auto_null")) {
                m_pMainWid->stopMonitor();
            }
            else {
                m_pMainWid->startMonitor(devInfo.idx);
            }

            const bool isNullorInternalDev =
                    devInfo.name.contains("auto_null") ||
                    devInfo.activePortName.contains("internal") ||
                    devInfo.activePortDesc.contains("Digital");


            // When default input is NULL or internal device, need to hide loopback frame
            m_pMainWid->updateUIByDevice(EnumToInt(NodeType::INPUT), !isNullorInternalDev);
        }

        initAppCurrentDefaultDevice(-1, IntToEnum<NodeType>(type));
        break;
    }
    case NodeType::SINK_INPUT:
    case NodeType::SOURCE_OUTPUT: {
        // TODO
        break;
    }
    default:
        break;
    }
}

void MainWidget::deviceAdjustSlots(int type)
{
    qDebug() << __func__ << "type: " << type;
    initSystemDeviceList(NodeType::INPUT);
    initSystemDeviceList(NodeType::OUTPUT);

    initDeviceManagerList(NodeType::INPUT);
    initDeviceManagerList(NodeType::OUTPUT);

    updateAppWidgetDeviceList(NodeType::OUTPUT);
    updateAppWidgetDeviceList(NodeType::INPUT);
}

void MainWidget::settingsChangedSlots(const QString& key, const QDBusVariant& v)
{
    qDebug() << "setting changed slot, key: " << key << " value:" << v.variant();
    if (EVENT_SOUND_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_ALERT), v.variant().toBool());
    }
    else if (THEME_NAME_KEY == key) {
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT),
                                                    ClientMethod::getInstance().findData(2, v.variant()));
    }
    else if (CUSTOM_THEME_KEY == key) {
        if (v.variant().toBool()) {
            ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT),
                                                        ClientMethod::getInstance().findData(2, QVariant("custom")));
        }
    }
    else if (VOLUME_CHANGED_KEY == key) {
        updateSoundTheme();
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST),
                                                    ClientMethod::getInstance().findData(4, v.variant()));
    }
    else if (NOTIFY_GENERAL_KEY == key) {
        updateSoundTheme();
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION),
                                                    ClientMethod::getInstance().findData(4, v.variant()));
    }
    else if (MONO_AUDIO_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_MONO), v.variant().toBool());
        DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                 AUDIO_OBJECT_PATH,
                                                 AUDIO_INTERFACE,
                                                 "LoadModule",
                                                 "module-mono", "");

        ClientMethod::getInstance().setBalanceEnabled(!v.variant().toBool());
    }
    else if (NOISE_REDUCTION_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_ECHOCANCEL), v.variant().toBool());
    }
    else if (LOOPBACK_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_LOOPBACK), v.variant().toBool());
    }
    else if (VOLUME_BOOST_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_VOLUMEBOOST), v.variant().toBool());

        int value = 100;
        if (v.variant().toBool()) {
            value = getValue("getVolumeBoostValue").toInt();
        }
        ClientMethod::getInstance().setRange(EnumToInt(VolumeSliderType::VOLUME_SLIDER_OUTPUT), 0, value);
    }
    else if (VOLUME_BOOST_VOLUME_KEY == key) {
        // TODO
    }
    else if (STARTUP_MUSIC_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_STARTUP), v.variant().toBool());
    }
    else if (ALERT_VOLUME_KEY == key) {
        ClientMethod::getInstance().setChecked(EnumToInt(SwitchButtonType::SWITCH_BUTTON_ALERT), v.variant().toBool());
    }
}

void MainWidget::addStreamSlots(int idx, int value, const QString& iconName, const QString& desc)
{
    auto outputDevList = getAvailablePortList(NodeType::OUTPUT);
    auto inputDevList = getAvailablePortList(NodeType::INPUT);
    auto mute = getMute(EnumToInt(NodeType::SINK_INPUT), idx);
    auto sinkList = getSinkList();
    auto sourceList = getSourceList();

    updateAppDeviceList(outputDevList, sinkList);
    updateAppDeviceList(inputDevList, sourceList);

    m_pMainWid->addAppManagerItem(idx, value, mute, iconName, desc, outputDevList, inputDevList);
    initAppCurrentDefaultDevice(idx, NodeType::OUTPUT);
    qDebug() << QString("Stream %1 added, stream name: %2, stream icon: %3.").arg(idx).arg(desc).arg(iconName);

}

void MainWidget::removeStreamSlots(int idx)
{
    qDebug() << QString("Stream %1 removed.").arg(idx);
    m_pMainWid->removeAppManagerItem(idx);
}

DesktopEnvironmentVersion MainWidget::getDesktopEnvironmentVersion()
{
    QDBusReply<int> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                     AUDIO_OBJECT_PATH,
                                                                     AUDIO_INTERFACE,
                                                                     "getDesktopEnvironmentVersion");

    return m_version = IntToEnum<DesktopEnvironmentVersion>(reply);
}

int MainWidget::getVolume(int type, int idx)
{
    QDBusReply<int> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                     AUDIO_OBJECT_PATH,
                                                                     AUDIO_INTERFACE,
                                                                     "getVolume",
                                                                     type, idx);

    return reply;
}

void MainWidget::setVolume(int type, int idx, int vaule)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setVolume",
                                             type, idx, vaule);
}

double MainWidget::getBalance(int type) const
{
    QDBusReply<double> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                        AUDIO_OBJECT_PATH,
                                                                        AUDIO_INTERFACE,
                                                                        "getBalance",
                                                                        type);

    return reply;
}

void MainWidget::setBalance(int type, double v)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setBalance",
                                             type, v);
}

bool MainWidget::getMute(int type, int idx)
{
    QDBusReply<bool> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "getMute",
                                                                      type, idx);

    return reply;
}

void MainWidget::setMute(int type, int idx, bool muted)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setMute",
                                             type, idx, muted);
}

void MainWidget::setEnabled(const QString& card, const QString& port, bool enable)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setEnabled",
                                             card, port, enable);
}

bool MainWidget::isEnabled(const QString& card, const QString& port) const
{
    QDBusReply<bool> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "isEnabled",
                                                                      card, port);

    return reply;
}

ClientDefaultDeviceInfo MainWidget::getDefaultDevice(int type, int idx)
{
    QDBusMessage replyList = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "getDefaultDevice",
                                                                      type, idx);

    if (replyList.arguments().isEmpty()) {
        qWarning() << "Get default device failed, arguments is nullptr.";
        return {};
    }

    qDebug() << "Get default device, deviceList: " << replyList;
    const QDBusArgument& siArgs = replyList.arguments().at(0).value<QDBusArgument>();
    ClientDefaultDeviceInfo info;
    siArgs >> info;
    return info;
}

QList<ClientDefaultDeviceInfo> MainWidget::getSinkList() const
{
    QList<ClientDefaultDeviceInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  AUDIO_INTERFACE,
                                                                  "getSinkList");

    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ClientDefaultDeviceInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}

QList<ClientDefaultDeviceInfo> MainWidget::getSourceList() const
{
    QList<ClientDefaultDeviceInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  AUDIO_INTERFACE,
                                                                  "getSourceList");

    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ClientDefaultDeviceInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}


void MainWidget::setDefaultDevice(int type, int idx, const QString& portName, const QString& cardName)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setDefaultDevice",
                                             type, idx, portName, cardName);
}

void MainWidget::setValue(const QString& method, const QVariant& value)
{
    qDebug() << "set " << method << " value:" << value;

    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             SETTINGS_INTERFACE,
                                             method,
                                             value);
}

QVariant MainWidget::getValue(const QString& method) const
{
    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  SETTINGS_INTERFACE,
                                                                  method);
    return reply.arguments().at(0);
}

QList<ClientSoundThemeInfo> MainWidget::getSoundThemeList()
{
    QList<ClientSoundThemeInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  SOUNDTHEME_INTERFACE,
                                                                  "getSoundThemeList");
    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ClientSoundThemeInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}

QList<ClientSoundEffectFileInfo> MainWidget::getSoundEffectFileList(const QString& name)
{
    QList<ClientSoundEffectFileInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  SOUNDTHEME_INTERFACE,
                                                                  "getSoundEffectFileList",
                                                                  name);
    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ClientSoundEffectFileInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}

QList<ClientDeviceInfo> MainWidget::getAvailablePortList(const NodeType& type)
{
    QList<ClientDeviceInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  AUDIO_INTERFACE,
                                                                  "getAvailablePortList",
                                                                  EnumToInt(type));

    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ClientDeviceInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}

QList<StreamInfo> MainWidget::getSinkInputList() const
{
    QList<StreamInfo> list;

    QDBusMessage reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                  AUDIO_OBJECT_PATH,
                                                                  AUDIO_INTERFACE,
                                                                  "getSinkInputList");

    if (reply.arguments().isEmpty()) {
        qDebug() << "返回参数为空";
        return {};
    }

    const QDBusArgument& dbusArgs = reply.arguments().at(0).value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        StreamInfo info;
        dbusArgs >> info;
        list.push_back(info);
    }
    dbusArgs.endArray();

    return list;
}

void MainWidget::updateSoundTheme()
{
    auto value = getValue("getSoundThemeName");
    qDebug() << "updateSoundTheme value :" << value.toString();

    auto fileList = getSoundEffectFileList(value.toString());
    qDebug() << "updateSoundTheme fileList :" << fileList.size();

    // 清空音量调节和通知音效中下拉框的内容
    ClientMethod::getInstance().clear(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST));
    ClientMethod::getInstance().clear(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION));

    if (fileList.isEmpty()) {
        ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST), tr("None"), QVariant(tr("None")));
        ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION), tr("None"), QVariant(tr("None")));
    } else {
        // init volumeChanged/notifygeneral combox
        for (const auto& f : fileList) {
            ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST), f.description, QVariant(f.name));
            ClientMethod::getInstance().addItem(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION), f.description, QVariant(f.name));
        }
    }

    auto idx = ClientMethod::getInstance().findData(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT), value);
    ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_SOUNDEFFECT), idx);

    if (fileList.isEmpty()) {
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST), 0);
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION), 0);
    } else {
        value = getValue("getVolumeChangedName");
        idx = ClientMethod::getInstance().findData(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST), value);
        qDebug() << "updateSoundTheme getVolumeChangedName idx:" << idx;
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_VOLUMEADJUST), idx);

        value = getValue("getNotifyGeneralName");
        idx = ClientMethod::getInstance().findData(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION), value);
        qDebug() << "updateSoundTheme getNotifyGeneralName idx:" << idx;
        ClientMethod::getInstance().setCurrentIndex(EnumToInt(SelectComboxType::SELECT_COMBOX_NOTIFICTION), idx);
    }
}

bool MainWidget::getAutoPauseStatus() const
{
    QDBusReply<bool> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "getAutoPauseStatus");

    return reply;
}

void MainWidget::setAutoPauseStatus(bool status)
{
    DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                             AUDIO_OBJECT_PATH,
                                             AUDIO_INTERFACE,
                                             "setAutoPauseStatus",
                                             status);
}

bool MainWidget::getMultiAudioCombineStatus(const NodeType& type) const
{
    QDBusReply<bool> reply = DBusClient::getInstance().dbusMethodCall(AUDIO_OBJECT_SERVICE,
                                                                      AUDIO_OBJECT_PATH,
                                                                      AUDIO_INTERFACE,
                                                                      "getMultiAudioCombineStatus",
                                                                      EnumToInt(type));

    return reply;
}

void MainWidget::updateAppWidgetDeviceList(const NodeType& type)
{
    auto deviceList = getAvailablePortList(type);
    auto appItemList = m_pMainWid->getAppItemList();

    if (appItemList.empty())
        return;

    for (const auto& item : appItemList) {
        auto portList = m_pMainWid->getAppManagetPortList(item.first, EnumToInt(type));
        for (const auto& dev : deviceList) {

            auto it = std::find_if(portList.begin(), portList.end(), [dev](const DevicePortInfo info) {
                return dev.cardName == info.cardName && dev.portName == info.portName;
            });
            // 没找到则添加
            if (it == portList.end()) {
                qDebug() << QString("Device adjust, add card: %1 port: %2 to %3.").arg(dev.cardName).arg(dev.portName).arg(item.first);
                m_pMainWid->addPortToAppItem(item.first, type, QString(dev.portLabel + " (" + dev.cardDesc + ")"),
                                             QVariant(std::initializer_list<QString> {dev.cardName, dev.portName}));
            }
        }

        for (auto it = portList.rbegin(); it != portList.rend(); ++it) {
            auto deviceIt = std::find_if(deviceList.begin(), deviceList.end(),
                                         [it](const ClientDeviceInfo& info) {
                                             return (*it).cardName == info.cardName && (*it).portName == info.portName;
                                         });

            // 没找到则移除
            if (deviceIt == deviceList.end()) {
                qDebug() << QString("can not found device in app manager widget, remove card %1 port %2").arg((*it).cardName).arg((*it).portName);
                m_pMainWid->removePortFromAppItem(item.first, type, (*it).index);
            }
        }
    }
}
