// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2010 Gaurav Gupta <1989.gaurav@googlemail.com>
// SPDX-FileCopyrightText: 2011 Friedrich W. H. Kossebau <kossebau@kde.org>
// SPDX-FileCopyrightText: 2012 Thibaut Gridel <tgridel@free.fr>
//

#include "EditBookmarkDialog.h"
#include "ui_EditBookmarkDialog.h"

#include "BookmarkManager.h"
#include "GeoDataCoordinates.h"
#include "GeoDataData.h"
#include "GeoDataDocument.h"
#include "GeoDataExtendedData.h"
#include "GeoDataFolder.h"
#include "GeoDataIconStyle.h"
#include "GeoDataLookAt.h"
#include "GeoDataPlacemark.h"
#include "GeoDataPoint.h"
#include "GeoDataStyle.h"
#include "MarbleDirs.h"
#include "MarbleModel.h"
#include "MarbleWidget.h"
#include "NewBookmarkFolderDialog.h"
#include "ReverseGeocodingRunnerManager.h"

#include <QPointer>

namespace Marble
{

class EditBookmarkDialogPrivate
{
public:
    MarbleWidget *m_widget;
    ReverseGeocodingRunnerManager *m_manager;
    BookmarkManager *m_bookmarkManager;
    GeoDataCoordinates m_bookmarkCoordinates;
    qreal m_range;
    Ui::UiEditBookmarkDialog m_ui;

    EditBookmarkDialogPrivate(EditBookmarkDialog *q, BookmarkManager *bookmarkManager);

    void initComboBox(const GeoDataContainer *const container, int level = 0);

    void initialize();

    void setFolderName(const QString &name);

    static QString append(const QString &bookmark, const QString &text);

    void openNewFolderDialog();

    void retrieveGeocodeResult(const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark);

    void updateCoordinates();

private:
    EditBookmarkDialog *const q;
};

EditBookmarkDialogPrivate::EditBookmarkDialogPrivate(EditBookmarkDialog *q_, BookmarkManager *bookmarkManager)
    : m_widget(nullptr)
    , m_manager(nullptr)
    , m_bookmarkManager(bookmarkManager)
    , m_range(0)
    , q(q_)
{
    // nothing to do
}

void EditBookmarkDialogPrivate::initialize()
{
    m_ui.setupUi(q);
    bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
    m_ui.m_header->setPositionVisible(!smallScreen);
    m_ui.m_header->setIdVisible(false);
    m_ui.m_header->setIconLink(MarbleDirs::path(QStringLiteral("bitmaps/bookmark.png")));
    QObject::connect(m_ui.m_newFolderButton, SIGNAL(clicked()), q, SLOT(openNewFolderDialog()));
    QObject::connect(m_ui.m_header, SIGNAL(valueChanged()), q, SLOT(updateCoordinates()));

    m_ui.m_folders->clear();
    initComboBox(m_bookmarkManager->document());
}

void EditBookmarkDialogPrivate::initComboBox(const GeoDataContainer *const container, int level)
{
    m_bookmarkManager->ensureDefaultFolder();
    for (GeoDataFolder *folder : container->folderList()) {
        QVariant folderVariant;
        folderVariant.setValue(folder);
        QString name = QStringLiteral(" ").repeated(4 * level) + folder->name();
        m_ui.m_folders->addItem(name, folderVariant);
        if (!folder->folderList().isEmpty()) {
            initComboBox(folder, level + 1);
        }
    }
}

void EditBookmarkDialogPrivate::setFolderName(const QString &name)
{
    for (int i = 0; i < m_ui.m_folders->count(); ++i) {
        if (m_ui.m_folders->itemText(i) == name) {
            m_ui.m_folders->setCurrentIndex(i);
            return;
        }
    }
}

EditBookmarkDialog::EditBookmarkDialog(BookmarkManager *bookmarkManager, QWidget *parent)
    : QDialog(parent)
    , d(new EditBookmarkDialogPrivate(this, bookmarkManager))
{
    d->initialize();
}

void EditBookmarkDialog::setCoordinates(const GeoDataCoordinates &coordinates)
{
    d->m_bookmarkCoordinates = coordinates;
    QObject::disconnect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));

    if (d->m_ui.m_header->name().isEmpty()) {
        d->m_ui.m_header->setName(coordinates.toString());
    }

    d->m_ui.m_header->setLongitude(coordinates.longitude(GeoDataCoordinates::Degree));
    d->m_ui.m_header->setLatitude(coordinates.latitude(GeoDataCoordinates::Degree));
    QObject::connect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
}

void EditBookmarkDialog::setRange(qreal range)
{
    d->m_range = range;
}

void EditBookmarkDialog::setName(const QString &text)
{
    QObject::disconnect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
    d->m_ui.m_header->setName(text);
    d->m_ui.m_header->selectAll();
    QObject::connect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
}

void EditBookmarkDialog::setDescription(const QString &text)
{
    QObject::disconnect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
    d->m_ui.m_description->setText(text);
    QObject::connect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
}

void EditBookmarkDialog::setFolderName(const QString &name)
{
    d->setFolderName(name);
}

void EditBookmarkDialog::setIconLink(const QString &iconLink)
{
    QObject::disconnect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
    d->m_ui.m_header->setIconLink(iconLink);
    QObject::connect(d->m_ui.m_header, SIGNAL(valueChanged()), this, SLOT(updateCoordinates()));
}

