/**************************************************************************
** Piperka Client
** Copyright (C) 2019  Kari Pahula
**
** 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 2 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, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
**************************************************************************/

#pragma once

#include <QJsonValue>
#include <QObject>
#include <QtQuick/QQuickView>
#include <QTimer>

#include "download.h"
#include "subscription.h"

class User : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name NOTIFY nameChange)
    Q_PROPERTY(bool loading READ loading NOTIFY loadingChange)
    Q_PROPERTY(bool logged READ logged NOTIFY loggedChange)
    Q_PROPERTY(bool offsetBack READ offsetBack WRITE setOffsetBack NOTIFY offsetBackChanged)
    Q_PROPERTY(QString storedLoginName READ storedLoginName CONSTANT)
    Q_PROPERTY(QString storedCreateEmail READ storedCreateEmail CONSTANT)
    Q_PROPERTY(QString storedCreatePassword READ storedCreatePassword CONSTANT)
    Q_PROPERTY(QString networkErrorMessage READ networkErrorMessage CONSTANT)
    Q_PROPERTY(bool syncAvailable READ syncAvailable NOTIFY syncAvailableChanged)
    Q_PROPERTY(int silentSyncFailure READ silentSyncFailure NOTIFY silentSyncFailureChanged)
    Q_PROPERTY(bool rememberMe READ rememberMe CONSTANT)
    Q_PROPERTY(bool noSubscriptions READ noSubscriptions NOTIFY noSubscriptionsChanged)
    Q_PROPERTY(bool recSubscriptions READ recSubscriptions NOTIFY recSubscriptionsChanged)
    Q_PROPERTY(bool browseHelpSeen READ browseHelpSeen WRITE setBrowseHelpSeen)
public:
    explicit User(Download &download, QObject *parent = nullptr);
    void emitIfLoggedin();

    const QString name() { return m_name; }
    bool loading() const { return m_loading > 0; }
    bool logged() const { return !m_name.isEmpty(); }
    QString storedLoginName() const { return m_storedLoginName; }
    QString storedCreateEmail() const { return m_storedCreateEmail; }
    QString storedCreatePassword() const { return m_storedCreatePassword; }
    QString networkErrorMessage() const { return m_networkErrorMessage; }
    bool syncAvailable() const { return m_syncAvailable; }
    int silentSyncFailure() const { return m_silentSyncFailure; }
    bool rememberMe() const { return m_remember; }
    bool noSubscriptions() const {return subs_set.empty();}
    bool recSubscriptions() const {return subs_set.size() >= 5;}
    bool browseHelpSeen() const {return m_browseHelpSeen;}
    void setBrowseHelpSeen(const bool &seen);

    Q_INVOKABLE void createAccount(const QString &user, const QString &email,
                                   const QString &password, bool remember);
    Q_INVOKABLE void login(const QString &user, const QString &password,
                           bool remember, bool importBookmarks);
    Q_INVOKABLE void logout();
    Q_INVOKABLE void subscribe(const int &cid, const bool &bookmarkFirst);
    Q_INVOKABLE void subscribeAt(const int &cid, const int &ord);
    Q_INVOKABLE void unsubscribe(const int &cid);
    Q_INVOKABLE int subscriptionCount() const { return subs_set.count(); }
    Q_INVOKABLE void resetStoredAccountDetails();
    Q_INVOKABLE void syncNow(bool interactive);
    Q_INVOKABLE void unlockSync();
    Q_INVOKABLE bool localBookmarks() const {return !localBM.isEmpty();}

    bool offsetBack() const { return m_offsetBack; }
    void setOffsetBack(const bool &offsetBack);

signals:
    void nameChange();
    void loadingChange();
    void loggedChange();
    void loggedOut();
    void newSubscription(const QPointer<Subscription> &ptr);
    void refreshSubscription(const QPointer<Subscription> &ptr);
    void synchronize();
    void syncAvailableChanged();
    void fetchSubscriptionsEnd();
    void offsetBackChanged();
    void createAccountNameReserved();
    void loginFailed();
    void networkError();
    void silentSyncFailureChanged();
    void noSubscriptionsChanged();
    void recSubscriptionsChanged();
    void forceLogout();

    // Catch all for any subscription changes
    void subscriptionChange();

public slots:
    void networkActionBegin();
    void networkActionEnd();
    void syncError(QNetworkReply *reply);
    void genericNetworkError(QNetworkReply *reply);
    void setCursor();

private:
    // Check hourly
    static const int syncInterval = 3600000;
    // On screen unlock, do an extra sync
    static const int unlockSyncInterval = 900000;
    // Give the sync button a rest
    static const int interactiveSyncInterval = 10000;

    bool m_interactiveSync;
    bool m_reportedSyncError;
    bool m_countedSilentFailure;
    int m_silentSyncFailure;
    bool m_syncAvailable = true;
    int m_loading = 0;
    Download &m_download;
    QTimer timer;
    QTimer syncAvailableTimer;
    QTime lastSync;
    bool m_remember = false;
    bool m_offsetBack;
    QString m_name;
    QByteArray m_token;
    QString m_storedLoginName;
    QString m_storedCreateEmail;
    QString m_storedCreatePassword;
    QString m_networkErrorMessage;
    bool m_browseHelpSeen;

    QHash<int, Subscription *> subs_set;
    QMap<int, int> localBM;

    bool extractAccountData(QNetworkReply *reply, QJsonObject &obj);
    void deleteSubscriptions();
    void _subscribe(const int &cid, const int &ord, const bool &bookmarkFirst);
    Subscription *addSubscription(const QJsonArray &val);
    void fetchSubscriptions();
    Subscription *parseAndSaveSubscription(const QString &fieldName, const int &cid, QNetworkReply *reply);
    void storeLocalSubscriptions();
    void handleNetworkError(QNetworkReply *reply, bool sync);
};
