/*
 * Fooyin
 * Copyright © 2024, Luke Taylor <luket@pm.me>
 *
 * Fooyin 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.
 *
 * Fooyin 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 Fooyin.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "lyricsource.h"

#include <core/coresettings.h>
#include <utils/stringutils.h>

#include <QIODevice>
#include <QJsonObject>
#include <QJsonParseError>
#include <QNetworkReply>
#include <QStringDecoder>

Q_LOGGING_CATEGORY(LYRICS, "fy.lyrics")

using namespace Qt::StringLiterals;

namespace Fooyin::Lyrics {
namespace {
Utils::DetectEncodingOptions detectEncodingOptions()
{
    const FySettings settings;
    return {.preferredFallbackEncoding
            = settings.value(QString::fromLatin1(Utils::PreferredFallbackEncodingSetting)).toString().toLatin1()};
}
} // namespace

LyricSource::LyricSource(NetworkAccessManager* network, SettingsManager* settings, int index, bool enabled,
                         QObject* parent)
    : QObject{parent}
    , m_network{network}
    , m_settings{settings}
    , m_index{index}
    , m_enabled{enabled}
{ }

int LyricSource::index() const
{
    return m_index;
}

bool LyricSource::enabled() const
{
    return m_enabled;
}

bool LyricSource::isLocal() const
{
    return false;
}

void LyricSource::cancel()
{
    resetReply();
}

void LyricSource::setIndex(int index)
{
    m_index = index;
}

void LyricSource::setEnabled(bool enabled)
{
    m_enabled = enabled;
}

NetworkAccessManager* LyricSource::network() const
{
    return m_network;
}

SettingsManager* LyricSource::settings() const
{
    return m_settings;
}

QString LyricSource::toUtf8(QIODevice* file)
{
    const QByteArray data = file->readAll();
    if(data.isEmpty()) {
        return {};
    }

    QStringDecoder toUtf16;

    auto encoding = QStringConverter::encodingForData(data);
    if(encoding) {
        toUtf16 = QStringDecoder{encoding.value()};
    }
    else {
        const auto encodingName = Utils::detectEncoding(data, detectEncodingOptions());
        if(encodingName.isEmpty()) {
            return {};
        }

        encoding = QStringConverter::encodingForName(encodingName.constData());
        if(encoding) {
            toUtf16 = QStringDecoder{encoding.value()};
        }
        else {
            toUtf16 = QStringDecoder{encodingName.constData()};
        }
    }

    if(!toUtf16.isValid()) {
        toUtf16 = QStringDecoder{QStringConverter::Utf8};
    }

    QString string = toUtf16(data);
    string.replace("\n\n"_L1, "\n"_L1);
    string.replace('\r'_L1, '\n'_L1);

    return string;
}

QString LyricSource::encode(const QString& str)
{
    return QString::fromLatin1(QUrl::toPercentEncoding(str));
}

bool LyricSource::getJsonFromReply(QNetworkReply* reply, QJsonObject* obj)
{
    bool success{false};

    if(reply->error() == QNetworkReply::NoError) {
        if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
            success = true;
        }
        else {
            qCDebug(LYRICS) << u"Received HTTP code %1"_s.arg(
                reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
        }
    }
    else {
        qCDebug(LYRICS) << u"%1 (%2)"_s.arg(reply->errorString()).arg(reply->error());
    }

    if(reply->error() == QNetworkReply::NoError || reply->error() >= 200) {
        const QByteArray data = reply->readAll();
        if(data.isEmpty() || !extractJsonObj(data, obj)) {
            success = false;
        }
    }

    return success;
}

bool LyricSource::extractJsonObj(const QByteArray& data, QJsonObject* obj)
{
    QJsonParseError error;
    const QJsonDocument json = QJsonDocument::fromJson(data, &error);

    if(error.error != QJsonParseError::NoError) {
        qCInfo(LYRICS) << error.errorString();
        return false;
    }

    if(json.isObject()) {
        *obj = json.object();
    }

    return true;
}

QNetworkReply* LyricSource::reply() const
{
    return m_reply;
}

void LyricSource::setReply(QNetworkReply* reply)
{
    m_reply = reply;
}

void LyricSource::resetReply()
{
    if(m_reply) {
        QObject::disconnect(m_reply, nullptr, nullptr, nullptr);
        m_reply->abort();
        m_reply->deleteLater();
    }
}
} // namespace Fooyin::Lyrics