void EditBookmarkDialog::setMarbleWidget(MarbleWidget *widget)
{
    d->m_widget = widget;
    const AngleUnit defaultAngleUnit = d->m_widget->defaultAngleUnit();
    const GeoDataCoordinates::Notation notation = (defaultAngleUnit == DecimalDegree) ? GeoDataCoordinates::Decimal
        : (defaultAngleUnit == DMSDegree)                                             ? GeoDataCoordinates::DMS
                                                                                      :
                                          /* else, UTM */ GeoDataCoordinates::UTM;
    d->m_ui.m_header->setNotation(notation);

    d->m_manager = new ReverseGeocodingRunnerManager(d->m_widget->model(), this);
    QObject::connect(d->m_manager,
                     SIGNAL(reverseGeocodingFinished(GeoDataCoordinates, GeoDataPlacemark)),
                     this,
                     SLOT(retrieveGeocodeResult(GeoDataCoordinates, GeoDataPlacemark)));
}

void EditBookmarkDialog::setReverseGeocodeName()
{
    if (!d->m_manager) {
        return;
    }
    // reverse geocode the bookmark point for better user experience
    d->m_manager->reverseGeocoding(d->m_bookmarkCoordinates);
}

EditBookmarkDialog::~EditBookmarkDialog()
{
    delete d;
}

void EditBookmarkDialogPrivate::retrieveGeocodeResult(const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark)
{
    Q_UNUSED(coordinates)
    GeoDataExtendedData data = placemark.extendedData();
    QString bookmarkName;
    qreal distance = m_widget->distance() * KM2METER;
    // FIXME : Optimal logic for suggestion with distance consideration is required

    if (distance >= 3500) {
        bookmarkName = data.value(QStringLiteral("country")).value().toString();
    } else if (distance >= 200) {
        bookmarkName = append(data.value(QStringLiteral("city")).value().toString(), data.value(QStringLiteral("state")).value().toString());
        bookmarkName = append(bookmarkName, data.value(QStringLiteral("country")).value().toString());
    } else {
        bookmarkName = append(data.value(QStringLiteral("road")).value().toString(), data.value(QStringLiteral("city")).value().toString());
        bookmarkName = append(bookmarkName, data.value(QStringLiteral("country")).value().toString());
    }

    if (bookmarkName.isEmpty()) {
        bookmarkName = placemark.address();
    }

    m_ui.m_header->setName(bookmarkName);
    m_ui.m_header->selectAll();
}

QString EditBookmarkDialogPrivate::append(const QString &bookmark, const QString &text)
{
    if (bookmark.isEmpty() && text.isEmpty()) {
        return QString();
    } else if (bookmark.isEmpty()) {
        return text;
    } else if (text.isEmpty()) {
        return bookmark;
    }
    return bookmark + QStringLiteral(", ") + text;
}

void EditBookmarkDialogPrivate::openNewFolderDialog()
{
    QPointer<NewBookmarkFolderDialog> dialog = new NewBookmarkFolderDialog(q);
    if (dialog->exec() == QDialog::Accepted) {
        m_bookmarkManager->addNewBookmarkFolder(m_bookmarkManager->document(), dialog->folderName());
        m_ui.m_folders->clear();
        initComboBox(m_bookmarkManager->document());
        setFolderName(dialog->folderName());
    }
    delete dialog;
}

void EditBookmarkDialogPrivate::updateCoordinates()
{
    m_bookmarkCoordinates.setLongitude(m_ui.m_header->longitude(), GeoDataCoordinates::Degree);
    m_bookmarkCoordinates.setLatitude(m_ui.m_header->latitude(), GeoDataCoordinates::Degree);
}

GeoDataPlacemark EditBookmarkDialog::bookmark() const
{
    // Create a bookmark object
    GeoDataPlacemark bookmark;
    bookmark.setName(name());
    bookmark.setDescription(description());
    GeoDataStyle::Ptr newStyle(new GeoDataStyle(*bookmark.style()));
    newStyle->iconStyle().setIconPath(iconLink());
    bookmark.setStyle(newStyle);
    // allow for HTML in the description
    bookmark.setDescriptionCDATA(true);
    bookmark.setCoordinate(coordinates());
    if (d->m_range) {
        auto lookat = new GeoDataLookAt;
        lookat->setCoordinates(coordinates());
        lookat->setRange(range());
        bookmark.setAbstractView(lookat);
    }

    bookmark.extendedData().addValue(GeoDataData(QStringLiteral("isBookmark"), true));

    if (d->m_widget != nullptr) {
        const QString celestialName = d->m_widget->model()->planetId();
        if (celestialName != QLatin1StringView("earth")) {
            bookmark.extendedData().addValue(GeoDataData(QStringLiteral("celestialBody"), celestialName));
        }
    }

    return bookmark;
}

QString EditBookmarkDialog::name() const
{
    return d->m_ui.m_header->name();
}

GeoDataFolder *EditBookmarkDialog::folder() const
{
    return qvariant_cast<GeoDataFolder *>(d->m_ui.m_folders->itemData(d->m_ui.m_folders->currentIndex()));
}

QString EditBookmarkDialog::description() const
{
    return d->m_ui.m_description->toPlainText();
}

GeoDataCoordinates EditBookmarkDialog::coordinates() const
{
    return d->m_bookmarkCoordinates;
}

qreal EditBookmarkDialog::range() const
{
    return d->m_range;
}

QString EditBookmarkDialog::iconLink() const
{
    return d->m_ui.m_header->iconLink();
}

}

#include "moc_EditBookmarkDialog.cpp"
