/*
* Copyright (C) 2024, KylinSoft Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 <https://www.gnu.org/licenses/>.
*
* Authors: zhouyong <zhouyong01@kylinos.cn>
*
*/

#include "ukui-audio-sink-model.h"
#include <QDebug>

UKUIAudioSinkModel& UKUIAudioSinkModel::getInstance()
{
    static UKUIAudioSinkModel instance;
    return instance;
}

void UKUIAudioSinkModel::initData()
{

}

void UKUIAudioSinkModel::initSlots()
{

}

int UKUIAudioSinkModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_sinkInfos.size();
}

QVariant UKUIAudioSinkModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= m_sinkInfos.size())
    {
        qDebug() << __func__ << "Invaild index:" << index;
        return QVariant();
    }

    const SinkInfo& sinkInfo = m_sinkInfos.at(index.row());
    switch (role) {
    case ItemTypeRole: return sinkInfo.itemType;
    case TitleRole: return sinkInfo.title;
    case CardNameRole: return sinkInfo.cardName;
    case CardDescRole: return sinkInfo.cardDesc;
    case PortNameRole: return sinkInfo.portName;
    case PortLabelRole:return sinkInfo.portLabel;
    case PriorityRole:return sinkInfo.priority;
    case DirectionRole: return sinkInfo.direction;
    case AvailableRole: return sinkInfo.available;
    case PortEnabledRole: return sinkInfo.enabled;
    case PortTypeRole: return sinkInfo.type;
    case PortIconRole: return sinkInfo.portIcon;
    case IsActivePortRole: return sinkInfo.isActivePort;
    case StreamNameRole: return sinkInfo.streamName;
    case StreamIndexRole: return sinkInfo.streamIndex;
    case StreamVolumeRole: return sinkInfo.streamVolume;
    case StreamRole: return sinkInfo.streamRole;
    case StreamIconNameRole: return sinkInfo.streamIconName;
    default: return QVariant();
    }
}

QHash<int,QByteArray> UKUIAudioSinkModel::roleNames() const
{
    return {
        {ItemTypeRole, "itemType"},
        {TitleRole, "title"},
        {CardNameRole, "cardName"},
        {CardDescRole, "cardDesc"},
        {PortNameRole, "portName"},
        {PortLabelRole, "portLabel"},
        {PriorityRole, "priority"},
        {DirectionRole, "direction"},
        {AvailableRole, "available"},
        {PortEnabledRole, "portEnabled"},
        {PortTypeRole, "portType"},
        {PortIconRole, "portIcon"},
        {IsActivePortRole, "isActivePort"},
        {StreamNameRole, "streamName"},
        {StreamIndexRole, "streamIndex"},
        {StreamVolumeRole, "streamVolume"},
        {StreamRole, "streamRole"},
        {StreamIconNameRole, "streamIconName"}
    };
}

void UKUIAudioSinkModel::addDeviceHeader()
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_sinkInfos.append({HeaderOutput, QObject::tr("Output"), "", "", "", "", 0, 0, 0, true, -1, "", false,
                        "", 0, 0, "", ""});
    endInsertRows();
}

void UKUIAudioSinkModel::addEmptyDevice()
{
    qDebug() << __func__ << "enter insert 1 index:" << (m_sinkInfos.size() - 1);
    beginInsertRows(QModelIndex(), (m_sinkInfos.size() - 1), (m_sinkInfos.size() - 1));
    m_sinkInfos.insert(1, {EmptyDeviceItem, QObject::tr("No output sound card detected"), "", "", "", "", 0, 0, 0, true, -1, "", false,
                                                  "", 0, 0, "", ""});
    endInsertRows();
}

void UKUIAudioSinkModel::addDevice(ClientDeviceInfo deviceInfo)
{
    qDebug() << __func__ << QString("Add card: %1 port: %2 to device list").arg(deviceInfo.cardName).arg(deviceInfo.portName);
    if ((m_sinkInfos.size() - 1) < 0) {
        qDebug() << __func__ << "m_sinkInfos.size invaild";
        return;
    }

    SinkInfo sinkInfo;
    deviceInfo >> sinkInfo;

    if (sinkInfo.portName.contains(QString("headphone"), Qt::CaseInsensitive)) {
        sinkInfo.portIcon = "audio-headphones-symbolic";
    } else if (sinkInfo.portName.contains(QString("headset"), Qt::CaseInsensitive)) {
        sinkInfo.portIcon = "audio-headset-symbolic";
    } else {
        sinkInfo.portIcon = "audio-speakers-symbolic";
    }

    beginInsertRows(QModelIndex(), getInsertDeviceIndex(), getInsertDeviceIndex());
    m_sinkInfos.insert(getInsertDeviceIndex(), sinkInfo);
    endInsertRows();
}

