/*
 * Copyright (C) 2019 Tianjin KYLIN Information Technology 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 by
 * the Free Software Foundation; either version 3, or (at your option)
 * 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/&gt;.
 *
 */

#include "ukmedia_volume_control.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <QEventLoop>
#include <set>
#include <pulse/introspect.h>
#include <QIcon>
#include <QStyle>
#include <QLabel>
#include <QSettings>
#include <QDebug>
#include <QSlider>
#include <qjsondocument.h>
#include <qjsonobject.h>
#include <QSettings>
#include <alsa/asoundlib.h>
#include <QGSettings>

#define KEY_SOUNDS_SCHEMA   "org.ukui.sound"
#define MONO_AUDIO          "mono-audio"

//pa_sink_info *m_pDefaultSink;
/* Used for profile sorting */
struct profile_prio_compare {
    bool operator() (pa_card_profile_info2 const * const lhs, pa_card_profile_info2 const * const rhs) const {

        if (lhs->priority == rhs->priority)
            return strcmp(lhs->name, rhs->name) > 0;

        return lhs->priority > rhs->priority;
    }
};

struct sink_port_prio_compare {
    bool operator() (const pa_sink_port_info& lhs, const pa_sink_port_info& rhs) const {

        if (lhs.priority == rhs.priority)
            return strcmp(lhs.name, rhs.name) > 0;

        return lhs.priority > rhs.priority;
    }
};

struct source_port_prio_compare {
    bool operator() (const pa_source_port_info& lhs, const pa_source_port_info& rhs) const {

        if (lhs.priority == rhs.priority)
            return strcmp(lhs.name, rhs.name) > 0;

        return lhs.priority > rhs.priority;
    }
};

UkmediaVolumeControl::UkmediaVolumeControl():

    showSinkInputType(SINK_INPUT_CLIENT),
    showSinkType(SINK_ALL),
    showSourceOutputType(SOURCE_OUTPUT_CLIENT),
    showSourceType(SOURCE_NO_MONITOR),

    canRenameDevices(false),
    m_connected(false),
    m_config_filename(nullptr) {
    profileNameMap.clear();
    //创建声音初始化记录文件
    customSoundFile = new CustomSound();
    customSoundFile->createAudioFile();
    connectToPulse(this);

    if (reconnectTimeout >= 0) {

    }

    if (reconnectTimeout < 0)
        showError(QObject::tr("Fatal Error: Unable to connect to PulseAudio").toUtf8().constData());
}

/*
 *  设置输出设备静音
 */
