#include "co009.h"
#include "streamunlimitedrequest.h"

#include <QJsonDocument>
#include <QLoggingCategory>

Q_DECLARE_LOGGING_CATEGORY(dcTrinod);

const QHash<QString, QString> inputSourceMap = {
    {"airable", "Airable"},
    {"airableRadios", "Airable"},
    {"airablePodcasts", "Airable"},
    {"SPDIFIN", "Optical"},
    {"AUX", "Line in"},
    {"AUX2", "AUX"},
    {"bluetooth", "Bluetooth"},
    {"Chromecast", "Chromecast"}
};

Co009::Co009(NetworkAccessManager *nam, const QHash<QString, QUuid> &idMap, QObject *parent):
    StreamUnlimitedDevice(nam, idMap, "ui:", parent)
{
    connect(this, &StreamUnlimitedDevice::playerDataChanged, this, &Co009::onPlayerDataChanged);
}

QString Co009::inputSource() const
{
    return m_inputSource;
}

QUuid Co009::setInputSource(const QString &inputSource)
{
    QUuid commandId = QUuid::createUuid();

    if (inputSource == "Chromecast") {
        qCWarning(dcTrinod()) << "Can't activate chromecast manually. Start streaming from another device instead.";
        return QUuid();
    }

    const QHash<QString, QString> sourceMap = {
        { "Optical", "optical" },
        { "AUX", "aux" },
        { "Line in", "linein" },
        { "Airable", "airable" },
        { "Bluetooth", "bluetooth" },
        { "Chromecast", "chromecast" },
    };

    QString path = QString("trinodcob:playSource%3Fsource=%1").arg(sourceMap.value(inputSource)).toUtf8();
    QString role = "activate";
    QVariantMap params = QVariantMap();

    qCDebug(dcTrinod()) << "Selecting input source:" << path << role << params;
    StreamUnlimitedSetRequest *request = new StreamUnlimitedSetRequest(m_nam, m_address, m_port, path, role, params, this);
    connect(request, &StreamUnlimitedSetRequest::error, this, [=](const QNetworkReply::NetworkError &error) {
        qCWarning(dcTrinod()) << "selectSource error" << error;
        emit commandCompleted(commandId, false);
    });
    connect(request, &StreamUnlimitedSetRequest::finished, this, [=](const QByteArray &response) {
        qCDebug(dcTrinod()) << "Select source response" << response;
        emit commandCompleted(commandId, true);
    });
    return commandId;

}

bool Co009::isFavorite() const
{
    return m_favorite;
}

QUuid Co009::setFavorite(bool favorite)
{
    QUuid commandId = QUuid::createUuid();

    // First, get the current player data
    qCDebug(dcTrinod()) << "Fetching currently playing item";
    StreamUnlimitedGetRequest *request = new StreamUnlimitedGetRequest(m_nam, m_address, m_port, "player:player/data", {"value"}, this);
    connect(request, &StreamUnlimitedGetRequest::error, this, [=](){
        qCWarning(dcTrinod()) << "Error fetching current context";
        emit commandCompleted(commandId, false);
    });
    connect(request, &StreamUnlimitedGetRequest::finished, this, [=](const QVariantMap &result){
//            qCDebug(dcStreamUnlimited()) << "Current item result" << qUtf8Printable(QJsonDocument::fromVariant(result).toJson());

        // Once we have the player data, get the context for it
        QString contextPath = result.value("value").toMap().value("contextPath").toString();
        qCDebug(dcTrinod()) << "Fetching contextPath" << contextPath;
        StreamUnlimitedBrowseRequest *contextRequest = new StreamUnlimitedBrowseRequest(m_nam, m_address, m_port, contextPath, {
                                                                                            "path","id","title","icon","type","containerType","personType","albumType","imageType","audioType","videoType","epgType","modifiable","disabled","flags","value","valueOperation()","edit","mediaData","query","activate","likeIt","rowsOperation","setRoles","timestamp","valueUnit","context","description","longDescription","search","prePlay","activity","cancel","accept","risky","preferred","httpRequest","encrypted","encryptedValue","rating","fillParent","autoCompletePath","busyText","sortKey","renderAsButton","doNotTrack","persistentMetaData","containerPlayable","releaseDate"
                                                                                                    }, this);

        // If we fail, bail out
        connect(contextRequest, &StreamUnlimitedBrowseRequest::error, this, [=](){
            qCWarning(dcTrinod()) << "Error fetching context for item for current player item";
            emit commandCompleted(commandId, false);
        });

        // Context is here, find the action path.
        connect(contextRequest, &StreamUnlimitedBrowseRequest::finished, this, [=](const QVariantMap &contextResult){
            qCDebug(dcTrinod()) << "Context menu item" << qUtf8Printable(QJsonDocument::fromVariant(contextResult).toJson());
            QVariantList contextItems = contextResult.value("rows").toList();

            foreach (const QVariant &contextItem, contextItems) {
                QStringList roles = contextItem.toStringList();
                QString contextItemPath = roles.takeFirst();
                QString id = roles.takeFirst();

                if ((favorite && id == "airable://airable/action/favorite.insert")
                        || (!favorite && "airable://airable/action/favorite.remove")) {
                    // We've found it! Execute it!
                    StreamUnlimitedSetRequest *request = new StreamUnlimitedSetRequest(m_nam, m_address, m_port, contextItemPath, "activate", "true", this);
                    connect(request, &StreamUnlimitedSetRequest::error, this, [=](){
                        qCWarning(dcTrinod()) << "Failed to execute context menu action" << id;
                        emit commandCompleted(commandId, false);
                    });
                    connect(request, &StreamUnlimitedSetRequest::finished, this, [=](const QByteArray &data){
                        qCDebug(dcTrinod()) << "Context menu execution result:" << data;
                        QJsonParseError error;
                        QJsonDocument result = QJsonDocument::fromJson(data, &error);
                        bool status = error.error == QJsonParseError::NoError && !result.toVariant().toMap().contains("error");
                        emit commandCompleted(commandId, status);
                        m_favorite = favorite;
                        emit favoriteChanged(m_favorite);
                    });
                }
            }
        });
    });
    return commandId;
}

