#pragma once

#include <QGeoCoordinate>
#include <QJsonObject>
#include <QObject>
#include <QString>

#include "QmlObjectListModel.h"

class GeoArea;
class SafeArea;
class MeasurementArea;

class AreaData : public QObject {
  Q_OBJECT
public:
  AreaData(QObject *parent = nullptr);
  ~AreaData();
  AreaData(const AreaData &other, QObject *parent = nullptr);
  AreaData &operator=(const AreaData &other);

  Q_PROPERTY(QmlObjectListModel *areaList READ areaList NOTIFY areaListChanged)
  Q_PROPERTY(QmlObjectListModel *measurementAreaList READ measurementAreaList
                 NOTIFY areaListChanged)
  Q_PROPERTY(
      QmlObjectListModel *safeAreaList READ safeAreaList NOTIFY areaListChanged)
  Q_PROPERTY(QString errorString READ errorString NOTIFY error)

  // Member Methodes
  //!
  //! \brief insert Inserts the area if areaList does not contain it.
  //! \param areaData
  bool insert(GeoArea *areaData);
  //!
  //! \brief remove
  //! \param areaData Removes the area.
  //! \note Deletes the area if it has either no parent or the parent is this
  //! object.
  void remove(GeoArea *areaData);
  void clear();
  //!
  //! \brief areaList
  //! \return Returns the list of areas.
  //! \note For Qml use only, don't alter the list, or risk to break invariants.
  QmlObjectListModel *areaList();
  //!
  //! \brief areaList
  //! \return Returns the list of areas.
  const QmlObjectListModel *areaList() const;

  //!
  //! \brief origin
  //! \return Returns an origin near one of the areas.
  //! \note Origin might change if the list of areas changes.
  QGeoCoordinate origin() const;

  Q_INVOKABLE bool isCorrect(bool showError = true);
  //!
  //! \brief initialize Initializes the areas in a valid way, such that they
  //! area inside the bounding box. \param bottomLeft bottom left corner of the
  //! bounding box. \param topRight  top right corner of the bounding box. \note
  //! Behavior is undefined, if \p bottomLeft and \p topRight are not the bottom
  //! left and the top right corner of the bounding box. \return Returns true on
  //! succes, false either.
  //!
  Q_INVOKABLE bool initialize(const QGeoCoordinate &bottomLeft,
                              const QGeoCoordinate &topRight);
  //!
  //! \brief initialized Checks if area data is initialized
  //! \return Returns true if area list contains a SafeArea and a
  //! MeasurementArea and both areas have atleast three vertices, returns false
  //! either.
  //!
  Q_INVOKABLE bool initialized();
  Q_INVOKABLE void intersection(bool showError = true);

  QVector<MeasurementArea *> measurementAreaArray();
  QVector<SafeArea *> safeAreaArray();
  QmlObjectListModel *measurementAreaList();
  QmlObjectListModel *safeAreaList();

  bool operator==(const AreaData &other) const;
  bool operator!=(const AreaData &other) const;

  bool load(const QJsonObject &obj, QString &errorString);
  bool save(QJsonObject &obj);

  QString errorString() const; // Contains a message about the last error.

signals:
  void areaListChanged();
  void originChanged();
  void error(); // Emitted if errorString() contains a new message.
private slots:
  void _updateOrigin();

private:
  void _setOrigin(const QGeoCoordinate &origin);
  void _processError(const QString &str, bool showError);
  bool _areasCorrect(bool showError);
  bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea,
                 bool showError);

  QGeoCoordinate _origin;
  QmlObjectListModel _areaList;
  QmlObjectListModel _measurementAreaList;
  QmlObjectListModel _safeAreaList;
  QString _errorString;
};