void UkmediaVolumeControl::setSinkMute(bool status)
{
    pa_operation* o;

    if (!(o = pa_context_set_sink_mute_by_index(getContext(), sinkIndex, status, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
}

/*
 *  设置输出设备音量通过info
 */
void UkmediaVolumeControl::setSinkVolumeByInfo(const pa_sink_info &info,int value)
{
    pa_cvolume v = info.volume;
//    v.channels = channel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;
    if (balance != 0) {
        pa_cvolume_set_balance(&v,&channelMap,balance);
    }

    pa_operation* o;
    if (sinkMuted) {
        if (!(o = pa_context_set_sink_mute_by_index(getContext(), info.index,false, nullptr, nullptr))) {
            showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
        }
    }
    if (!(o = pa_context_set_sink_volume_by_index(getContext(), info.index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
    qDebug() << "set sink volume by info" << sinkIndex << "channel:" << v.channels << "balance:" << balance << info.volume.values;
}

void UkmediaVolumeControl::setSourceVolumeByInfo(const pa_source_info &info,int value)
{
    pa_cvolume v = info.volume;
//    v.channels = channel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;
    if (balance != 0) {
        pa_cvolume_set_balance(&v,&channelMap,balance);
    }

    pa_operation* o;
    if (sourceMuted) {
        if (!(o = pa_context_set_source_mute_by_index(getContext(), info.index,false, nullptr, nullptr))) {
            showError(tr("pa_context_set_source_mute_by_index() failed").toUtf8().constData());
        }
    }
    if (!(o = pa_context_set_source_volume_by_index(getContext(), info.index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_source_volume_by_index() failed").toUtf8().constData());
    }
    qDebug() << "set source volume by info" << sourceIndex << "channel:" << v.channels << "balance:" << balance << info.volume.values;
}


/*
 *  设置输出设备音量
 */
void UkmediaVolumeControl::setSinkVolume(int index,int value)
{
    if(m_pDefaultSink == nullptr){
        return;
    }

    pa_cvolume v = m_pDefaultSink->volume;
    v.channels = channel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;
    if (balance != 0) {
        pa_cvolume_set_balance(&v,&channelMap,balance);
    }

    pa_operation* o;
    if (sinkMuted) {
        if (!(o = pa_context_set_sink_mute_by_index(getContext(), index,false, nullptr, nullptr))) {
            showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
        }
    }
    if (!(o = pa_context_set_sink_volume_by_index(getContext(), index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
    qDebug() << "set sink volume" << sinkIndex << "channel:" << v.channels << "balance:" << balance;
}

/*
 *  设置输入设备静音
 */
void UkmediaVolumeControl::setSourceMute(bool status)
{
    pa_operation* o;

    if (!(o = pa_context_set_source_mute_by_index(getContext(), sourceIndex, status, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
}


/*
 * 设置输入设备音量
 */
void UkmediaVolumeControl::setSourceVolume(int index,int value)
{
    if(m_pDefaultSink == nullptr){
        return;
    }

    pa_cvolume v = m_pDefaultSink->volume;
    v.channels = inputChannel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;

    qDebug() << "set source volume" << sourceIndex << v.channels << value;
    pa_operation* o;
    if (!(o = pa_context_set_source_volume_by_index(getContext(), index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
}

/*
 * 获取输出设备的静音状态
 */
bool UkmediaVolumeControl::getSinkMute()
{
    return sinkMuted;
}

/*
 * 获取输出设备的音量值
 */
int UkmediaVolumeControl::getSinkVolume()
{
    return sinkVolume;
}

/*
 * 获取输入设备的静音状态
 */
bool UkmediaVolumeControl::getSourceMute()
{
    return sourceMuted;
}

/*
 * 获取输入设备的音量值
 */
int UkmediaVolumeControl::getSourceVolume()
{
    return sourceVolume;
}

int UkmediaVolumeControl::getDefaultSinkIndex()
{
    pa_operation *o;
    if (!(o = pa_context_get_server_info(getContext(), serverInfoIndexCb, this))) {
        showError(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
        return -1;
    }
    pa_operation_unref(o);

    return sinkIndex;
}

/*
 * 根据名称获取sink input静音状态
 */
bool UkmediaVolumeControl::getSinkInputMuted(QString description)
{
    int muteStatus = 1;

    QMap<QString,int>::iterator it;
    for (it=sinkInputMuteMap.begin();it!=sinkInputMuteMap.end();) {
        qDebug() << "getSinkInputMuted" << it.key() << description << it.value();
        if (it.key() == description) {
            muteStatus = it.value();
            break;
        }
        ++it;
    }

    return muteStatus==1?true:false;
}

/*
 * 根据模块名称获取对应模块索引
 */
int UkmediaVolumeControl::getModuleIndexByName(QString name)
{
    findModuleStr = name;
    pa_context_get_module_info_list(getContext(), moduleInfoCb,this);
}

/*
 *  滑动条更改设置sink input 音量值
 */
void UkmediaVolumeControl::setSinkInputVolume(int index, int value,int channel)
{
    pa_cvolume v = m_pDefaultSink->volume;

    v.channels = channel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;

    qDebug() << "set sink input volume" << index << v.channels << value;

    pa_operation* o;
    if (!(o = pa_context_set_sink_input_mute(getContext(), index,false, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }

    if (!(o = pa_context_set_sink_input_volume(getContext(), index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
}

/*
 *  滑动条更改设置sink input静音状态
 */
void UkmediaVolumeControl::setSinkInputMuted(int index, bool status)
{
    qDebug() << "set sink input muted" << index << status;

    pa_operation* o;
    if (!(o = pa_context_set_sink_input_mute(getContext(), index,status, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData());
    }
}

/*
 *  滑动条更改设置source output 音量值
 */
void UkmediaVolumeControl::setSourceOutputVolume(int index, int value, int channel)
{
    pa_cvolume v = m_pDefaultSink->volume;
    v.channels = channel;
    for (int i=0;i<v.channels;i++)
        v.values[i] = value;

    qDebug() << "set source output volume" << index << v.channels << value;

    pa_operation* o;
    if (!(o = pa_context_set_source_output_mute(getContext(), index,false, nullptr, nullptr))) {
        showError(tr("pa_context_set_source_output_volume() failed").toUtf8().constData());
    }

    if (!(o = pa_context_set_source_output_volume(getContext(), index, &v, nullptr, nullptr))) {
        showError(tr("pa_context_set_source_output_volume() failed").toUtf8().constData());
    }
}

/*
 *  滑动条更改设置source output静音状态
 */
void UkmediaVolumeControl::setSourceOutputMuted(int index, bool status)
{
    qDebug() << "set source output muted" << index << status;

    pa_operation* o;
    if (!(o = pa_context_set_source_output_mute(getContext(), index,status, nullptr, nullptr))) {
        showError(tr("pa_context_set_source_output_mute() failed").toUtf8().constData());
    }
}

/*
 * 设置声卡的配置文件
 */
bool UkmediaVolumeControl::setCardProfile(int index, const gchar *name)
{
    qDebug() << "setCardProfile" << index << name;

    pa_operation* o;
    if (!(o = pa_context_set_card_profile_by_index(getContext(), index, name, nullptr, nullptr))) {
        showError(tr("pa_context_set_card_profile_by_index() failed").toUtf8().constData());
        return false;
    }

    return true;
}

/*
 * 设置默认的输出设备
 */
bool UkmediaVolumeControl::setDefaultSink(const gchar *name)
{
    bool monoAudioState = false;

    QGSettings *m_pSoundSettings = new QGSettings(KEY_SOUNDS_SCHEMA);
    monoAudioState = m_pSoundSettings->get(MONO_AUDIO).toBool();

    pa_operation* o;
    if (!(o = pa_context_set_default_sink(getContext(), name, nullptr, nullptr))) {
        showError(tr("pa_context_set_default_sink() failed").toUtf8().constData());
        return false;
    }
    if(monoAudioState)
    {
        if (!(o = pa_context_set_default_sink(getContext(), "mono", nullptr, nullptr))) {
            showError(tr("pa_context_set_default_sink() failed").toUtf8().constData());
            return false;
        }
    }
    qDebug() << " setDefaultSink  " << name << defaultSinkName << monoAudioState;
    return true;
}

/*
 * 设置默认的输入设备
 */
bool UkmediaVolumeControl::setDefaultSource(const gchar *name)
{
    qDebug() << "setDefaultSource" << name;

    pa_operation* o;
    if (!(o = pa_context_set_default_source(getContext(), name, nullptr, nullptr))) {
        showError(tr("pa_context_set_default_source() failed").toUtf8().constData());
        return false;
    }

    return true;
}

/*
 * 设置输出设备的端口
 */
bool UkmediaVolumeControl::setSinkPort(const gchar *sinkName, const gchar *portName)
{
    qDebug() << "setSinkPort" << "sinkname:" << sinkName << "portname:" << portName;

    pa_operation* o;
    if (!(o = pa_context_set_sink_port_by_name(getContext(), sinkName, portName, nullptr, nullptr))) {
        showError(tr("pa_context_set_sink_port_by_name() failed").toUtf8().constData());
        return false;
    }

    return true;
}

/*
 * 设置输入设备端口
 */
bool UkmediaVolumeControl::setSourcePort(const gchar *sourceName, const gchar *portName)
{
    qDebug() << "setSourcePort" << sourceName << portName;
    pa_operation* o;
    if (!(o = pa_context_set_source_port_by_name(getContext(), sourceName, portName, nullptr, nullptr))) {
        showError(tr("pa_context_set_source_port_by_name() failed").toUtf8().constData());
        return false;
    }
    return true;
}

/*
 * kill 索引为index 的sink input
 */
bool UkmediaVolumeControl::killSinkInput(int index)
{
    pa_operation* o;
    if (!(o = pa_context_kill_sink_input(getContext(), index, nullptr, nullptr))) {
        showError(tr("pa_context_kill_sink_input() failed").toUtf8().constData());
        return false;
    }
    return true;
}

UkmediaVolumeControl::~UkmediaVolumeControl() {

    while (!clientNames.empty()) {
        auto i = clientNames.begin();
        g_free(i->second);
        clientNames.erase(i);
    }
}

static void updatePorts(UkmediaVolumeControl *d, std::map<QByteArray, PortInfo> &ports) {
    std::map<QByteArray, PortInfo>::iterator it;
    PortInfo p;
    for (auto & port : d->dPorts) {
        QByteArray desc;
        it = ports.find(port.first);

        if (it == ports.end())
            continue;

        p = it->second;
        desc = p.description;

        if (p.available == PA_PORT_AVAILABLE_YES)
            desc +=  UkmediaVolumeControl::tr(" (plugged in)").toUtf8().constData();
        else if (p.available == PA_PORT_AVAILABLE_NO) {
            if (p.name == "analog-output-speaker" ||
                p.name == "analog-input-microphone-internal")
                desc += UkmediaVolumeControl::tr(" (unavailable)").toUtf8().constData();
            else
                desc += UkmediaVolumeControl::tr(" (unplugged)").toUtf8().constData();
        }
        port.second = desc;
    }
    qDebug() << "updatePorts" << p.name << p.description;

    Q_EMIT d->updatePortSignal();

    it = ports.find(d->activePort);

    if (it != ports.end()) {
        p = it->second;
//        d->setLatencyOffset(p.latency_offset);
    }
}

static void setIconByName(QLabel* label, const char* name) {
    QIcon icon = QIcon::fromTheme(name);
    int size = label->style()->pixelMetric(QStyle::PM_ToolBarIconSize);
    QPixmap pix = icon.pixmap(size, size);
    label->setPixmap(pix);
}

int UkmediaVolumeControl::boolToInt(bool b)
{
    if (b == true)
        return 1;
    else
        return 0;
}

void UkmediaVolumeControl::updateCard(UkmediaVolumeControl *c, const pa_card_info &info) {
    const char *description;
    QMap<QString,QString> tempInput;
    QMap<QString,QString> tempOutput;
    QList<QString> profileName;
    QMap<QString,QString>portMap;
    QMap<QString,QString> outputPortNameLabelMap;
    QMap<QString,QString>inputPortNameLabelMap;
    QMap<QString,int> profilePriorityMap;
    std::set<pa_card_profile_info2 *, profile_prio_compare> profile_priorities;

    description = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_DESCRIPTION);//获取device.description字符串

    hasSinks = c->hasSources = false;
    profile_priorities.clear();
    for (pa_card_profile_info2 ** p_profile = info.profiles2; *p_profile != nullptr; ++p_profile) {
//        c->hasSinks = c->hasSinks || ((*p_profile)->n_sinks > 0);
//        c->hasSources = c->hasSources || ((*p_profile)->n_sources > 0);
        profile_priorities.insert(*p_profile);
        profileName.append((*p_profile)->name);
        profilePriorityMap.insertMulti((*p_profile)->name,(*p_profile)->priority);
    }
    QList<QMap<QString,int>> cardProfilePriorityMapList;
    if(cardProfilePriorityMap.isEmpty())
        cardProfilePriorityMap.insertMulti(info.index,profilePriorityMap);

    cardProfilePriorityMapList = cardProfilePriorityMap.values();
    if(!cardProfilePriorityMapList.contains(profilePriorityMap))
        cardProfilePriorityMap.insertMulti(info.index,profilePriorityMap);

    c->ports.clear();
    for (uint32_t i = 0; i < info.n_ports; ++i) {
        PortInfo p;
        p.name = info.ports[i]->name;
        p.description = info.ports[i]->description;
        p.priority = info.ports[i]->priority;
        p.available = info.ports[i]->available;
        p.direction = info.ports[i]->direction;
        p.latency_offset = info.ports[i]->latency_offset;
        if (info.ports[i]->profiles2)//bug#92426 该蓝牙音响存在输入端口无输入配置文件会导致段错误
            for (pa_card_profile_info2 ** p_profile = info.ports[i]->profiles2; *p_profile != nullptr; ++p_profile) {
                p.profiles.push_back((*p_profile)->name);
            }
        if (p.direction == 1 && p.available != PA_PORT_AVAILABLE_NO) {
//            portMap.insertMulti(p.name,p.description.data());

            //新增UI设计，combobox显示portname+description
            QString outputPortName = p.description.data();//端口名(如：扬声器,模拟耳机..)
            QString outputPortName_and_description = outputPortName + "（" + description + "）";

            tempOutput.insertMulti(p.name,outputPortName_and_description);

            QList<QString> portProfileName;
            for (auto p_profile : p.profiles) {
                portProfileName.append(p_profile.data());
                outputPortNameLabelMap.insertMulti(p.description.data(),p_profile.data());
                qDebug() << "ctf profilename map insert -----------" << p.description.data() << p_profile.data();
            }
            profileNameMap.insert(info.index,outputPortNameLabelMap);
            cardProfileMap.insert(info.index,portProfileName);
        }
        else if (p.direction == 2 && p.available != PA_PORT_AVAILABLE_NO){

            qDebug() << " add source port name "<< info.index << p.name << p.description.data();
            QString inputPortName = p.description.data();//端口名(如：扬声器,模拟耳机..)
            QString inputPortName_and_description = inputPortName + "（" + description + "）";

            tempInput.insertMulti(p.name,inputPortName_and_description);

            for (auto p_profile : p.profiles) {
                inputPortNameLabelMap.insertMulti(p.description.data(),p_profile.data());
            }
            inputPortProfileNameMap.insert(info.index,inputPortNameLabelMap);
        }
        c->ports[p.name] = p;
    }
    inputPortMap.insert(info.index,tempInput);
    outputPortMap.insert(info.index,tempOutput);
    cardActiveProfileMap.insert(info.index,info.active_profile->name);
    c->profiles.clear();
    for (auto p_profile : profile_priorities) {
        bool hasNo = false, hasOther = false;
        std::map<QByteArray, PortInfo>::iterator portIt;
        QByteArray desc = p_profile->description;

        for (portIt = c->ports.begin(); portIt != c->ports.end(); portIt++) {
            PortInfo port = portIt->second;

            if (std::find(port.profiles.begin(), port.profiles.end(), p_profile->name) == port.profiles.end())
                continue;

            if (port.available == PA_PORT_AVAILABLE_NO)
                hasNo = true;
            else {
                hasOther = true;
                break;
            }
        }
        if (hasNo && !hasOther)
            desc += tr(" (unplugged)").toUtf8().constData();

        if (!p_profile->available)
            desc += tr(" (unavailable)").toUtf8().constData();

        c->profiles.push_back(std::pair<QByteArray,QByteArray>(p_profile->name, desc));
        if (p_profile->n_sinks == 0 && p_profile->n_sources == 0)
            c->noInOutProfile = p_profile->name;
    }
    c->activeProfile = info.active_profile ? info.active_profile->name : "";

    //bug 102001 针对hw990 9a0 panguv panguw，由于连上部分音箱后，创建蓝牙声卡时a2d_sink对应transport状态还是不可用的状态，因此进行延迟300ms的异常处理
    QString sessionType = getenv("XDG_SESSION_TYPE");
    if (sessionType.compare("wayland", Qt::CaseSensitive)) {
        if (strstr(info.name,"bluez_card") && strcmp(info.active_profile->name,"off") == 0) {
                qDebug() << "bluez card profile is off,need to set profile...";
                QTimer::singleShot(300,this,[=](){
                    setCardProfile(info.index,"a2dp_sink");
                });
            }
    }

    if (strstr(info.name, "bluez_card")) {
        getBatteryLevel(info.name);
    }


    /* Because the port info for sinks and sources is discontinued we need
     * to update the port info for them here. */
    updatePorts(c,c->ports);

    Q_EMIT checkDeviceSelectionSianal(&info);
}

/*
    获取声卡名，参考aplay -L
*/

QString card_get(int card)
{
    snd_ctl_t *handle;
    int  err, dev, idx;
    snd_ctl_card_info_t *info;
    snd_pcm_info_t *pcminfo;
    snd_ctl_card_info_alloca(&info);
    snd_pcm_info_alloca(&pcminfo);

    char name[32];
    sprintf(name, "hw:%d", card);
    if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
        qInfo("control open (%i): %s", card, snd_strerror(err));
        return "Error";
    }
    if ((err = snd_ctl_card_info(handle, info)) < 0) {
        qInfo("control hardware info (%i): %s", card, snd_strerror(err));
        snd_ctl_close(handle);
        return "Error";
    }
    dev = -1;
    while (1) {
        unsigned int count;
        if (dev < 0)
            break;
        snd_pcm_info_set_device(pcminfo, dev);
        snd_pcm_info_set_subdevice(pcminfo, 0);
        snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
        if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
//                    if (err != -ENOENT)
//                        error("control digital audio info (%i): %s", card, snd_strerror(err));
            continue;
        }
        qDebug() <<"card_get"<< card << snd_ctl_card_info_get_id(info);

        count = snd_pcm_info_get_subdevices_count(pcminfo);
        printf(("  Subdevices: %i/%i\n"),
            snd_pcm_info_get_subdevices_avail(pcminfo), count);
        for (idx = 0; idx < (int)count; idx++) {
            snd_pcm_info_set_subdevice(pcminfo, idx);
            if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
                qInfo("control digital audio playback info (%i): %s", card, snd_strerror(err));
            } else {
                printf(("  Subdevice #%i: %s\n"),
                    idx, snd_pcm_info_get_subdevice_name(pcminfo));
            }
        }
    }
    snd_ctl_close(handle);

    return snd_ctl_card_info_get_id(info);
}

void UkmediaVolumeControl::insertCardInfoJson(QString cardKey, QString cardstr, QString volumeKey, int volume, QString muteKey, bool mute, QString jsonfile)
{
     QJsonObject rootObj;

    rootObj.insert(cardKey, cardstr); //插入数据
    rootObj.insert(volumeKey, paVolumeToValue(volume));
    rootObj.insert(muteKey, mute);

    // 将json对象里的数据转换为字符串
    QJsonDocument doc;
    // 将object设置为本文档的主要对象
    doc.setObject(rootObj);

    QString filename = QDir::homePath();
    filename += jsonfile;

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        qDebug() << "can't open error!";
        return ;
    }

    QTextStream stream(&file);
    stream.setCodec("UTF-8");          // 设置写入编码是UTF8
    // 写入文件
    stream << doc.toJson();
    file.close();
}

void UkmediaVolumeControl::insertJson(QString key, QString value, QString jsonfile)
{
    QJsonObject rootObj;

    rootObj.insert(key, value); //插入数据

    // 将json对象里的数据转换为字符串
    QJsonDocument doc;
    // 将object设置为本文档的主要对象
    doc.setObject(rootObj);

    QString filename = QDir::homePath();
    filename += jsonfile;

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        qDebug() << "can't open error!";
        return ;
    }

    QTextStream stream(&file);
    stream.setCodec("UTF-8");		// 设置写入编码是UTF8
    // 写入文件
    stream << doc.toJson();
    file.close();
}

/*
 * 滑动条值转换成音量值
 */
int UkmediaVolumeControl::valueToPaVolume(int value)
{
    return value / UKMEDIA_VOLUME_NORMAL * PA_VOLUME_NORMAL;
}

/*
 * 音量值转换成滑动条值
 */
int UkmediaVolumeControl::paVolumeToValue(int value)
{
    return (value / PA_VOLUME_NORMAL * UKMEDIA_VOLUME_NORMAL) + 0.5;
}

/*
 * 根据声卡索引查找声卡名
 */
QString UkmediaVolumeControl::findCardName(int index,QMap<int,QString> cardMap)
{
    QMap<int, QString>::iterator it;

    for(it=cardMap.begin();it!=cardMap.end();) {
        if (it.key() == index) {
            return it.value();
        }
        ++it;
    }
    return "";
}

/*
 * 初始化默认输出音量
 */
void UkmediaVolumeControl::initDefaultSinkVolume(const pa_sink_info &info)
{
    int volume;
    QString outputCardName = findCardName(defaultOutputCard,cardMap);
    QString active_port_name = "";

    if (info.active_port->name != "")
        active_port_name = info.active_port->name;

    if (!defaultSinkName.contains("bluez") && strstr(info.name, "bluez")) {
        return;
    }
    else if (defaultSinkName.contains("bluez") && strstr(defaultSinkName,info.name)
             && !customSoundFile->isExist(stringRemoveUnrecignizedChar(active_port_name))) {
        Q_EMIT initBlueDeviceVolumeSig(info.index, active_port_name);
        return;
    }

    if (!active_port_name.isEmpty() && !customSoundFile->isExist(stringRemoveUnrecignizedChar(active_port_name))) {

        if (active_port_name.contains("headphone",Qt::CaseInsensitive)) {
            if(outputCardName.contains("bluez",Qt::CaseSensitive))
                volume = OUTPUT_VOLUME;
            else
                volume = HEADPHONE_VOLUME;
        }
        else if (active_port_name.contains("hdmi",Qt::CaseInsensitive)) {
            volume = HDMI_VOLUME;
        }
        else if (active_port_name.contains("speaker",Qt::CaseInsensitive)){
            volume = OUTPUT_VOLUME;
        }
        else if (active_port_name.contains("lineout",Qt::CaseInsensitive)){
            volume = HEADPHONE_VOLUME;
        }
        else {
            volume = OUTPUT_VOLUME;
        }
        const pa_sink_info *i = &info;
        setSinkVolumeByInfo(*i,valueToPaVolume(volume));

        customSoundFile->addXmlNode(active_port_name,false);
        qDebug() << "initDefaultSinkVolume" << active_port_name << volume ;
    }
}

/*
 * Update output device when the default output device or port is updated
 */
bool UkmediaVolumeControl::updateSink(UkmediaVolumeControl *w,const pa_sink_info &info) {
    bool is_new = false;
    m_defaultSinkVolume = info.volume;
    QMap<QString,QString>temp;
    int volume;
    QString now_card ="hw:CARD=";

    if (info.volume.channels >= 2)
        volume = MAX(info.volume.values[0],info.volume.values[1]);
    else
        volume = info.volume.values[0];
    if (info.active_port)
        sinkActivePortMap.insert(info.name,info.active_port->name);

    if(pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE))
        masterSinkDev = pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE);

    //fixed bug#155860 【声音】声音输出设备由外放切换耳机音量不会自动变成17%
    if (info.active_port) {
        if (!customSoundFile->isExist(stringRemoveUnrecignizedChar(info.active_port->name)) && !strstr(info.active_port->name,"histen") && !strstr(info.active_port->name,"3a_algo")) {
            const pa_sink_info *i = &info;
            initDefaultSinkVolume(*i);
        }
    }
    else if(strcmp(info.name,"auto_null")==0 && strcmp(defaultSinkName,"auto_null")==0){
        //此处引发问题:164995(音量初始化问题)、157123(自适应升级音量改变)
        //#if (QT_VERSION > QT_VERSION_CHECK(5, 12, 8))
        if (!customSoundFile->isExist(stringRemoveUnrecignizedChar("auto_null_sink"))) {
            setSinkVolumeByInfo(info, valueToPaVolume(OUTPUT_VOLUME));
            customSoundFile->addXmlNode("auto_null_sink", false);
            qDebug() << "initSystemVolume for auto_null_sink";
        }
        //#endif
    }

    //默认的输出音量
    if (strcmp(defaultSinkName.data(), info.name) == 0) {
        channel = info.volume.channels;
        channelMap = info.channel_map;
        sinkIndex = info.index;
        QString portName = "";

        if (info.active_port) {
            portName = info.active_port->name;

            //获取当前声卡名
            if( pa_proplist_gets(info.proplist,"alsa.card") && pa_proplist_gets(info.proplist,"alsa.device"))
            {
                now_card +=card_get(atoi(pa_proplist_gets(info.proplist,"alsa.card")));
                now_card +=",DEV=";
                now_card += pa_proplist_gets(info.proplist,"alsa.device");
            }
        }
        else
            portName = "";

        //  是否发送音乐暂停信号
        if (isNeedSendPortChangedSignal(portName, sinkPortName, info.name))
            sendPortChangedSignal();

        //  是否发送声音输出设备切换弹窗信号
        sendOsdWidgetSignal(portName, info.description);

        //  同步sinkPortName
        if (sinkPortName != portName) {
            sinkPortName = portName;
            QTimer::singleShot(100, this, SLOT(timeoutSlot()));
        }
        //向 /home/username/.config/audio.json 文件中插入属性
        insertCardInfoJson("card", now_card, "volume", volume, "mute", info.mute, "/.config/audio.json");


        //  同步默认声卡index
        defaultOutputCard = info.card;

        //  同步音量，同步UI
        balance = pa_cvolume_get_balance(&info.volume,&info.channel_map);
        refreshVolume(SoundType::SINK, volume, info.mute);
    }
    qDebug() << "UpdateSink--->" << "sinkIndex:" << sinkIndex << "sinkPortName:" << sinkPortName << "sinkVolume:" << sinkVolume << "channel:" << channel;

    if (info.ports) {
        for (pa_sink_port_info ** sinkPort = info.ports; *sinkPort != nullptr; ++sinkPort) {
            temp.insertMulti(info.name,(*sinkPort)->name);
        }
        QList <QMap<QString,QString>> sinkPortMapList;
        if(sinkPortMap.isEmpty())
            sinkPortMap.insertMulti(info.card,temp);
        sinkPortMapList = sinkPortMap.values();

        if(!sinkPortMapList.contains(temp))
            sinkPortMap.insertMulti(info.card,temp);


        const char *icon;
        std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities;

        icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);

        port_priorities.clear();
        for (uint32_t i=0; i<info.n_ports; ++i) {
            port_priorities.insert(*info.ports[i]);
        }

        w->ports.clear();
    }
    return is_new;
}

QString UkmediaVolumeControl::stringRemoveUnrecignizedChar(QString str) {
    str.remove(" ");
    str.remove("/");
    str.remove("(");
    str.remove(")");
    str.remove("[");
    str.remove("]");
    return str;
}


/*
 * stream suspend callback
 */
static void suspended_callback(pa_stream *s, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (pa_stream_is_suspended(s)) {
//        w->updateVolumeMeter(pa_stream_get_device_index(s), PA_INVALID_INDEX, -1);
    }
}

static void read_callback(pa_stream *s, size_t length, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    const void *data;
    double v;

    if (pa_stream_peek(s, &data, &length) < 0) {
        w->showError(UkmediaVolumeControl::tr("Failed to read data from stream").toUtf8().constData());
        return;
    }

    if (!data) {
        /* nullptr data means either a hole or empty buffer.
         * Only drop the stream when there is a hole (length > 0) */
        if (length)
            pa_stream_drop(s);
        return;
    }

    assert(length > 0);
    assert(length % sizeof(float) == 0);

    v = ((const float*) data)[length / sizeof(float) -1];

    pa_stream_drop(s);

    if (v < 0)
        v = 0;
    if (v > 1)
        v = 1;
}

pa_stream* UkmediaVolumeControl::createMonitorStreamForSource(uint32_t source_idx, uint32_t stream_idx = -1, bool suspend = false) {
    pa_stream *s;
    char t[16];
    pa_buffer_attr attr;
    pa_sample_spec ss;
    pa_stream_flags_t flags;

    ss.channels = 1;
    ss.format = PA_SAMPLE_FLOAT32;
    ss.rate = 25;

    memset(&attr, 0, sizeof(attr));
    attr.fragsize = sizeof(float);
    attr.maxlength = (uint32_t) -1;

    snprintf(t, sizeof(t), "%u", source_idx);
    m_pPaContext = getContext();
    if (!(s = pa_stream_new(getContext(), tr("Peak detect").toUtf8().constData(), &ss, nullptr))) {
        showError(tr("Failed to create monitoring stream").toUtf8().constData());
        return nullptr;
    }

    if (stream_idx != (uint32_t) -1)
        pa_stream_set_monitor_stream(s, stream_idx);

    pa_stream_set_read_callback(s, read_callback, this);
    pa_stream_set_suspended_callback(s, suspended_callback, this);

    flags = (pa_stream_flags_t) (PA_STREAM_DONT_MOVE | PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY |
                                 (suspend ? PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND : PA_STREAM_NOFLAGS) /*|
                                 (!showVolumeMetersCheckButton->isChecked() ? PA_STREAM_START_CORKED : PA_STREAM_NOFLAGS)*/);

    if (pa_stream_connect_record(s, t, &attr, flags) < 0) {
        showError(tr("Failed to connect monitoring stream").toUtf8().constData());
        pa_stream_unref(s);
        return nullptr;
    }
    return s;
}

void UkmediaVolumeControl::initDefaultSourceVolume(const pa_source_info &info)
{
    QString active_port_name = "";

    if (info.active_port->name != "")
         active_port_name = info.active_port->name;

    if (!active_port_name.isEmpty() && !customSoundFile->isExist(stringRemoveUnrecignizedChar(active_port_name))) {

        if(active_port_name.contains("hda"))
        {
            QString str = "hda-";
            QString preSourcePortName = active_port_name;
            int index = preSourcePortName.indexOf(str);
            str = preSourcePortName.remove(index,4);
            if(customSoundFile->isExist(stringRemoveUnrecignizedChar(preSourcePortName))){
                qDebug() << "自适应升级输入音量问题";
                return;
            }
        }
        if(active_port_name.contains("alc269vc"))
        {
            QString str = "alc269vc-";
            QString preSourcePortName = active_port_name;
            int index = preSourcePortName.indexOf(str);
            str = preSourcePortName.remove(index,9);
            if(customSoundFile->isExist(stringRemoveUnrecignizedChar(preSourcePortName))){
                qDebug() << "自适应升级输入音量问题" << active_port_name << preSourcePortName;
                return;
            }
        }

        setSourceVolumeByInfo(info,valueToPaVolume(MIC_VOLUME));
        customSoundFile->addXmlNode(active_port_name,false);
        qDebug() << "initDefaultSourceVolume" << active_port_name << MIC_VOLUME;
    }
}

void UkmediaVolumeControl::updateSource(const pa_source_info &info) {
    int volume;
    if (info.volume.channels >= 2)
        volume = MAX(info.volume.values[0],info.volume.values[1]);
    else
        volume = info.volume.values[0];

    if(pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE))
        masterDevice = pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE);

    if (info.active_port) {
        if(!customSoundFile->isExist(stringRemoveUnrecignizedChar(info.active_port->name))&&!strstr(info.active_port->name,"histen") && !strstr(info.active_port->name,"3a-algo")) {
            const pa_source_info *i = &info;
            initDefaultSourceVolume(*i);
        }
    }
    else {
        #if (QT_VERSION > QT_VERSION_CHECK(5, 12, 8))
        if (!customSoundFile->isExist(stringRemoveUnrecignizedChar("auto_null_source"))) {
            setSourceVolume(info.index, valueToPaVolume(MIC_VOLUME));
            customSoundFile->addXmlNode("auto_null_source", false);
            qDebug() << "initSystemVolume for auto_null_source";
        }
        #endif
    }

    if (strcmp(defaultSourceName.data(),info.name) == 0) {
        inputChannel = info.volume.channels;
        sourceIndex = info.index;

        QString portName = "";
        if (info.active_port)
            portName = info.active_port->name;
        else
            portName = "";

        if(pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE))
            portName = findMasterDeviceInfo(info.proplist, info.name);

        if (sourcePortName != portName) {
            qDebug() << "Send SIGNAL: sourcePortChanged. Case: different port" << sourcePortName << portName;
            sourcePortName = portName;
            sendSourcePortChangedSignal();
        }
        else {
            int index = -1;
            if (pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE))
                 index = findPortSourceIndex(masterDevice);

            if (defaultInputCard != info.card && defaultInputCard != index) {
                sendSourcePortChangedSignal();
                qDebug() << "Send SIGNAL: sourcePortChanged. Case: same port, different card" << defaultInputCard << info.card;
            }
        }

        if (pa_proplist_gets(info.proplist,PA_PROP_DEVICE_MASTER_DEVICE))
            defaultInputCard = findPortSourceIndex(masterDevice);
        else
            defaultInputCard = info.card;

        refreshVolume(SoundType::SOURCE, volume, info.mute);

    }

    QMap<QString,QString>temp;
    if(info.ports) {
        for (pa_source_port_info ** sourcePort = info.ports; *sourcePort != nullptr; ++sourcePort) {
            temp.insertMulti(info.name,(*sourcePort)->name);
        }
        QList <QMap<QString,QString>> sourcePortMapList;
        if(sourcePortMap.isEmpty())
            sourcePortMap.insertMulti(info.card,temp);
        sourcePortMapList = sourcePortMap.values();

        if(!sourcePortMapList.contains(temp))
            sourcePortMap.insertMulti(info.card,temp);

        qDebug() << "UpdateSource--->" << "sourceIndex:" << sourceIndex << "sourcePortName:" << sourcePortName << "sourceVolume:" << sourceVolume << "inputChannel:" << inputChannel;
    }
}


void UkmediaVolumeControl::setIconFromProplist(QLabel *icon, pa_proplist *l, const char *def) {
    const char *t;

    if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ICON_NAME)))
        goto finish;

    if ((t = pa_proplist_gets(l, PA_PROP_WINDOW_ICON_NAME)))
        goto finish;

    if ((t = pa_proplist_gets(l, PA_PROP_APPLICATION_ICON_NAME)))
        goto finish;

    if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ROLE))) {

        if (strcmp(t, "video") == 0 ||
            strcmp(t, "phone") == 0)
            goto finish;

        if (strcmp(t, "music") == 0) {
            t = "audio";
            goto finish;
        }

        if (strcmp(t, "game") == 0) {
            t = "applications-games";
            goto finish;
        }

        if (strcmp(t, "event") == 0) {
            t = "dialog-information";
            goto finish;
        }
    }

    t = def;

finish:

    setIconByName(icon, t);
}


void UkmediaVolumeControl::updateSinkInput(const pa_sink_input_info &info) {
    sinkInputChannel = info.volume.channels;
    const char *t;

    if ((t = pa_proplist_gets(info.proplist, "module-stream-restore.id"))) {
        if (t && strcmp(t, "sink-input-by-media-role:event") == 0) {
            g_debug("%s", tr("Ignoring sink-input due to it being designated as an event and thus handled by the Event widget").toUtf8().constData());
            return;
        }
    }

    const gchar *description = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_NAME);
    QString appId = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ID);
    QString appIconName = pa_proplist_gets(info.proplist,PA_PROP_APPLICATION_ICON_NAME);\
    const gchar *appBinary = pa_proplist_gets(info.proplist,PA_PROP_APPLICATION_PROCESS_BINARY);

    if(appBinary && strcmp(appBinary,"qaxbrowser") == 0){
        appBinary = "qaxbrowser-safe";
    }
    else if (description && strstr(description,"ALSA")){
        appBinary = nullptr;
    }
    else if (appBinary && strcmp(appBinary,"kylin-kmre-audio") == 0){
        appBinary = nullptr;
    }
    //网页版微信需要适配desktop文件
    else if (appBinary && strcmp(appBinary,"微信") == 0){
        appBinary = "wechat";
    }
    //win32微信
    else if (description && strstr(description, "WeChat")) {
        appBinary = "kylin-kwre-wechat";
    }
    //fixed bug:100614、100610 无线投屏时应用音量界面里应用音量没有图标显示
    else if (description && (strcmp(description,"castplayer") == 0  || strcmp(description,"miraclecast") == 0 )) {
        appBinary = "kylin-miracast";
     }

    if(appBinary) {
        appId = appBinary;
        appIconName = appBinary;
        description = appBinary;
    }
    else {
        qDebug() << "updateSinkInput appBinary is null";
        return;
    }

    qDebug() << " updateSinkInput：" << "icon:"<< appIconName << "id" << appId <<"dess"<< description <<"bind"<< appBinary << info.volume.values[0];

    //没制定应用名称的不加入到应用音量中
    if(description && !strstr(description,"QtPulseAudio") && !strstr(description,"ALSA") && !strstr(description,"aplay"))
    {
        sinkInputIndexMap.insert(info.index,description);    //记录每个sinkinput的index
        sinkInputValueMap.insert(description,info.volume.values[0]);     //记录相同description的最新音量
        sinkInputMuteMap.insert(description,info.mute);
        if(!sinkInputList.contains(description)){
            sinkInputList.append(description);  //记录应用的个数 来增删应用widget
            Q_EMIT addSinkInputSignal(description,appId.toLatin1().data(),info.index,info.volume.values[0],info.volume.channels);
         }
        Q_EMIT sinkInputVolumeChangedSignal(description,appId.toLatin1().data(),info.volume.values[0]); //应用音量发生改变时发送信号更新滑动条的值
     }
}

void UkmediaVolumeControl::updateSourceOutput(const pa_source_output_info &info) {
    sourceOutputChannel = info.volume.channels;
    const char *app;
    bool is_new = false;

    if ((app = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ID)))
        if (strcmp(app, "org.PulseAudio.pavucontrol") == 0
                || strcmp(app, "org.gnome.VolumeControl") == 0
                || strcmp(app, "org.kde.kmixd") == 0)
            return;

    const gchar *description = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_NAME);
    QString appId = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ID);
    QString appIconName = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ICON_NAME);
    const gchar *appBinary = pa_proplist_gets(info.proplist,PA_PROP_APPLICATION_PROCESS_BINARY);

    //kmre没有设计对应的desktop图标，暂时不显示在应用音量窗口
    if (appBinary && strcmp(appBinary,"kylin-kmre-audio") == 0){
        appBinary = nullptr;
    }
    //fixed bug:100614、100610 无线投屏时应用音量界面里应用音量没有图标显示
    else if (description && (strcmp(description,"castplayer") == 0  || strcmp(description,"miraclecast") == 0 )) {
        appBinary = "kylin-miracast";
    }

    if(appBinary) {
        appId = appBinary;
        description = appBinary;
        appIconName = appBinary;
    }
    else {
        qDebug() << "updateSourceOutput appBinary is null";
        return;
    }

    qDebug() << "updateSourceOutput:" << "icon:"<< appIconName << "id" << appId <<"dess"<< description <<"bind"<< appBinary << info.volume.values[0];

    if(sinkInputValueMap.keys().contains(description)){
        qDebug() << description << "已产生了sink-input，不再对其source-output生成app widget";
        return;
    }

    if(description && !strstr(description,"QtPulseAudio") && !strstr(description,"ALSA") && !strstr(description,"Volume Control") && !strstr(description,"aplay"))
    {
        sourceOutputIndexMap.insert(info.index,description); //记录所有source-output index
        sourceOutputValueMap.insert(description,info.volume.values[0]); //记录相同description的最新音量
        sinkInputMuteMap.insert(description,info.mute);
        if(!sinkInputList.contains(description)){
            sinkInputList.append(description);
            Q_EMIT addSinkInputSignal(description,appId.toLatin1().data(),info.index,info.volume.values[0],info.volume.channels);
        }
        Q_EMIT sinkInputVolumeChangedSignal(description,appId.toLatin1().data(),info.volume.values[0]);
    }
}


