/***************************************************************************
                          opscatalog.cpp  -  K Desktop Planetarium
                             -------------------
    begin                : Sun Feb 29  2004
    copyright            : (C) 2004 by Jason Harris
    email                : jharris@30doradus.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "opscatalog.h"

#include "kstars.h"
#include "kstarsdata.h"
#include "Options.h"
#include "skymap.h"
#include "dialogs/addcatdialog.h"
#include "skycomponents/catalogcomponent.h"
#include "skycomponents/skymapcomposite.h"
#include "widgets/magnitudespinbox.h"

#include <KActionCollection>
#include <KConfigDialog>

#include <QList>
#include <QListWidgetItem>
#include <QTextStream>
#include <QFileDialog>

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

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

    //Populate CatalogList
    populateInbuiltCatalogs();

    m_ShowMessier    = Options::showMessier();
    m_ShowMessImages = Options::showMessierImages();
    m_ShowNGC        = Options::showNGC();
    m_ShowIC         = Options::showIC();

    //    kcfg_MagLimitDrawStar->setValue( Options::magLimitDrawStar() );
    kcfg_StarDensity->setValue(Options::starDensity());
    //    kcfg_MagLimitDrawStarZoomOut->setValue( Options::magLimitDrawStarZoomOut() );
    //    m_MagLimitDrawStar = kcfg_MagLimitDrawStar->value();
    m_StarDensity = kcfg_StarDensity->value();
    //    m_MagLimitDrawStarZoomOut = kcfg_MagLimitDrawStarZoomOut->value();

    //    kcfg_MagLimitDrawStar->setMinimum( Options::magLimitDrawStarZoomOut() );
    //    kcfg_MagLimitDrawStarZoomOut->setMaximum( 12.0 );

    kcfg_MagLimitDrawDeepSky->setMaximum(16.0);
    kcfg_MagLimitDrawDeepSkyZoomOut->setMaximum(16.0);

    //disable star-related widgets if not showing stars
    if (!kcfg_ShowStars->isChecked())
        slotStarWidgets(false);

    //Add custom catalogs, if necessary
    /*
     * 1) Get the list from DB and add it as unchecked
     * 2) If the showCatalogNames list has any of the items, check it
    */
    m_CustomCatalogFile   = KStars::Instance()->data()->catalogdb()->Catalogs();
    m_CheckedCatalogNames = Options::showCatalogNames();
    populateCustomCatalogs();

    connect(CatalogList, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(updateCustomCatalogs()));
    connect(CatalogList, SIGNAL(itemSelectionChanged()), this, SLOT(selectCatalog()));
    connect(AddCatalog, SIGNAL(clicked()), this, SLOT(slotAddCatalog()));
    connect(LoadCatalog, SIGNAL(clicked()), this, SLOT(slotLoadCatalog()));
    connect(RemoveCatalog, SIGNAL(clicked()), this, SLOT(slotRemoveCatalog()));

    /*
    connect( kcfg_MagLimitDrawStar, SIGNAL(valueChanged(double)),
             SLOT(slotSetDrawStarMagnitude(double)) );
    connect( kcfg_MagLimitDrawStarZoomOut, SIGNAL(valueChanged(double)),
             SLOT(slotSetDrawStarZoomOutMagnitude(double)) );
    */
    connect(kcfg_ShowStars, SIGNAL(toggled(bool)), SLOT(slotStarWidgets(bool)));
    connect(kcfg_ShowDeepSky, SIGNAL(toggled(bool)), SLOT(slotDeepSkyWidgets(bool)));
    connect(kcfg_ShowDeepSkyNames, SIGNAL(toggled(bool)), kcfg_DeepSkyLongLabels, SLOT(setEnabled(bool)));
    connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply()));
    connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply()));
    connect(m_ConfigDialog->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(slotCancel()));

    // Keep track of changes
    connect(CatalogList, &QListWidget::itemChanged, this, [&]()
    {
        isDirty = true;
    });
    connect(catalogButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonPressed), this,
            [&]()
    {
        isDirty = true;
    });

    isDirty = false;
}

