/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2023-05-15
 * Description : geolocation engine based on Marble.
 *               (c) 2007-2022 Marble Team
 *               https://invent.kde.org/education/marble/-/raw/master/data/credits_authors.html
 *
 * SPDX-FileCopyrightText: 2023-2026 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QObject>
#include <QString>

// Local includes

#include "MarbleGlobal.h"
#include "digikam_export.h"

class QItemSelectionModel;
class QAbstractItemModel;
class QTextDocument;
class QDateTime;

/**
 * MarbleModel is the data store and index class for the MarbleWidget.
 */

namespace Marble
{

class GeoDataPlacemark;
class GeoPainter;
class MeasureTool;
class HttpDownloadManager;
class MarbleModelPrivate;
class MarbleClock;
class SunLocator;
class TileCreator;
class PluginManager;
class GeoDataCoordinates;
class GeoDataTreeModel;
class GeoSceneDocument;
class Planet;
class FileManager;
class ElevationModel;

/**
 * @short The data model (not based on QAbstractModel) for a MarbleWidget.
 *
 * This class provides a data storage and indexer that can be
 * displayed in a MarbleWidget.  It contains 3 different datatypes:
 * <b>tiles</b> which provide the background, <b>vectors</b> which
 * provide things like country borders and coastlines and
 * <b>placemarks</b> which can show points of interest, such as
 * cities, mountain tops or the poles.
 *
 * The <b>tiles</b> provide the background of the image and can be for
 * instance height and depth fields, magnetic strength, topographic
 * data or anything else that is area based.
 *
 * The <b>vectors</b> provide things like country borders and
 * coastlines.  They are stored in separate files and can be added or
 * removed at anytime.
 *
 * The <b>placemarks</b> contain points of interest, such as cities,
 * mountain tops or the poles. These are sorted by size (for cities)
 * and category (capitals, other important cities, less important
 * cities, etc) and are displayed with different color or shape like
 * square or round.
 *
 * @see MarbleWidget
 */

class DIGIKAM_EXPORT MarbleModel : public QObject
{
    friend class MarbleModelPrivate;

    Q_OBJECT

    Q_PROPERTY(QString mapThemeId READ mapThemeId WRITE setMapThemeId NOTIFY themeChanged)
    Q_PROPERTY(bool workOffline READ workOffline WRITE setWorkOffline NOTIFY workOfflineChanged)

public:

    /**
     * @brief  Construct a new MarbleModel.
     * @param parent the parent widget
     */
    explicit MarbleModel(QObject* parent = nullptr);
    ~MarbleModel()                                            override;

    /**
     * @brief Return the list of Placemarks as a QAbstractItemModel *
     * @return a list of all Placemarks in the MarbleModel.
     */
    GeoDataTreeModel* treeModel();
    const GeoDataTreeModel* treeModel()                 const;

    QAbstractItemModel* groundOverlayModel();
    const QAbstractItemModel* groundOverlayModel()      const;

    QAbstractItemModel* placemarkModel();
    const QAbstractItemModel* placemarkModel()          const;

    QItemSelectionModel* placemarkSelectionModel();

    /**
     * @brief Return the name of the current map theme.
     * @return the identifier of the current MapTheme.
     * To ensure that a unique identifier is being used the theme does NOT
     * get represented by its name but the by relative location of the file
     * that specifies the theme:
     *
     * Example:
     *    maptheme = "earth/bluemarble/bluemarble.dgml"
     */
    QString mapThemeId()                                const;

    GeoSceneDocument* mapTheme();
    const GeoSceneDocument* mapTheme()                  const;
    void setMapTheme(GeoSceneDocument* document);

    /**
     * @brief Set a new map theme to use.
     * @param mapThemeId  the identifier of the new map theme
     *
     * This function sets the map theme, i.e. combination of tile set
     * and color scheme to use.  If the map theme is not previously
     * used, some basic tiles are created and a progress dialog is
     * shown.
     *
     * The ID of the new maptheme. To ensure that a unique
     * identifier is being used the theme does NOT get represented by its
     * name but the by relative location of the file that specifies the theme:
     *
     * Example:
     *    maptheme = "earth/bluemarble/bluemarble.dgml"
     */
    void setMapThemeId(const QString& mapThemeId);

    /**
     * @brief  get the home point
     * @param  lon  the longitude of the home point.
     * @param  lat  the latitude of the home point.
     * @param  zoom the default zoom level of the home point.
     */
    void home(qreal& lon, qreal& lat, int& zoom)        const;
    /**
     * @brief  Set the home point
     * @param  lon  the longitude of the new home point.
     * @param  lat  the latitude of the new home point.
     * @param  zoom the default zoom level for the new home point.
     */
    void setHome(qreal lon, qreal lat, int zoom = 1050);
    /**
     * @brief  Set the home point
     * @param  homePoint  the new home point.
     * @param  zoom       the default zoom level for the new home point.
     */
    void setHome(const GeoDataCoordinates& homePoint, int zoom = 1050);