void UKUIAudioSinkModel::addDevice(ClientDefaultDeviceInfo deviceInfo)
{
    qDebug() << __func__ << QString("Add card: %1 port: %2 to device list").arg(deviceInfo.cardName).arg(deviceInfo.activePortName);
    if ((m_sinkInfos.size() - 1) < 0) {
        qDebug() << __func__ << "m_sinkInfos.size invaild";
        return;
    }

    SinkInfo sinkInfo;
    deviceInfo >> sinkInfo;

    if (sinkInfo.portName.contains(QString("headphone"), Qt::CaseInsensitive)) {
        sinkInfo.portIcon = "audio-headphones-symbolic";
    } else if (sinkInfo.portName.contains(QString("headset"), Qt::CaseInsensitive)) {
        sinkInfo.portIcon = "audio-headset-symbolic";
    } else if (sinkInfo.portName.contains(QString("combine"), Qt::CaseInsensitive)) {
        sinkInfo.portIcon = "audio-speakers-bluetooth-symbolic";
    } else {
        sinkInfo.portIcon = "audio-speakers-symbolic";
    }

    beginInsertRows(QModelIndex(), getInsertDeviceIndex(), getInsertDeviceIndex());
    m_sinkInfos.insert(getInsertDeviceIndex(), sinkInfo);
    endInsertRows();
}

bool UKUIAudioSinkModel::setDeviceActivePort(QString portName, QString cardName)
{
    qDebug() << __func__ << "enter" << "portName:" << portName << "cardName:" << cardName;
    bool ret = false;
    int index = 0;
    beginResetModel();
    for (; index < m_sinkInfos.size(); ++index) {
        if (m_sinkInfos.at(index).itemType != DeviceItem)
        {
            continue;
        }

//        qDebug() << __func__ << "index:" << index << "cardName:" << m_sinkInfos.at(index).cardName
//                 << "portName:" << m_sinkInfos.at(index).portName;
        if ((m_sinkInfos.at(index).cardName.compare(cardName) == 0)
                && (m_sinkInfos.at(index).portName.compare(portName) == 0))
        {
            m_sinkInfos[index].isActivePort = true;
            QModelIndex topLeft = createIndex(index, 0, nullptr);
            QModelIndex bottomRight = createIndex(index, 0, nullptr);
//            emit dataChanged(topLeft, bottomRight);
            ret = true;
        }
        else
        {
            m_sinkInfos[index].isActivePort = false;
            QModelIndex topLeft = createIndex(index, 0, nullptr);
            QModelIndex bottomRight = createIndex(index, 0, nullptr);
//            emit dataChanged(topLeft, bottomRight);
        }
    }
    endResetModel();

    return ret;
}

void UKUIAudioSinkModel::removeDevice(int index)
{
    if (index < 0 || index >= m_sinkInfos.size())
    {
        qDebug() << __func__ << "index is invaild" << index;
        return;
    }

    if (m_sinkInfos.at(index).itemType != DeviceItem)
    {
        qDebug() << __func__ << "this index no DeviceItem" << index;
        return;
    }

    qDebug() << __func__ << QString("Remove %1 device.").arg(index);
    beginRemoveRows(QModelIndex(), index, index);
    m_sinkInfos.removeAt(index);
    endRemoveRows();
}

void UKUIAudioSinkModel::clearDevice()
{
    int index = m_sinkInfos.size() - 1;
    for (; index >= 0; --index) {
//        qDebug() << __func__ << "index:" << index << "itemType:" << m_sinkInfos.at(index).itemType << "size:" << m_sinkInfos.size();
        if (m_sinkInfos.at(index).itemType == DeviceItem
                || m_sinkInfos.at(index).itemType == EmptyDeviceItem)
        {
            beginRemoveRows(QModelIndex(), index, index);
            m_sinkInfos.removeAt(index);
            endRemoveRows();
        }
    }
}

int UKUIAudioSinkModel::getDeviceCount()
{
    int index = 0;
    int count = 0;
    for (; index < m_sinkInfos.size(); ++index) {
        if (m_sinkInfos.at(index).itemType == DeviceItem)
        {
            count++;
        }
    }
    qDebug() << __func__ << "count:" << count;

    return count;
}

int UKUIAudioSinkModel::getEnabledDeviceCount()
{
    int index = 0;
    int count = 0;
    for (; index < m_sinkInfos.size(); ++index) {
        if ((m_sinkInfos.at(index).itemType == DeviceItem)
            && (m_sinkInfos.at(index).enabled))
        {
            count++;
        }
    }
    qDebug() << __func__ << "count:" << count;

    return count;
}

