/*
    SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/


#include "opships.h"

#include "kstars.h"
#include "hipsmanager.h"
#include "Options.h"
#include "skymap.h"
#include "auxiliary/ksnotification.h"
#include "auxiliary/filedownloader.h"
#include "auxiliary/kspaths.h"

#include <KConfigDialog>

#include <QCheckBox>
#include <QComboBox>
#include <QFileDialog>
#include <QPushButton>
#include <QStringList>

// Qt version calming
#include <qtkeepemptyparts.h>

static const QStringList hipsKeys = { "ID", "obs_title", "obs_description", "hips_order", "hips_frame", "hips_tile_width", "hips_tile_format", "hips_service_url", "moc_sky_fraction"};

OpsHIPSDisplay::OpsHIPSDisplay() : QFrame(KStars::Instance())
{
    setupUi(this);
}

OpsHIPSCache::OpsHIPSCache() : QFrame(KStars::Instance())
{
    setupUi(this);

    connect(selectDirectoryB, &QPushButton::clicked, this, [this]()
    {
        QString dir = QFileDialog::getExistingDirectory(this, i18nc("@title:window", "HiPS Offline Storage"),
                      kcfg_HIPSOfflinePath->text());

        if (dir.isEmpty())
            return;

        kcfg_HIPSOfflinePath->setText(dir);

        QDir hipsDirectory(dir);
        auto orders = hipsDirectory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
        HIPSManager::Instance()->setOfflineLevels(orders);
        HIPSManager::Instance()->setCurrentSource("DSS Colored");
    });
}

OpsHIPS::OpsHIPS() : QFrame(KStars::Instance())
{
    setupUi(this);

    //Get a pointer to the KConfigDialog
    m_ConfigDialog = KConfigDialog::exists("hipssettings");

    QString path = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(
                       QLatin1String("hips_previews/"));
    QDir dir;
    dir.mkpath(path);

    connect(refreshSourceB, &QPushButton::clicked, this, &OpsHIPS::slotRefresh);

    connect(sourcesList, &QListWidget::itemChanged, this, &OpsHIPS::slotItemUpdated);
    connect(sourcesList, &QListWidget::itemClicked, this, &OpsHIPS::slotItemClicked);
    connect(filterEdit, &QLineEdit::textChanged, this, &OpsHIPS::slotFilterChanged);

    if (sourcesList->count() == 0)
        slotRefresh();
}

void OpsHIPS::slotRefresh()
{
    downloadJob = new FileDownloader();

    downloadJob->setProgressDialogEnabled(true, i18n("HiPS Update"), i18n("Downloading HiPS sources..."));

    connect(downloadJob, &FileDownloader::downloaded, this, &OpsHIPS::downloadReady);
    connect(downloadJob, &FileDownloader::error, this, &OpsHIPS::downloadError);

    downloadJob->get(
        QUrl("http://alasky.unistra.fr/MocServer/query?hips_service_url=*&dataproduct_type=!catalog&dataproduct_type=!cube&get=record"));
}

void OpsHIPS::downloadReady()
{
    sources.clear();

    QTextStream stream(downloadJob->downloadedData());

    QStringList hipsTitles;

    QMap<QString, QString> oneSource;
    while (stream.atEnd() == false)
    {
        QString line = stream.readLine();
        if (line.isEmpty())
        {
            sources.append(oneSource);
            oneSource.clear();
            continue;
        }

        QStringList keyvalue = line.split('=', Qt::KeepEmptyParts);
        QString key   = keyvalue[0].simplified();
        if (hipsKeys.contains(key) == false)
            continue;
        QString value = keyvalue[1].simplified();
        oneSource[key] = value;
        if (key == "obs_title")
            hipsTitles << value;
    }

    // Get existing database sources
    QList<QMap<QString, QString>> dbSources;
    KStarsData::Instance()->userdb()->GetAllHIPSSources(dbSources);

    // Get existing database titles
    QStringList dbTitles;
    for (QMap<QString, QString> oneSource : dbSources)
        dbTitles << oneSource["obs_title"];

    // Sort titles alphabetically (case-insensitive)
    hipsTitles.sort(Qt::CaseInsensitive);
    // Add all titles to list widget
    sourcesList->addItems(hipsTitles);
    QListWidgetItem* item = nullptr;

    // Make sources checkable and check sources that already exist in the database
    sourcesList->blockSignals(true);
    for(int i = 0; i < sourcesList->count(); ++i)
    {
        item = sourcesList->item(i);
        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
        item->setCheckState(dbTitles.contains(item->text()) ? Qt::Checked : Qt::Unchecked);

        if (item->text() == Options::hIPSSource())
        {
            item->setSelected(true);
            sourcesList->scrollToItem(item);
            slotItemClicked(item);
        }
    }
    sourcesList->blockSignals(false);

    // Delete job later
    downloadJob->deleteLater();
}

void OpsHIPS::downloadError(const QString &errorString)
{
    KSNotification::error(i18n("Error downloading HiPS sources: %1", errorString));
    downloadJob->deleteLater();
}

void OpsHIPS::slotItemUpdated(QListWidgetItem *item)
{
    for(QMap<QString, QString> &oneSource : sources)
    {
        if (oneSource.value("obs_title") == item->text())
        {
            if (item->checkState() == Qt::Checked)
                KStarsData::Instance()->userdb()->AddHIPSSource(oneSource);
            else
                KStarsData::Instance()->userdb()->DeleteHIPSSource(oneSource.value("ID"));
            break;
        }
    }
}

void OpsHIPS::slotItemClicked(QListWidgetItem *item)
{
    for(QMap<QString, QString> &oneSource : sources)
    {
        if (oneSource.value("obs_title") == item->text())
        {
            sourceDescription->setText(oneSource.value("obs_description"));
            // Get stored preview, if not found, it will be downloaded.
            setPreview(oneSource.value("ID"), oneSource.value("hips_service_url"));
            break;
        }
    }
}

void OpsHIPS::setPreview(const QString &id, const QString &url)
{
    uint hash = qHash(id);
    QString previewName = QString("%1.jpg").arg(hash);

    QString currentPreviewPath = QDir(KSPaths::locate(QStandardPaths::AppLocalDataLocation,
                                      QLatin1String("hips_previews"))).filePath(previewName);
    if (currentPreviewPath.isEmpty() == false)
    {
        sourceImage->setPixmap(QPixmap(currentPreviewPath));
    }
    else
    {
        currentPreviewPath = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(
                                 QLatin1String("hips_previews/") + previewName);

        previewJob = new FileDownloader();
        connect(previewJob, &FileDownloader::downloaded, this, &OpsHIPS::previewReady);

        previewJob->setDownloadedFileURL(QUrl::fromLocalFile(currentPreviewPath));

        previewJob->get(QUrl(url + QLatin1String("/preview.jpg")));
    }
}

void OpsHIPS::previewReady()
{
    QString previewFile = previewJob->getDownloadedFileURL().toLocalFile();
    QFileInfo previewInfo(previewFile);
    // If less than 1kb then it's junk
    if (previewInfo.size() < 1024)
    {
        sourceImage->setPixmap(QPixmap(":/images/noimage.png"));
        QFile::remove(previewFile);
    }
    else
        sourceImage->setPixmap(QPixmap(previewFile));
}

void OpsHIPS::slotFilterChanged(const QString &text)
{
    // Filter the list based on the search text
    QString filterText = text.trimmed();

    for(int i = 0; i < sourcesList->count(); ++i)
    {
        QListWidgetItem* item = sourcesList->item(i);
        if (item)
        {
            // Show item if filter is empty or if item text contains filter text (case-insensitive)
            bool matches = filterText.isEmpty() || item->text().contains(filterText, Qt::CaseInsensitive);
            item->setHidden(!matches);
        }
    }
}