void UkmediaVolumeControl::updateClient(const pa_client_info &info) {
    g_free(clientNames[info.index]);
    clientNames[info.index] = g_strdup(info.name);
}

void UkmediaVolumeControl::updateServer(const pa_server_info &info) {
    defaultSourceName = info.default_source_name ? info.default_source_name : "";
    defaultSinkName = info.default_sink_name ? info.default_sink_name : "";
    qDebug() << "updateServer" << "defaultSinkName " << defaultSinkName << "defaultSourceName" << defaultSourceName;

}

#if HAVE_EXT_DEVICE_RESTORE_API
void UkmediaVolumeControl::updateDeviceInfo(const pa_ext_device_restore_info &info) {
//    if (sinkWidgets.count(info.index)) {
//        SinkWidget *w;
//        pa_format_info *format;

//        w = sinkWidgets[info.index];

//        w->updating = true;

//        /* Unselect everything */
//        for (int j = 1; j < PAVU_NUM_ENCODINGS; ++j)
//            w->encodings[j].widget->setChecked(false);


//        for (uint8_t i = 0; i < info.n_formats; ++i) {
//            format = info.formats[i];
//            for (int j = 1; j < PAVU_NUM_ENCODINGS; ++j) {
//                if (format->encoding == w->encodings[j].encoding) {
//                    w->encodings[j].widget->setChecked(true);
//                    break;
//                }
//            }
//        }

//        w->updating = false;
//    }
}
#endif