void Co009::onPlayerDataChanged(const QVariantMap &playerData)
{
    QString inputSource = playerData.value("value").toMap().value("mediaRoles").toMap().value("mediaData").toMap().value("metaData").toMap().value("serviceID").toString();
    if (inputSource.isEmpty()) {
        inputSource = playerData.value("value").toMap().value("trackRoles").toMap().value("mediaData").toMap().value("metaData").toMap().value("serviceID").toString();
    }
    if (!inputSourceMap.contains(inputSource)) {
        qCWarning(dcTrinod()) << "Unknown input source:" << inputSource;
        return;
    }
    qCDebug(dcTrinod()) << "Input source is" << inputSource;
    m_inputSource = inputSourceMap.value(inputSource);
    emit inputSourceChanged(m_inputSource);

    // Check the context menu if this is a favorite
    QString contextPath = playerData.value("value").toMap().value("contextPath").toString();
    StreamUnlimitedBrowseRequest *contextRequest = new StreamUnlimitedBrowseRequest(m_nam, m_address, m_port, contextPath, {
                                                                                        "path","id","title","icon","type","containerType","personType","albumType","imageType","audioType","videoType","epgType","modifiable","disabled","flags","value","valueOperation()","edit","mediaData","query","activate","likeIt","rowsOperation","setRoles","timestamp","valueUnit","context","description","longDescription","search","prePlay","activity","cancel","accept","risky","preferred","httpRequest","encrypted","encryptedValue","rating","fillParent","autoCompletePath","busyText","sortKey","renderAsButton","doNotTrack","persistentMetaData","containerPlayable","releaseDate"
                                                                                                }, this);
    connect(contextRequest, &StreamUnlimitedBrowseRequest::error, this, [=](){
        m_favorite = false;
        emit favoriteChanged(m_favorite);
    });

    connect(contextRequest, &StreamUnlimitedBrowseRequest::finished, this, [=](const QVariantMap &contextResult){
        qCDebug(dcTrinod()) << "Context menu item" << qUtf8Printable(QJsonDocument::fromVariant(contextResult).toJson());
        QVariantList contextItems = contextResult.value("rows").toList();

        m_favorite = false;
        foreach (const QVariant &contextItem, contextItems) {
            QStringList roles = contextItem.toStringList();
            QString contextItemPath = roles.takeFirst();
            QString id = roles.takeFirst();

            if (id == "airable://airable/action/favorite.remove") {
                m_favorite = true;
            }
        }
        qCDebug(dcTrinod()) << "Favorite is:" << m_favorite;
        emit favoriteChanged(m_favorite);
    });
}

