#ifndef __APPLICATION_H__
#define __APPLICATION_H__

#include "DataStructure.h"
#include "ModuleCommunication.h"
#include "Singleton.h"
#include <QObject>
#include <QQmlEngine>
#include <memory>

class QGuiApplication;
class Database;
class VideoPlayer;
class VideoFrameProvider;
class QTimer;
class CdcUpdater;

class Application : public QObject {
    Q_OBJECT
    QML_NAMED_ELEMENT(App)
    QML_SINGLETON
    Q_PROPERTY(ModuleCommunication *module READ module NOTIFY connectedChanged)
    Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
    Q_PROPERTY(bool uvcOpened READ uvcOpened NOTIFY uvcOpenedChanged)
    Q_PROPERTY(bool persistenceMode READ persistenceMode WRITE setPersistenceMode NOTIFY persistenceModeChanged)
    Q_PROPERTY(bool imageUploadPersistenceMode READ imageUploadPersistenceMode WRITE setImageUploadPersistenceMode
                   NOTIFY imageUploadPersistenceModeChanged)
    Q_PROPERTY(int persistenceVerifyInterval READ persistenceVerifyInterval WRITE setPersistenceVerifyInterval NOTIFY
                   persistenceVerifyIntervalChanged)
    Q_PROPERTY(bool isVerifying READ isVerifying NOTIFY isVerifyingChanged)
    friend class Amass::Singleton<Application>;
    static constexpr auto JpgPath = "jpg";
    static constexpr auto YuvPath = "yuv";

public:
    enum TipType {
        Tip,
        Error,
        Info,
        Warnging,
    };
    Q_ENUM(TipType)

    void initializeLogger();
    int exec();
    static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
    Q_INVOKABLE QStringList availableSerialPorts() const;
    Q_INVOKABLE QStringList availableUsbVideoCameras() const;
    Q_INVOKABLE bool open(const QString &portName, int baudRate);
    Q_INVOKABLE bool openUVC(const QString &deviceName);
    Q_INVOKABLE void close();
    Q_INVOKABLE void closeUVC();
    Q_INVOKABLE bool startOta(const QString &path);
    Q_INVOKABLE void verify(bool captureImage, uint8_t timeout);
    Q_INVOKABLE void enroll(const QString &username, bool strictMode, uint8_t excludeMode, bool persistence,
                            uint8_t timeout);
    Q_INVOKABLE void enrollExtended(const QString &username, bool strictMode, uint8_t excludeMode, bool persistence,
                                    uint8_t timeout);
    Q_INVOKABLE void deleteUser(uint16_t userid);
    Q_INVOKABLE void deleteAll();
    Q_INVOKABLE void uploadImage(const QString &path, const QString &username, int operation);
    ModuleCommunication *module() const;
    bool connected() const;
    bool uvcOpened() const;
    bool persistenceMode() const;
    void setPersistenceMode(bool enabled);
    bool imageUploadPersistenceMode() const;
    void setImageUploadPersistenceMode(bool enabled);

    int persistenceVerifyInterval() const;
    void setPersistenceVerifyInterval(int interval);
    bool isVerifying() const;

signals:
    void connectedChanged();
    void persistenceModeChanged();
    void persistenceVerifyIntervalChanged();
    void imageUploadPersistenceModeChanged();
    void isVerifyingChanged();
    void uvcOpenedChanged();
    void newVideoFrame();
    void newLog(const QString &log);
    void newStatusTip(TipType type, const QString &tip, const QString &detailMessage = "");
    void updateFinished();
    void otaMessage(const QString &text);
    void otaProgressChanged(int32_t progress);

protected:
    Application(int &argc, char **argv);
    void onNewEnrollResult(uint16_t userid);
    void onNewVerifyResult(uint16_t userid, const QString &username, uint16_t elapsed);
    void onNewPalmFeature(const PalmFeature &feature);
    void onErrorOccurred(ModuleCommunication::NoteId note, const QString &error, const QString &detailMessage);
    void onNewImageInfo(ModuleCommunication::MessageId messageId, uint32_t size, const uint8_t *md5);
    void onNewImageSliceData(const std::vector<uint8_t> &data);
    void onCommandStarted(ModuleCommunication::MessageId messageId);
    void onCommandFinished(ModuleCommunication::MessageId messageId, ModuleCommunication::MessageStatus status);
    void onVerifyTimeout();
    void onCdcDeviceDiscovered(const QSerialPortInfo &info);
    void onUpdateFinished();

private:
    std::shared_ptr<QGuiApplication> m_app;
    std::shared_ptr<ModuleCommunication> m_communication;
    std::shared_ptr<CdcUpdater> m_updater;
    std::shared_ptr<Database> m_database;

    bool m_persistenceMode = true; // 模组持续识别
    bool m_verifyExtendedMode = false;
    bool m_persistenceModeStarted = false;
    int m_persistenceVerifyInterval = 1;
    QTimer *m_verifyTimer = nullptr;

    ModuleCommunication::MessageId m_palmImageId; // 通过哪个指令获取的图片
    uint32_t m_palmImageSize = 0;
    QString m_palmUsername;
    uint16_t m_palmId = ModuleCommunication::InvalidUserId;
    QByteArray m_palmYImageBuffer;
    std::chrono::system_clock::time_point m_startUploadTime;

    uint32_t m_uploadImageSendedSize;
    std::vector<uint8_t> m_uploadBuffer;
    int m_currentUploadOperation = 0;
    QString m_uploadPath;
    QString m_uploadUsername;
    bool m_imageUploadling = false;
    bool m_imageUploadPersistenceMode = false;

    std::shared_ptr<VideoPlayer> m_videoPlayer;
    VideoFrameProvider *m_videoFrameProvider;
};

#endif // __APPLICATION_H__