void UkmediaVolumeControl::setConnectionState(gboolean connected) {
    if (m_connected != connected) {
        m_connected = connected;
        if (m_connected) {
//            connectingLabel->hide();
//            notebook->show();
        } else {
//            notebook->hide();
//            connectingLabel->show();
        }
    }
}


void UkmediaVolumeControl::removeCard(uint32_t index) {

}

void UkmediaVolumeControl::removeSink(uint32_t index) {
    QMap<int,QString>::iterator it;
    Q_EMIT removeSinkSignal();
    for (it=sinkMap.begin();it!=sinkMap.end();)  {
        if (it.key() == index) {
            qDebug() << "removeSink" << index;
            sinkMap.erase(it);
            break;
        }
        ++it;
    }
}

void UkmediaVolumeControl::removeSource(uint32_t index) {
    QMap<int,QString>::iterator it;
    for (it=sourceMap.begin();it!=sourceMap.end();)  {
        if (it.key() == index) {
            qDebug() << "removeSource" << index;
            sourceMap.erase(it);
            break;
        }
        ++it;
    }
}

void UkmediaVolumeControl::removeSinkInput(uint32_t index)
{
    QString description = "";
    qDebug() << "removeSinkInput" << index;

    description = sinkInputIndexMap.value(index); //先获取要移除sinkinput的description
    sinkInputIndexMap.remove(index);//移除对应index的sinkinput

    if(!sinkInputIndexMap.values().contains(description) || sinkInputIndexMap.isEmpty())
    {
        sinkInputValueMap.remove(description);//如果所有相同description的index都被移除，需要移除valueMap对应的description
        sinkInputMuteMap.remove(description);
        if(description != "")
            Q_EMIT removeSinkInputSignal(description.toLatin1().data());//并移除对应app widget
    }

}

