/**********************************************************************************************
    Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>

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

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************************/

#ifndef IMAP_H
#define IMAP_H

#include <QImage>
#include <QMutex>
#include <QPointer>

#include "canvas/IDrawContext.h"
#include "canvas/IDrawObject.h"
#include "gis/proj_x.h"

class CMapDraw;
class IMapProp;
struct IPoiItem;

class IMap : public IDrawObject {
  Q_OBJECT
 public:
  IMap(quint32 features, CMapDraw* parent);
  virtual ~IMap();

  void saveConfig(QSettings& cfg) override;

  void loadConfig(QSettings& cfg) override;

  enum features_e {
    eFeatVisibility = 0x00000001,
    eFeatVectorItems = 0x00000002,
    eFeatTileCache = 0x00000004,
    eFeatLayers = 0x00000008,
    eFeatTypFile = 0x00000010
  };

  virtual void draw(IDrawContext::buffer_t& buf) = 0;

  /**
     @brief Test if map has been loaded successfully
     @return Return false if map is not loaded
   */
  bool activated() const { return isActivated; }

  /**
     @brief Get the map's setup widget.

     As default an instance of CMapPropSetup is used. For other setups you have
     to override this method.

     @return A pointer to the widget. Use a smart pointer to store as the widget can be destroyed at any time
   */
  virtual IMapProp* getSetup();

  virtual void getInfo(const QPoint&, QString&) {}

  virtual void getToolTip(const QPoint&, QString&) const {}

  virtual void findPOICloseBy(const QPoint&, IPoiItem&) const {}

  /**
     @brief True if this map's overview pyramid needs attention (missing or too shallow)
            and that hasn't been suppressed for this file.

     Always false except for CMapVRT, where it drives the proactive warning badge in
     CMapItemDelegate - the same underlying advice used by the reactive advisory dialog
     that fires on a render timeout, just queried ahead of any render.
   */
  virtual bool showsOverviewWarning() const { return false; }

  /**
     @brief True if this map has overview-pyramid info available to show on demand
            (independent of showsOverviewWarning() - available whether or not there's
            actually anything to fix). Always false except for CMapVRT.
   */
  virtual bool hasOverviewInfo() const { return false; }

  /**
     @brief Return copyright notice if any
     @return If no copyright notice has been decoded the string will be empty
   */
  const QString& getCopyright() const { return copyright; }

  bool hasFeatureVisibility() const { return flagsFeature & eFeatVisibility; }
  bool hasFeatureVectorItems() const { return flagsFeature & eFeatVectorItems; }
  bool hasFeatureTileCache() const { return flagsFeature & eFeatTileCache; }
  bool hasFeatureLayers() const { return flagsFeature & eFeatLayers; }

  bool hasFeatureTypFile() const { return flagsFeature & eFeatTypFile; }

  bool getShowPolygons() const { return showPolygons; }

  bool getShowPolylines() const { return showPolylines; }

  bool getShowPOIs() const { return showPOIs; }

  const QString& getCachePath() const { return cachePath; }

  qint32 getCacheSize() const { return cacheSizeMB; }

  qint32 getCacheExpiration() const { return cacheExpiration; }

  qint32 getAdjustDetailLevel() const { return adjustDetailLevel; }

  const QString& getTypeFile() const { return typeFile; }

  /**
     @brief Find a matching street polyline

     The polyline must be close enough in terms of pixel to point 1 and 2. "Close enough" is defined by
     the threshold. The returned polyline uses lon/lat as coordinates.

     @param pt1           first point in [rad]
     @param pt2           second point in [rad]
     @param threshold     the "close enough" threshold in [pixel]
     @param polyline      the resulting polyline, if any, in [rad]
     @return              Return true if a line has been found.
   */
  virtual bool findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline);

 public slots:
  void slotSetShowPolygons(bool yes) { showPolygons = yes; }
  void slotSetShowPolylines(bool yes) { showPolylines = yes; }
  void slotSetShowPOIs(bool yes) { showPOIs = yes; }

  void slotSetCachePath(const QString& path) {
    cachePath = path;
    configureCache();
  }
  void slotSetCacheSize(qint32 size) {
    cacheSizeMB = size;
    configureCache();
  }
  void slotSetCacheExpiration(qint32 days) {
    cacheExpiration = days;
    configureCache();
  }

  void slotSetAdjustDetailLevel(qint32 level) { adjustDetailLevel = level; }

  virtual void slotSetTypeFile(const QString& filename) { typeFile = filename; }

 protected:
  void convertRad2M(QPointF& p) const;
  void convertM2Rad(QPointF& p) const;

  /**
     @brief Detect what reprojection is needed and select the correct handler

     This has to be called prior to the loop that calls drawTile();

   */
  void detectTileDrawMode(const IDrawContext::buffer_t& buf);

  /**
     @brief Reproject (translate, rotate, scale) tile before drawing it.
     @param img   the tile as QImage
     @param l     a 4 point polygon to fit the tile in
     @param p     the QPainter used to paint the tile
   */
  void drawTile(const QImage& img, QPolygonF& l, QPainter& p);

 protected:
  /// the drawcontext this map belongs to
  QPointer<CMapDraw> map;

  /**
      target should always be "EPSG:4326"
      source will be the map's projection
   */
  CProj proj;

  /**
     @brief True if map was loaded successfully
   */
  bool isActivated = false;

  /// the setup dialog. Use getSetup() for access
  QPointer<IMapProp> setup;

  /// flag field for features defined in features_e
  quint32 flagsFeature;

  bool showPolygons = true;      //< vector maps only: hide/show polygons
  bool showPolylines = true;     //< vector maps only: hide/show polylines
  bool showPOIs = true;          //< vector maps only: hide/show point of interest
  qint32 adjustDetailLevel = 0;  //< vector maps only: alter threshold to show details.

  QString cachePath;           //< streaming map only: path to cached tiles
  qint32 cacheSizeMB = 100;    //< streaming map only: maximum size of all tiles in cache [MByte]
  qint32 cacheExpiration = 8;  //< streaming map only: maximum age of tiles in cache [days]

  QString copyright;  //< a copyright string to be displayed as tool tip

  QString typeFile;
};

#endif  // IMAP_H