void OpsCatalog::updateCustomCatalogs()
{
    m_ShowMessier    = showMessier->checkState();
    m_ShowMessImages = showMessImages->checkState();
    m_ShowNGC        = showNGC->checkState();
    m_ShowIC         = showIC->checkState();

    int limit = m_CustomCatalogFile->size();
    for (int i = 0; i < limit; ++i)
    {
        QString name               = m_CustomCatalogFile->at(i);
        QList<QListWidgetItem *> l = CatalogList->findItems(name, Qt::MatchExactly);

        /*
         * Options::CatalogNames contains the list of those custom catalog
         * names which are to be checked.
         * For every checked item, we check if the option CatalogNames has
         * the name. If not, we add it.
         * For every unchecked item, we check if the option CatalogNames does
         * not contain the name. If it does, we remove it.
        */
        if (l.isEmpty())
            continue; // skip the name if no match found
        if (l[0]->checkState() == Qt::Checked)
        {
            if (!m_CheckedCatalogNames.contains(name))
            {
                m_CheckedCatalogNames.append(name);
                //isDirty = true;
            }
        }
        else if (l[0]->checkState() == Qt::Unchecked)
        {
            if (m_CheckedCatalogNames.contains(name))
            {
                m_CheckedCatalogNames.removeAll(name);
                //isDirty = true;
            }
        }
    }

    m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false);
}

void OpsCatalog::selectCatalog()
{
    //If selected item is a custom catalog, enable the remove button (otherwise, disable it)
    RemoveCatalog->setEnabled(false);

    if (!CatalogList->currentItem())
        return;

    //isDirty = true;

    for (auto sc : KStars::Instance()->data()->skyComposite()->customCatalogs())
    {
        CatalogComponent *cc = dynamic_cast<CatalogComponent *>(sc);
        if (CatalogList->currentItem()->text() == cc->name())
        {
            RemoveCatalog->setEnabled(true);
            break;
        }
    }
}

void OpsCatalog::slotAddCatalog()
{
    QPointer<AddCatDialog> ac = new AddCatDialog(KStars::Instance());
    if (ac->exec() == QDialog::Accepted)
    {
        KStars::Instance()->data()->catalogdb()->AddCatalogContents(ac->filename());
        refreshCatalogList();
        isDirty = true;
    }
    delete ac;
}

void OpsCatalog::slotLoadCatalog()
{
    //Get the filename from the user
    QString filename = QFileDialog::getOpenFileName(KStars::Instance(), QString(), QDir::homePath(), "*");
    if (!filename.isEmpty())
    {
        KStars::Instance()->data()->catalogdb()->AddCatalogContents(filename);
        isDirty = true;
        refreshCatalogList();
    }

    m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false);
}

void OpsCatalog::refreshCatalogList()
{
    KStars::Instance()->data()->catalogdb()->Catalogs();
    populateCustomCatalogs();
}

void OpsCatalog::slotRemoveCatalog()
{
    if (KMessageBox::warningYesNo(
                nullptr, i18n("The selected database will be removed. This action cannot be reversed! Delete Catalog?"),
                i18n("Delete Catalog?")) == KMessageBox::No)
    {
        return;
    }

    isDirty = true;

    //Ask DB to remove catalog
    KStars::Instance()->data()->catalogdb()->RemoveCatalog(CatalogList->currentItem()->text());

    // Remove from Options if it exists in it (i.e. was marked as visible)
    // This does not remove it from the database or from the widget in Options
    QList<QString> checkedlist = Options::showCatalogNames();
    if (checkedlist.contains(CatalogList->currentItem()->text()))
    {
        checkedlist.removeAll(CatalogList->currentItem()->text());
        Options::setShowCatalogNames(checkedlist);
    }

    //Remove entry in the QListView
    QListWidgetItem *todelete = CatalogList->takeItem(CatalogList->row(CatalogList->currentItem()));
    delete todelete;
    refreshCatalogList();
    m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false);
    KStars::Instance()->data()->skyComposite()->reloadDeepSky();
}

/*
void OpsCatalog::slotSetDrawStarMagnitude(double newValue) {
    m_MagLimitDrawStar = newValue;
    kcfg_MagLimitDrawStarZoomOut->setMaximum( newValue );
    m_ConfigDialog->enableButtonApply( true );
}

void OpsCatalog::slotSetDrawStarZoomOutMagnitude(double newValue) {
    m_MagLimitDrawStarZoomOut = newValue;
    kcfg_MagLimitDrawStar->setMinimum( newValue );
    m_ConfigDialog->enableButtonApply( true );
}
*/

void OpsCatalog::slotApply()
{
    if (isDirty == false)
        return;

    isDirty = false;

    refreshCatalogList();

    Options::setStarDensity(kcfg_StarDensity->value());
    //    Options::setMagLimitDrawStarZoomOut( kcfg_MagLimitDrawStarZoomOut->value() );

    //FIXME: need to add the ShowDeepSky meta-option to the config dialog!
    //For now, I'll set showDeepSky to true if any catalog options changed
    if (m_ShowMessier != Options::showMessier() || m_ShowMessImages != Options::showMessierImages() ||
            m_ShowNGC != Options::showNGC() || m_ShowIC != Options::showIC())
    {
        Options::setShowDeepSky(true);
    }

    updateCustomCatalogs();

    KStars::Instance()->data()->skyComposite()->reloadDeepSky();

    // update time for all objects because they might be not initialized
    // it's needed when using horizontal coordinates
    KStars::Instance()->data()->setFullTimeUpdate();
    KStars::Instance()->updateTime();
    KStars::Instance()->map()->forceUpdate();

    Options::setShowCatalogNames(m_CheckedCatalogNames);

    Options::setShowMessier(m_ShowMessier);
    Options::setShowMessierImages(m_ShowMessImages);
    Options::setShowNGC(m_ShowNGC);
    Options::setShowIC(m_ShowIC);
}