void UkmediaVolumeControl::removeSourceOutput(uint32_t index)
{
    QString description = "";
    qDebug() << "removeSourceOutput" << index;

    description = sourceOutputIndexMap.value(index);
    sourceOutputIndexMap.remove(index);
    if(description == "Loopback")
        isLoadLoopback = false;

    if(!sourceOutputIndexMap.values().contains(description) || sourceOutputIndexMap.isEmpty())
    {
        sourceOutputValueMap.remove(description);
        sinkInputMuteMap.remove(description);
        if(description != "")
            Q_EMIT removeSinkInputSignal(description.toLatin1().data());
    }

}

void UkmediaVolumeControl::removeClient(uint32_t index) {
    g_free(clientNames[index]);
    clientNames.erase(index);
}

void UkmediaVolumeControl::setConnectingMessage(const char *string) {
    QByteArray markup = "<i>";
    if (!string)
        markup += tr("Establishing connection to PulseAudio. Please wait...").toUtf8().constData();
    else
        markup += string;
    markup += "</i>";
}

void UkmediaVolumeControl::showError(const char *txt) {
    char buf[256];

    snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context)));
    qDebug() <<QString::fromUtf8(buf);
}

void UkmediaVolumeControl::decOutstanding(UkmediaVolumeControl *w) {
    if (w->n_outstanding <= 0)
        return;

    if (--w->n_outstanding <= 0) {
        w->setConnectionState(true);
    }
}

void UkmediaVolumeControl::cardCb(pa_context *c, const pa_card_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Card callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    w->cardMap.insert(i->index,i->name);

    w->signalCount += 1;
//    if(w->signalCount >= 2){
//        w->updateCard(w,*i);
//        w->signalCount = 1;
//    }
    w->updateCard(w,*i);
}

void UkmediaVolumeControl::batteryLevelCb(pa_context *c, const pa_card_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Card callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    qDebug() << "batteryLevelCb" << i->index << eol;
    const char *battery = pa_proplist_gets(i->proplist,"bluetooth.battery");
    QString macAddr = pa_proplist_gets(i->proplist, "device.string");

    if (battery) {

        qDebug() << "get bluetooth battery is:" << battery << atoi(battery);
        w->batteryLevel = atoi(battery);
    }
    else
        w->batteryLevel = -1;

    Q_EMIT w->bluetoothBatteryChanged(macAddr, w->batteryLevel);
}

void UkmediaVolumeControl::bluetoothCardCb(pa_context *c, const pa_card_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Card callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    qDebug() << "bluetooth card cb" << i->index << eol;
    const char *battery = pa_proplist_gets(i->proplist,"bluetooth.battery");
    QString macAddr = pa_proplist_gets(i->proplist, "device.string");
    if (battery) {

        qDebug() << "bluetooth battery is ===" << battery << atoi(battery);
//        char *str = strtok(battery,".");
        w->batteryLevel = atoi(battery);
    }
    else
        w->batteryLevel = -1;

    Q_EMIT w->bluetoothBatteryChanged(macAddr, w->batteryLevel);
}