int UKUIAudioSinkModel::getInsertDeviceIndex()
{
    int index = 1;
    for (; index < m_sinkInfos.size(); ++index) {
        if (m_sinkInfos.at(index).itemType == HeaderAppVolume)
        {
            break;
        }
    }
//    qDebug() << __func__ << "index:" << index;

    return index;
}

void UKUIAudioSinkModel::addStreamHeader()
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_sinkInfos.append({HeaderAppVolume, QObject::tr("Application volume"), "", "", "", "", 0, 0, 0, true, -1, "", false,
                        "", 0, 0, "", ""});
    endInsertRows();
}

void UKUIAudioSinkModel::addEmptyStream()
{
    qDebug() << __func__ << "enter index:" << m_sinkInfos.size();
    beginInsertRows(QModelIndex(), m_sinkInfos.size(), m_sinkInfos.size());
    m_sinkInfos.insert(m_sinkInfos.size(), {EmptyStreamItem, QObject::tr("There are currently no applications that play sound"), "", "", "", "", 0, 0, 0, true, -1, "", false,
                                                  "", 0, 0, "", ""});
    endInsertRows();
}

void UKUIAudioSinkModel::addStream(StreamInfo streamInfo)
{
    qDebug() << __func__ << "enter streamInfo.name:" << streamInfo.name << "streamInfo.index:" << streamInfo.index
             << "streamInfo.volume:" << streamInfo.volume << "streamInfo.role:" << streamInfo.role
             << "streamInfo.iconName:" << streamInfo.iconName;
    if (m_sinkInfos.size() <= 0) {
        qDebug() << __func__ << "m_sinkInfos.size invaild";
        return;
    }

    if ((streamInfo.role.compare(QString("filter")) == 0) ||
        (streamInfo.role.compare(QString("abstract")) == 0) ||
        (streamInfo.role.compare(QString("event")) == 0)) {
        qDebug() << __func__ << streamInfo.role << "abandoned";
        return;
    }

    SinkInfo sinkInfo;
    streamInfo >> sinkInfo;

    beginInsertRows(QModelIndex(), m_sinkInfos.size(), m_sinkInfos.size());
    m_sinkInfos.insert(m_sinkInfos.size(), sinkInfo);
    endInsertRows();
}

void UKUIAudioSinkModel::removeStream(int index)
{
    if (index < 0 || index >= m_sinkInfos.size())
    {
        qDebug() << __func__ << "index is invaild" << index;
        return;
    }

    if (m_sinkInfos.at(index).itemType != StreamItem)
    {
        qDebug() << __func__ << "this index no StreamItem" << index;
        return;
    }

    beginRemoveRows(QModelIndex(), index, index);
    m_sinkInfos.removeAt(index);
    endRemoveRows();
}

void UKUIAudioSinkModel::clearStream()
{
    int index = m_sinkInfos.size() - 1;
    for (; index >= 0; --index) {
//        qDebug() << __func__ << "index:" << index << "itemType:" << m_sinkInfos.at(index).itemType << "size:" << m_sinkInfos.size();
        if (m_sinkInfos.at(index).itemType == StreamItem
                || m_sinkInfos.at(index).itemType == EmptyStreamItem)
        {
            beginRemoveRows(QModelIndex(), index, index);
            m_sinkInfos.removeAt(index);
            endRemoveRows();
        }
    }
}

int UKUIAudioSinkModel::getStreamCount()
{
    int index = 0;
    int count = 0;
    for (; index < m_sinkInfos.size(); ++index) {
        if (m_sinkInfos.at(index).itemType == StreamItem)
        {
            count++;
        }
    }
    qDebug() << __func__ << "count:" << count;

    return count;
}

int UKUIAudioSinkModel::getStreamIndexByStreamIconName(QString iconName)
{
    int index = 0;
    int streamIndex = -1;
    for (; index < m_sinkInfos.size(); ++index) {
        if (m_sinkInfos.at(index).itemType == StreamItem
            && m_sinkInfos.at(index).streamIconName.compare(iconName) == 0)
        {
            streamIndex = m_sinkInfos.at(index).streamIndex;
            break;
        }
    }
    qDebug() << __func__ << "streamIndex:" << streamIndex;

    return streamIndex;
}

void UKUIAudioSinkModel::releaseModel()
{
    qDebug() << "UKUIAudioSinkModel::releaseModel" << "enter";
//    beginResetModel();
    m_sinkInfos.clear();
//    endResetModel();
    qDebug() << "UKUIAudioSinkModel::releaseModel" << "leave";
}

UKUIAudioSinkModel::~UKUIAudioSinkModel()
{
    qDebug() << __func__ << "enter";
    releaseModel();
}