    /**
     * @brief Return the downloadmanager to load missing tiles
     * @return the HttpDownloadManager instance.
     */
    HttpDownloadManager* downloadManager();
    const HttpDownloadManager* downloadManager()        const;

    /**
     * @brief Handle file loading into the treeModel
     * @param filename the file to load
     */
    void addGeoDataFile(const QString& filename);

    /**
     * @brief Handle raw data loading into the treeModel
     * @param data the raw data to load
     * @param key the name to remove this raw data later
     */
    void addGeoDataString(const QString& data,
                          const QString& key = QLatin1String("data"));

    /**
     * @brief Remove the file or raw data from the treeModel
     * @param key either the file name or the key for raw data
     */
    void removeGeoData(const QString& key);

    FileManager*          fileManager();

    qreal                 planetRadius()                const;
    QString               planetName()                  const;
    QString               planetId()                    const;

    MarbleClock* clock();
    const MarbleClock* clock()                          const;

    SunLocator* sunLocator();
    const SunLocator* sunLocator()                      const;

    /**
     * @brief  Returns the limit in kilobytes of the persistent (on hard disc) tile cache.
     * @return the limit of persistent tile cache in kilobytes.
     */
    quint64 persistentTileCacheLimit()                  const;

    /**
     * @brief  Returns the limit of the volatile (in RAM) tile cache.
     * @return the cache limit in kilobytes
     */
    quint64 volatileTileCacheLimit()                    const;

    const PluginManager* pluginManager()                const;

    PluginManager* pluginManager();

    /**
     * @brief Returns the planet object for the current map.
     * @return the planet object for the current map
     */
    const Planet* planet()                              const;

    void setClockDateTime(const QDateTime& datetime);
    QDateTime clockDateTime()                           const;

    int clockSpeed()                                    const;
    void setClockSpeed(int speed);

    void setClockTimezone(int timeInSec);
    int clockTimezone()                                 const;

    QTextDocument* legend();

    /**
     * @brief Uses the given text document as the new content of the legend
     * Any previous legend content is overwritten. MarbleModel takes ownership
     * of the passed document.
     */
    void setLegend(QTextDocument* document);

    bool workOffline()                                  const;
    void setWorkOffline(bool workOffline);

    ElevationModel* elevationModel();
    const ElevationModel* elevationModel()              const;

    /**
     * Returns the placemark being tracked by this model or 0 if no
     * placemark is currently tracked.
     * @see setTrackedPlacemark(), trackedPlacemarkChanged()
     */
    const GeoDataPlacemark* trackedPlacemark()          const;

public Q_SLOTS:

    void clearPersistentTileCache();

    /**
     * @brief  Set the limit of the persistent (on hard disc) tile cache.
     * @param  kiloBytes The limit in kilobytes, 0 means no limit.
     */
    void setPersistentTileCacheLimit(quint64 kiloBytes);

    /**
     * @brief Change the placemark tracked by this model
     * @see trackedPlacemark(), trackedPlacemarkChanged()
     */
    void setTrackedPlacemark(const GeoDataPlacemark* placemark);

    void updateProperty(const QString& property, bool value);

Q_SIGNALS:

    /**
     * @brief Signal that the MarbleModel has started to create a new set of tiles.
     * @param name name of the set
     * @param description the set description
     */
    void creatingTilesStart(TileCreator*, const QString& name, const QString& description);

    /**
     * @brief Signal that the map theme has changed, and to which theme.
     * @param mapTheme the identifier of the new map theme.
     * @see  mapTheme
     * @see  setMapTheme
     */
    void themeChanged(const QString& mapTheme);

    void workOfflineChanged();

    /**
     * @brief Emitted when the placemark tracked by this model has changed
     * @see setTrackedPlacemark(), trackedPlacemark()
     */
    void trackedPlacemarkChanged(const GeoDataPlacemark* placemark);

    /** @brief Emitted when the home location is changed
     * @see home(), setHome()
     */
    void homeChanged(const GeoDataCoordinates& newHomePoint);

private:

    Q_DISABLE_COPY(MarbleModel)

    Q_PRIVATE_SLOT(d, void assignFillColors(const QString& filePath))

    void addDownloadPolicies(const GeoSceneDocument* mapTheme);

private:

    MarbleModelPrivate* const d = nullptr;
};

} // namespace Marble