#if HAVE_EXT_DEVICE_RESTORE_API
static void ext_device_restore_subscribeCb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata);
#endif

void UkmediaVolumeControl::sinkIndexCb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Sink callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }

    int volume;
    QString portName = "";
    if(i->volume.channels >= 2)
        volume = MAX(i->volume.values[0],i->volume.values[1]);
    else
        volume = i->volume.values[0];

    w->channel = i->volume.channels;
    w->sinkIndex = i->index;
    w->balance = pa_cvolume_get_balance(&i->volume,&i->channel_map);

    /*  同步sinkPortName三种情况
     *  case1: 默认声卡端口可用，直接同步当前可用端口
     *  case2：默认声卡端口不可用，端口设置为空
     *  case3: 默认声卡为虚拟声卡，查找master.device声卡信息并同步
     */
    if (i->active_port)
        portName = i->active_port->name;
    else
        portName = "";

    if (pa_proplist_gets(i->proplist, PA_PROP_DEVICE_MASTER_DEVICE))
        portName = w->findSinkMasterDeviceInfo(i->proplist, i->name);

    //  是否发送音乐暂停信号
    if (w->isNeedSendPortChangedSignal(portName, w->sinkPortName, i->name))
        w->sendPortChangedSignal();

    //  是否发送声音输出设备切换弹窗信号
    w->sendOsdWidgetSignal(portName, i->description);

    //  同步sinkPortName
    w->sinkPortName = (w->sinkPortName != portName) ? portName : w->sinkPortName;

    //  同步默认声卡index
    w->defaultOutputCard = i->card;

    //  同步音量，同步UI
    w->refreshVolume(SoundType::SINK, volume, i->mute);

    qDebug() << "sinkIndexCb" << w->defaultOutputCard << w->sinkPortName << w->sinkIndex;
}

void UkmediaVolumeControl::sourceIndexCb(pa_context *c, const pa_source_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Source callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    int volume;
    if(i->volume.channels >= 2)
        volume = MAX(i->volume.values[0],i->volume.values[1]);
    else
        volume = i->volume.values[0];

    w->inputChannel = i->volume.channels;
    w->sourceIndex = i->index;

    QString portName = "";
    if (i->active_port)
        portName = i->active_port->name;
    else
        portName = "";

    if(pa_proplist_gets(i->proplist,PA_PROP_DEVICE_MASTER_DEVICE))
        portName = w->findMasterDeviceInfo(i->proplist, i->name);

    if (w->sourcePortName != portName) {
        qDebug() << "Send SIGNAL: sourcePortChanged. Case: different port" << w->sourcePortName << portName;
        w->sourcePortName = portName;
        w->sendSourcePortChangedSignal();
    }
    else {
        int index = -1;
        if (pa_proplist_gets(i->proplist,PA_PROP_DEVICE_MASTER_DEVICE))
             index = w->findPortSourceIndex(w->masterDevice);
        if (w->defaultInputCard != i->card && w->defaultInputCard != index) {
            w->sendSourcePortChangedSignal();
            qDebug() << "Send SIGNAL: sourcePortChanged. Case: same port, different card" << w->defaultInputCard << i->card;
        }
    }

    if (pa_proplist_gets(i->proplist,PA_PROP_DEVICE_MASTER_DEVICE))
        w->defaultInputCard = w->findPortSourceIndex(w->masterDevice);
    else
        w->defaultInputCard = i->card;

    w->refreshVolume(SoundType::SOURCE, volume, i->mute);

    qDebug() << "sourceIndexCb---" << w->sourcePortName;
}

void UkmediaVolumeControl::sinkCb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Sink callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    w->m_pDefaultSink = i;
    qDebug() << "SinkCb"  << "Volume:" << i->volume.values[0] << "isMute:" << i->mute << i->name;
    w->sinkMap.insert(i->index,i->name);
    w->updateSink(w,*i);
}

void UkmediaVolumeControl::sourceCb(pa_context *c, const pa_source_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Source callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    qDebug() << "SourceCb" << "Volume:" << i->volume.values[0] << "isMute:" << i->mute << i->name ;
//    w->sourceIndex = i->index;

    w->sourceMap.insert(i->index,i->name);
    w->updateSource(*i);
}

void UkmediaVolumeControl::sinkInputCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Sink input callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }

    w->updateSinkInput(*i);
}

void UkmediaVolumeControl::sourceOutputCb(pa_context *c, const pa_source_output_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Source output callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0)  {
        decOutstanding(w);
        return;
    }

    if (strstr(i->name,"Loopback")) {
        w->sourceOutputIndexMap.insert(i->index,"Loopback");
        qDebug() << "The module-loopback has been loaded." << w->sourceOutputIndexMap;
    }

    qDebug() << "sourceOutputCb" << i->name;
    w->updateSourceOutput(*i);
}

void UkmediaVolumeControl::clientCb(pa_context *c, const pa_client_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Client callback failure").toUtf8().constData());
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
//    qDebug() << "clientCb" << i->name;
    w->updateClient(*i);
}

void UkmediaVolumeControl::serverInfoCb(pa_context *, const pa_server_info *i, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (!i) {
        w->showError(QObject::tr("Server info callback failure").toUtf8().constData());
        return;
    }

    pa_operation *o;
    //默认的输出设备改变时需要获取默认的输出音量
    if (strcmp(w->defaultSinkName.data(),i->default_sink_name) != 0) {
      //默认输出改变时才走sinkindexcb
        if(!(o = pa_context_get_sink_info_by_name(w->getContext(),i->default_sink_name,sinkIndexCb,w))) {
            w->showError(tr("pa_context_get_sink_info_by_name() failed").toUtf8().constData());
        }
    }
    if(!(o = pa_context_get_source_info_by_name(w->getContext(),i->default_source_name,sourceIndexCb,w))){
        w->showError(tr("pa_context_get_source_info_by_name() failed").toUtf8().constData());
    }

    qDebug() << "serverInfoCb" << i->default_sink_name << i->default_source_name;
    w->updateServer(*i);

    QTimer::singleShot(100, w, SLOT(timeoutSlot()));
    decOutstanding(w);
}

void UkmediaVolumeControl::serverInfoIndexCb(pa_context *, const pa_server_info *i, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (!i) {
        w->showError(QObject::tr("Server info callback failure").toUtf8().constData());
        return;
    }
    pa_operation *o;

//    qDebug() << "serverInfoIndexCb" << i->default_sink_name << i->default_source_name;
    w->updateServer(*i);
    decOutstanding(w);
}

void UkmediaVolumeControl::timeoutSlot()
{
    Q_EMIT deviceChangedSignal();
}

void UkmediaVolumeControl::getBatteryLevel(QString dev)
{
    pa_operation *o;
    if (!(o = pa_context_get_card_info_by_name(getContext(), dev.toLatin1().data(), batteryLevelCb, this))) {
        showError(QObject::tr("pa_context_get_card_info_by_index() failed").toUtf8().constData());
        return ;
    }
    pa_operation_unref(o);
}

