#ifndef USERDATABASE_HH
#define USERDATABASE_HH

#include <QObject>
#include <QVector>
#include <QHash>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include <QGeoPositionInfoSource>
#include <QFuture>

/** Auto-updating DMR user database.
 *
 * This class represents the complete DMR user database. The user database gets downloaded from
 * https://www.radioid.net/static/users.json and kept up-to-date by re-downloading it
 * periodically (by default every 30 days). This user database gets used in the GUI application
 * to help assemble private call contacts and to assemble so-called CSV callsign databases, that
 * are programmable to some DMR radios to resolve the DMR ID to callsigns and names.
 *
 * @ingroup util */
class UserDatabase : public QAbstractTableModel
{
  Q_OBJECT

  /** Get notification, once the database has been loaded. */
  Q_PROPERTY(bool ready READ ready NOTIFY readyChanged FINAL)

public:
  /** Represents the user information within the @c UserDatabase. */
  class User {
  public:
    /** Empty constructor. */
    User();
    /** Constructs entry from JSON object. */
    User(const QJsonObject &obj);

    /** Returns @c true if the entry is valid. */
    inline bool isValid() const { return 0 != id; }

    /** Returns the "distance" between this user and the given ID. */
    unsigned distance(unsigned id) const;

    /** The DMR ID of the user. */
    unsigned id;
    /** The callsign of the user. */
    QString call;
    /** The name of the user. */
    QString name;
    /** The surname of the user. */
    QString surname;
    /** The city of the user. */
    QString city;
    /** The state of the user. */
    QString state;
    /** The country of the user. */
    QString country;
    /** Some arbitrary comment or text. */
    QString comment;
  };

public:
  /** Constructs the user-database.
   * The constructor will download the current user database if it was not downloaded yet or
   * if the downloaded version is older than @c updatePeriodDays days. */
  explicit UserDatabase(bool parallel, unsigned updatePeriodDays=30, QObject *parent=nullptr);

  /** Returns the number of users. */
  qint64 count() const;

  /** Retruns @c true, if the user database file exists. */
  bool exists() const;

  /** Loads all entries from the downloaded user database. */
  bool load();
  /** Loads all entries from the downloaded user database at the specified location. */
  bool load(const QString &filename);
  /** Returns @c true, if the database has been loaded. */
  bool ready() const;

  /** Sorts users with respect to the distance to the given ID. */
  void sortUsers(unsigned id);
  /** Sorts users with respect to the minimum distance to the given IDs. */
  void sortUsers(const QSet<unsigned> &ids);

  /** Returns the user with index @c idx. */
  const User &user(int idx) const;

  /** Returns the age of the database in days. */
  unsigned dbAge() const;

  /** Implements the QAbstractTableModel interface, returns the number of rows (number of entries). */
  int rowCount(const QModelIndex &parent=QModelIndex()) const;
  /** Implements the QAbstractTableModel interface, returns the number of columns. */
  int columnCount(const QModelIndex &parent=QModelIndex()) const;
  /** Implements the QAbstractTableModel interface, return the entry data. */
  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;

signals:
  /** Gets emitted once the call-sign database has been loaded. */
  void loaded();
  /** Gets emitted if the loading of the call-sign database fails. */
  void error(const QString &msg);
  /** Gets emitted, once the database has been loaded or cleard.
   * @warning This event might be emitted by another thread. */
  void readyChanged(bool ready);

public slots:
  /** Starts the download of the user database. */
  void download();

private slots:
  /** Gets called whenever the download is complete. */
  void downloadFinished(QNetworkReply *reply);
  /** Parses cache and stores all users in given result list. */
  bool parse(QList<User> &users);
  /** Parses the given file and stores all users in given result list. */
  bool parse(const QString &filename, QList<User> &users);
  /** Loads all entries from the parsed file. */
  bool load(const QList<User> &users);

private:
  /** Holds all users sorted by their ID. */
  QVector<User>         _user;
  /** The network access used for downloading. */
  QNetworkAccessManager _network;
  /** The current parallel task of parsing the database. */
  QFuture<bool> _parsing;
};


#endif // USERDATABASE_HH