void OpsCatalog::slotCancel()
{
    //Revert all local option placeholders to the values in the global config object
    //    m_MagLimitDrawStar = Options::magLimitDrawStar();
    m_StarDensity = Options::starDensity();
    //    m_MagLimitDrawStarZoomOut = Options::magLimitDrawStarZoomOut();

    m_ShowMessier    = Options::showMessier();
    m_ShowMessImages = Options::showMessierImages();
    m_ShowNGC        = Options::showNGC();
    m_ShowIC         = Options::showIC();

    m_ShowCustomCatalog = Options::showCatalog();
}

void OpsCatalog::slotStarWidgets(bool on)
{
    //    LabelMagStars->setEnabled(on);
    LabelStarDensity->setEnabled(on);
    //    LabelMagStarsZoomOut->setEnabled(on);
    LabelDensity->setEnabled(on);
    //    LabelMag1->setEnabled(on);
    //    LabelMag2->setEnabled(on);
    //    kcfg_MagLimitDrawStar->setEnabled(on);
    kcfg_StarDensity->setEnabled(on);
    LabelStarDensity->setEnabled(on);
    //    kcfg_MagLimitDrawStarZoomOut->setEnabled(on);
    kcfg_StarLabelDensity->setEnabled(on);
    kcfg_ShowStarNames->setEnabled(on);
    kcfg_ShowStarMagnitudes->setEnabled(on);
}

void OpsCatalog::slotDeepSkyWidgets(bool on)
{
    CatalogList->setEnabled(on);
    AddCatalog->setEnabled(on);
    LoadCatalog->setEnabled(on);
    LabelMagDeepSky->setEnabled(on);
    LabelMagDeepSkyZoomOut->setEnabled(on);
    kcfg_MagLimitDrawDeepSky->setEnabled(on);
    kcfg_MagLimitDrawDeepSkyZoomOut->setEnabled(on);
    kcfg_ShowDeepSkyNames->setEnabled(on);
    kcfg_ShowDeepSkyMagnitudes->setEnabled(on);
    kcfg_DeepSkyLabelDensity->setEnabled(on);
    kcfg_DeepSkyLongLabels->setEnabled(on);
    LabelMag3->setEnabled(on);
    LabelMag4->setEnabled(on);
    if (on)
    {
        //Enable RemoveCatalog if the selected catalog is custom
        selectCatalog();
    }
    else
    {
        RemoveCatalog->setEnabled(on);
    }
}

void OpsCatalog::populateInbuiltCatalogs()
{
    showIC = new QListWidgetItem(i18n("Index Catalog (IC)"), CatalogList);
    showIC->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    showIC->setCheckState(Options::showIC() ? Qt::Checked : Qt::Unchecked);

    showNGC = new QListWidgetItem(i18n("New General Catalog (NGC)"), CatalogList);
    showNGC->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    showNGC->setCheckState(Options::showNGC() ? Qt::Checked : Qt::Unchecked);

    showMessImages = new QListWidgetItem(i18n("Messier Catalog (images)"), CatalogList);
    showMessImages->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    showMessImages->setCheckState(Options::showMessierImages() ? Qt::Checked : Qt::Unchecked);

    showMessier = new QListWidgetItem(i18n("Messier Catalog (symbols)"), CatalogList);
    showMessier->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    showMessier->setCheckState(Options::showMessier() ? Qt::Checked : Qt::Unchecked);
}

void OpsCatalog::populateCustomCatalogs()
{
    QStringList toggleNames = Options::showCatalogNames();
    QStringList customList  = *m_CustomCatalogFile; // Create a copy
    QStringListIterator catalogIter(customList);

    while (catalogIter.hasNext())
    {
        QString catalogname = catalogIter.next();
        //Skip already existing items
        if (CatalogList->findItems(catalogname, Qt::MatchExactly).length() > 0)
            continue;

        //Allocate new catalog list item
        QListWidgetItem *newItem = new QListWidgetItem(catalogname, CatalogList);
        newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);

        if (toggleNames.contains(catalogname))
        {
            newItem->setCheckState(Qt::Checked);
        }
        else
        {
            newItem->setCheckState(Qt::Unchecked);
        }
    }
}