void UkmediaVolumeControl::extStreamRestoreReadCb(
        pa_context *c,
        const pa_ext_stream_restore_info *i,
        int eol,
        void *userdata) {

    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        decOutstanding(w);
        g_debug(QObject::tr("Failed to initialize stream_restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(c)));
        return;
    }

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
}

void UkmediaVolumeControl::extStreamRestoreSubscribeCb(pa_context *c, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    pa_operation *o;

    if (!(o = pa_ext_stream_restore_read(c, extStreamRestoreReadCb, w))) {
        w->showError(QObject::tr("pa_ext_stream_restore_read() failed").toUtf8().constData());
        return;
    }
    qDebug() << "extStreamRestoreSubscribeCb" ;
    pa_operation_unref(o);
}

#if HAVE_EXT_DEVICE_RESTORE_API
void ext_device_restore_read_cb(
        pa_context *,
        const pa_ext_device_restore_info *i,
        int eol,
        void *userdata) {

    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        w->decOutstanding(w);
        g_debug(QObject::tr("Failed to initialize device restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
        return;
    }

    if (eol > 0) {
        w->decOutstanding(w);
        return;
    }

    /* Do something with a widget when this part is written */
    w->updateDeviceInfo(*i);
}

static void ext_device_restore_subscribeCb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    pa_operation *o;

    if (type != PA_DEVICE_TYPE_SINK)
        return;

    if (!(o = pa_ext_device_restore_read_formats(c, type, idx, ext_device_restore_read_cb, w))) {
        w->showError(QObject::tr("pa_ext_device_restore_read_sink_formats() failed").toUtf8().constData());
        return;
    }

    pa_operation_unref(o);
}
#endif

void UkmediaVolumeControl::extDeviceManagerReadCb(
        pa_context *c,
        const pa_ext_device_manager_info *,
        int eol,
        void *userdata) {

    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        decOutstanding(w);
        g_debug(QObject::tr("Failed to initialize device manager extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(c)));
        return;
    }

    w->canRenameDevices = true;

    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    qDebug() << "extDeviceManagerReadCb";
    /* Do something with a widget when this part is written */
}

void UkmediaVolumeControl::extDeviceManagerSubscribeCb(pa_context *c, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    pa_operation *o;

    if (!(o = pa_ext_device_manager_read(c, extDeviceManagerReadCb, w))) {
        w->showError(QObject::tr("pa_ext_device_manager_read() failed").toUtf8().constData());
        return;
    }
    qDebug() << "extDeviceManagerSubscribeCb";
    pa_operation_unref(o);
}

void UkmediaVolumeControl::subscribeCb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
        case PA_SUBSCRIPTION_EVENT_SINK:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
                w->removeSink(index);
            else {
                    pa_operation *o;
                    if (!(o = pa_context_get_sink_info_by_index(c, index, sinkCb, w))) {
                        w->showError(QObject::tr("pa_context_get_sink_info_by_index() failed").toUtf8().constData());
                        return;
                    }
                    pa_operation_unref(o);
            }
            break;

        case PA_SUBSCRIPTION_EVENT_SOURCE:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
                w->removeSource(index);
            else {
                pa_operation *o;
                if (!(o = pa_context_get_source_info_by_index(c, index, sourceCb, w))) {
                    w->showError(QObject::tr("pa_context_get_source_info_by_index() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
                w->removeSinkInput(index);
            else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_input_info(c, index, sinkInputCb, w))) {
                    w->showError(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
                w->removeSourceOutput(index);
            else {
                pa_operation *o;
                if (!(o = pa_context_get_source_output_info(c, index, sourceOutputCb, w))) {
                    w->showError(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
                w->removeClient(index);
            else {
                pa_operation *o;
                if (!(o = pa_context_get_client_info(c, index, clientCb, w))) {
                    w->showError(QObject::tr("pa_context_get_client_info() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

        case PA_SUBSCRIPTION_EVENT_SERVER: {
                pa_operation *o;
                if (!(o = pa_context_get_server_info(c, serverInfoCb, w))) {
                    w->showError(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

        //case PA_SUBSCRIPTION_EVENT_BLUETOOTH_BATTERY:

        //    qDebug() << "PA_SUBSCRIPTION_EVENT_BLUETOOTH_BATTERY" ;
        //    pa_operation *o;
        //    if (!(o = pa_context_get_card_info_by_index(c, index, bluetoothCardCb, w))) {
        //        w->showError(QObject::tr("pa_context_get_card_info_by_index() failed").toUtf8().constData());
        //        return;
        //    }
        //    pa_operation_unref(o);
        //    break;

        case PA_SUBSCRIPTION_EVENT_CARD:
            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "remove cards------";
                //移除outputPort
                w->removeSinkPortMap(index);
                w->removeOutputPortMap(index);
                w->removeInputPortMap(index);

                Q_EMIT w->updatePortSignal();

                w->removeCardMap(index);
                w->removeCardProfileMap(index);

                w->removeProfileMap(index);
                w->removeInputProfile(index);
                w->removeCard(index);
            }
            else {
                pa_operation *o;
                if (!(o = pa_context_get_card_info_by_index(c, index, cardCb, w))) {
                    w->showError(QObject::tr("pa_context_get_card_info_by_index() failed").toUtf8().constData());
                    return;
                }
                pa_operation_unref(o);
            }
            break;

    }
}

void UkmediaVolumeControl::contextStateCallback(pa_context *c, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    g_assert(c);

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_UNCONNECTED:
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:

            break;

        case PA_CONTEXT_READY: {
            pa_operation *o;
            qDebug() << "pa_context_get_state" << "PA_CONTEXT_READY" << pa_context_get_state(c);
            w->reconnectTimeout = 1;

            /* Create event widget immediately so it's first in the list */

            pa_context_set_subscribe_callback(c, subscribeCb, w);

            if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
                                           (PA_SUBSCRIPTION_MASK_SINK|
                                            PA_SUBSCRIPTION_MASK_SOURCE|
                                            PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                            PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
                                            PA_SUBSCRIPTION_MASK_CLIENT|
                                            PA_SUBSCRIPTION_MASK_SERVER|
                                            PA_SUBSCRIPTION_MASK_CARD), nullptr, nullptr))) {
                w->showError(QObject::tr("pa_context_subscribe() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);

            /* Keep track of the outstanding callbacks for UI tweaks */
            w->n_outstanding = 0;

            if (!(o = pa_context_get_server_info(c, serverInfoCb, w))) {
                w->showError(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;

            if (!(o = pa_context_get_client_info_list(c, clientCb, w))) {
                w->showError(QObject::tr("pa_context_client_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;

            if (!(o = pa_context_get_card_info_list(c, cardCb, w))) {
                w->showError(QObject::tr("pa_context_get_card_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;

            if (!(o = pa_context_get_sink_info_list(c, sinkCb, w))) {
                w->showError(QObject::tr("pa_context_get_sink_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;

            if (!(o = pa_context_get_source_info_list(c, sourceCb, w))) {
                w->showError(QObject::tr("pa_context_get_source_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;
            if (!(o = pa_context_get_sink_input_info_list(c, sinkInputCb, w))) {
                w->showError(QObject::tr("pa_context_get_sink_input_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;

            if (!(o = pa_context_get_source_output_info_list(c, sourceOutputCb, w))) {
                w->showError(QObject::tr("pa_context_get_source_output_info_list() failed").toUtf8().constData());
                return;
            }
            pa_operation_unref(o);
            w->n_outstanding++;
            Q_EMIT w->paContextReady();
            break;
        }

        case PA_CONTEXT_FAILED:
            w->setConnectionState(false);
            pa_context_unref(w->m_pPaContext);
            w->m_pPaContext = nullptr;

            if (w->reconnectTimeout > 0) {
                g_debug("%s", QObject::tr("Connection failed, attempting reconnect").toUtf8().constData());
//                g_timeout_add_seconds(reconnectTimeout, connectToPulse, w);
            }
            return;

        case PA_CONTEXT_TERMINATED:
        default:
            return;
    }
}

void UkmediaVolumeControl::moduleInfoCb(pa_context *c, const pa_module_info *i, int eol, void *userdata)
{
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);

    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("moduleInfoCb callback failure").toUtf8().constData());
        return;
    }
    if(i && strcmp(i->name,w->findModuleStr.toLatin1().data()) == 0) {
        w->findModuleIndex = i->index;
    }

}

pa_context* UkmediaVolumeControl::getContext(void) {
    return context;
}

gboolean UkmediaVolumeControl::connectToPulse(gpointer userdata) {
    pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default());
    api = pa_glib_mainloop_get_api(m);
    pa_proplist *proplist = pa_proplist_new();
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, QObject::tr("Ukui Media Volume Control").toUtf8().constData());
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.pavucontrol");
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card");
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "PACKAGE_VERSION");
    context = pa_context_new_with_proplist(api, nullptr, proplist);
    g_assert(context);

    pa_proplist_free(proplist);

    pa_context_set_state_callback(getContext(), contextStateCallback, this);
    if (pa_context_connect(getContext(), nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
        if (pa_context_errno(getContext()) == PA_ERR_INVALID) {
            /*w->setConnectingMessage(QObject::tr("Connection to PulseAudio failed. Automatic retry in 5s\n\n"
                "In this case this is likely because PULSE_SERVER in the Environment/X11 Root Window Properties\n"
                "or default-server in client.conf is misconfigured.\n"
                "This situation can also arrise when PulseAudio crashed and left stale details in the X11 Root Window.\n"
                "If this is the case, then PulseAudio should autospawn again, or if this is not configured you should\n"
                "run start-pulseaudio-x11 manually.").toUtf8().constData());*/
            qFatal("connect pulseaudio failed")  ;
        }
        else {
//            g_timeout_add_seconds(5,connectToPulse,w);
        }
    }

    return false;
}


/*
 * 根据名称获取sink input音量
 */
int UkmediaVolumeControl::getSinkInputVolume(const gchar *name)
{
    return sinkInputValueMap.value(name);
}

/*
 * 根据名称获取source output音量
 */
int UkmediaVolumeControl::getSourceOutputVolume(const gchar *name)
{
   return sourceOutputValueMap.value(name);
}


void UkmediaVolumeControl::sinkInputCallback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
    UkmediaVolumeControl *w = static_cast<UkmediaVolumeControl*>(userdata);
    if (eol < 0) {
        if (pa_context_errno(c) == PA_ERR_NOENTITY)
            return;

        w->showError(QObject::tr("Sink input callback failure").toUtf8().constData());
        return;
    }
    if (eol > 0) {
        decOutstanding(w);
        return;
    }
    w->sinkInputMuted = i->mute;
    if (i->volume.channels >= 2)
        w->sinkInputVolume = MAX(i->volume.values[0],i->volume.values[1]);
    else
        w->sinkInputVolume = i->volume.values[0];
    qDebug() << "sinkInputCallback" <<w->sinkInputVolume <<i->name;
}

/*
 * 移除指定索引的output port
 */
void UkmediaVolumeControl::removeOutputPortMap(int index)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    for (it=outputPortMap.begin();it!=outputPortMap.end();) {
        if (it.key() == index) {
            qDebug() << "removeoutputport" <<it.key() << it.value();
            outputPortMap.erase(it);
            break;
        }
        ++it;
    }
}

/*
 * 移除指定索引的input port
 */
void UkmediaVolumeControl::removeInputPortMap(int index)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    for (it=inputPortMap.begin();it!=inputPortMap.end();) {
        if (it.key() == index) {
            inputPortMap.erase(it);
            break;
        }
        ++it;
    }
}

/*
 * 移除指定索引的card
 */
void UkmediaVolumeControl::removeCardMap(int index)
{
    QMap<int, QString>::iterator it;
    for (it=cardMap.begin();it!=cardMap.end();) {
        if (it.key() == index) {
            cardMap.erase(it);
            break;
        }
        ++it;
    }
}

void UkmediaVolumeControl::removeCardProfileMap(int index)
{
    qDebug() << "removeCardProfileMap";
    QMap<int, QList<QString>>::iterator it;
    QMap<int, QMap<QString,int>>::iterator at;
    for (it=cardProfileMap.begin();it!=cardProfileMap.end();) {
        if (it.key() == index) {
            cardProfileMap.erase(it);
            break;
        }
        ++it;
    }

    for (at=cardProfilePriorityMap.begin();at!=cardProfilePriorityMap.cend();) {
        if (at.key() == index) {
            cardProfilePriorityMap.erase(at);
            break;
        }
        ++at;
    }
}

void UkmediaVolumeControl::removeSinkPortMap(int index)
{
    qDebug() << "removeSinkPortMap///";
    QMap<int,QMap<QString,QString>>::iterator it;
    for(it=sinkPortMap.begin();it!=sinkPortMap.end();){
        if(it.key() == index) {
            //usb耳机一个声卡存在两个端口，使用erase时只会擦除一个key时，导致拔出耳机时没有完全移除key值，需要使用remove将存入的两个相同的key值全部删除
            //sinkPortMap.erase(it);
            sinkPortMap.remove(index);
            break;
        }
        ++it;
    }
}

void UkmediaVolumeControl::removeProfileMap(int index)
{
    QMap<int,QMap<QString,QString>>::iterator it;
    qDebug() << "removeProfileMap"  << index;

    for (it=profileNameMap.begin();it!=profileNameMap.end();) {
        if(it.key() == index)
        {
            profileNameMap.erase(it);
            break;
        }
        ++it;
    }

}

bool UkmediaVolumeControl::isExitOutputPort(QString name)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString>::iterator at;
    QMap<QString,QString> portMap;
    for (it=outputPortMap.begin();it!=outputPortMap.end();) {
        portMap = it.value();
        for (at=portMap.begin();at!=portMap.end();) {
            if (at.value() == name)
                return true;
            ++at;
        }
        ++it;
    }
    return false;
}

int UkmediaVolumeControl::findOutputPort(QString name){
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString>::iterator at;
    QMap<QString,QString> portMap;
    int count = 0;
    for (it=outputPortMap.begin();it!=outputPortMap.end();) {
        portMap = it.value();
        for (at=portMap.begin();at!=portMap.end();) {
            if (at.key() == name) {
                ++count;
            }
            ++at;
        }
        ++it;
    }
    return count;
}

QString UkmediaVolumeControl::findSinkActivePortName(QString name)
{
    QString portName = "";
    QMap<QString,QString>::iterator at;
    for (at=sinkActivePortMap.begin();at!=sinkActivePortMap.end();++at) {
        if (at.key() == name) {
            portName = at.value();
            break;
        }
    }
    return portName;
}

void UkmediaVolumeControl::removeInputProfile(int index)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    qDebug() << "removeInputProfile"  << index;

    for (it=inputPortProfileNameMap.begin();it!=inputPortProfileNameMap.end();) {
        if(it.key() == index){
            inputPortProfileNameMap.erase(it);
            break;
        }
        ++it;
    }
}

bool UkmediaVolumeControl::isExitInputPort(QString name)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString>::iterator at;
    QMap<QString,QString> portMap;
    for (it=inputPortMap.begin();it!=inputPortMap.end();) {
        portMap = it.value();
        for (at=portMap.begin();at!=portMap.end();) {
            if (at.value() == name)
                return true;
            ++at;
        }
        ++it;
    }
    return false;
}

bool UkmediaVolumeControl::isNeedSendPortChangedSignal(QString newPort, QString prePort, QString cardName)
{
    //  端口不变时不发送信号
    if (newPort == prePort)
        return false;

    //  切换到空端口发送信号
    if (newPort.isEmpty() && !prePort.isEmpty()&&(cardName!="mono")) {
        qDebug() << "Send SIGNAL: sinkPortChanged. Case: Switch to a null port" << newPort << prePort;
        return true;
    }

    //  拔插设备自动切换到扬声器端口时发送信号，用户主动切换输出时不做信号发送
    if (newPort.contains(SPEAKER) && !cardName.contains("bluez") && !cardName.contains("usb")) {
        int newPortCount = -1;
        int prePortCount = -1;
        newPortCount = findOutputPort(newPort);
        prePortCount = findOutputPort(prePort);
        //  保证outputPortMap中只含有一个扬声器端口，并且之前的端口已不在map中
        if (newPortCount == 1 && prePortCount == 0) {
            qDebug() << "Send SIGNAL: sinkPortChanged. Case: Switch to speaker port" << newPort << prePort;
            return true;
        }
    }

    return false;
}


void UkmediaVolumeControl::sendPortChangedSignal()
{
    QDBusMessage message = QDBusMessage::createSignal("/","org.ukui.media","sinkPortChanged");
    message<<"portChanged";
    QDBusConnection::sessionBus().send(message);
    qDebug() << "sendPortChangedSignal sinkPortChanged " << endl;
}

void UkmediaVolumeControl::sendSourcePortChangedSignal()
{
    QDBusMessage message = QDBusMessage::createSignal("/","org.ukui.media","sourcePortChanged");
    message<<"portChanged";
    QDBusConnection::sessionBus().send(message);
    qDebug() << "sendPortChangedSignal sourcePortChanged " << endl;
}


int UkmediaVolumeControl::findPortSinkIndex(QString name)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString> portNameMap;
    QMap<QString,QString>::iterator tempMap;
    int cardIndex = -1;
    for (it=sinkPortMap.begin();it!=sinkPortMap.end();) {
        portNameMap = it.value();
        for (tempMap=portNameMap.begin();tempMap!=portNameMap.end();) {
            if (tempMap.key() == name) {
                cardIndex = it.key();
                break;
            }
            ++tempMap;
        }
        ++it;
    }
    return cardIndex;
}


int UkmediaVolumeControl::findPortSourceIndex(QString name)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString> portNameMap;
    QMap<QString,QString>::iterator tempMap;
    int cardIndex = -1;
    for (it=sourcePortMap.begin();it!=sourcePortMap.end();) {
        portNameMap = it.value();
        for (tempMap=portNameMap.begin();tempMap!=portNameMap.end();) {
            if (tempMap.key() == name) {
                cardIndex = it.key();
                break;
            }
            ++tempMap;
        }
        ++it;
    }
    return cardIndex;
}

QString UkmediaVolumeControl::findSinkPortName(int cardIndex)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString> portNameMap;
    QMap<QString,QString>::iterator tempMap;
    QString portName = "";
    for (it=outputPortMap.begin();it!=outputPortMap.end();) {
        if(it.key() == cardIndex){
            portNameMap = it.value();
            for (tempMap=portNameMap.begin();tempMap!=portNameMap.end();) {
                portName = tempMap.key();
                break;
            }
            ++tempMap;
        }
        ++it;
    }
    return portName;
}

QString UkmediaVolumeControl::findSourcePortName(int cardIndex)
{
    QMap<int, QMap<QString,QString>>::iterator it;
    QMap<QString,QString> portNameMap;
    QMap<QString,QString>::iterator tempMap;
    QString portName = "";
    for (it=inputPortMap.begin();it!=inputPortMap.end();) {
        if(it.key() == cardIndex){
            portNameMap = it.value();
            for (tempMap=portNameMap.begin();tempMap!=portNameMap.end();) {
                portName = tempMap.key();
                break;
            }
            ++tempMap;
        }
        ++it;
    }
    return portName;
}

QString UkmediaVolumeControl::findSinkMasterDeviceInfo(pa_proplist *proplist, const char *info_name)
{
    masterSinkDev = pa_proplist_gets(proplist,PA_PROP_DEVICE_MASTER_DEVICE);
    if (!masterSinkDev.isEmpty()) {
        int cardIndex = findPortSinkIndex(masterSinkDev);
        if (cardIndex != -1) {
            qDebug() << "findSinkMasterDeviceInfo" << sinkPortName << findSinkPortName(cardIndex);
            defaultOutputCard = findPortSinkIndex(masterSinkDev);
            return findSinkPortName(cardIndex);
        }
        else {
            qDebug() << "can't find masterDevice info in findSinkMasterDeviceInfo()";
            return "";
        }
    }
}


QString UkmediaVolumeControl::findMasterDeviceInfo(pa_proplist *proplist, const char *info_name)
{
    masterDevice = pa_proplist_gets(proplist,PA_PROP_DEVICE_MASTER_DEVICE);
    if (!masterDevice.isEmpty()) {
        int cardIndex = findPortSourceIndex(masterDevice);
        if (cardIndex != -1) {
            qDebug() << "findMasterDeviceInfo" << sourcePortName << findSourcePortName(cardIndex);
            defaultInputCard = findPortSourceIndex(masterDevice);
            return findSourcePortName(cardIndex);
        }
        else {
            qDebug() << "can't find masterDevice info in findMasterDeviceInfo()";
            return "";
        }
    }
}

/**
 * @brief UkmediaVolumeControl::refreshVolume
 * 发送信号刷新音量
 */
void UkmediaVolumeControl::refreshVolume(int soundType, int info_Vol, bool info_Mute)
{
    switch (soundType) {
    case SoundType::SINK:
        if(sinkMuted != info_Mute) {
            sinkMuted = info_Mute;
            Q_EMIT updateMute(sinkMuted);
        }

        if(sinkVolume != info_Vol) {
            sinkVolume = info_Vol;
            sinkMuted = info_Mute;
            Q_EMIT updateVolume(info_Vol);
        }
        break;

    case SoundType::SOURCE:
        if(sourceMuted != info_Mute) {
            sourceMuted = info_Mute;
            Q_EMIT updateSourceMute(sourceMuted);
        }

        if(sourceVolume != info_Vol) {
            sourceVolume = info_Vol;
            sourceMuted = info_Mute;
            Q_EMIT updateSourceVolume(info_Vol);
        }
        break;

    default:
        break;
    }
}


void UkmediaVolumeControl::sendOsdWidgetSignal(QString portName, QString description)
{
    if (portName == "histen-algo")
        return;

    if (osdFirstFlag) {
        m_description = description;
        osdFirstFlag = false;
        return;
    }

    if (portName != sinkPortName || (sinkPortName == portName && !m_description.contains(description) && !description.contains(m_description))) {
        QString iconStr = "";

        if (portName.contains("headphone", Qt::CaseInsensitive))
            iconStr = "audio-headphones-symbolic";
        else if (portName.contains("headset", Qt::CaseInsensitive))
            iconStr = "audio-headset-symbolic";
        else
            iconStr = "audio-volume-high-symbolic";

        Q_EMIT device_changed_signal(iconStr);
        qDebug() << "Send SIGNAL: osdWidgetSignal. Case: sink port has changed" << portName << sinkPortName << description << m_description;
    }

    m_description = description;
}
