/***
   This file is part of fly-ocr.

   Copyright (C) 2016-2020 Sandro Mani <manisandro@gmail.com>
   Copyright (C) 2021 LLC "RusBITech-Astra" (https://astralinux.ru)

    Based on gImageReader source code by Sandro Mani under the GNU
    General Public License (GPL) version 3 or later

   Foobar 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.

   Foobar 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 fly-ocr. If not, see <https://www.gnu.org/licenses/>.
***/

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QFutureWatcher>
#include <QList>
#include <QMainWindow>
#include <QMutexLocker>
#include <QStack>
#include <QStringList>
#include <QTimer>
#include <QActionGroup>
#include <fly-ocr/ocrenginefactory.h>
#include "common.h"
#include "configdialog.h"

#define MAIN MainWindow::instance()

class QToolButton;
class Acquirer;
class Displayer;
class DisplayerTool;
class OutputEditor;
class RecognitionMenu;
class Recognizer;
class SourceManager;
class QProgressBar;
class MainWindowUi;

namespace KSaneIface
{
class KSaneWidget;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    enum class State { Idle, Normal, Busy };

    struct NotificationAction {
        QString text;
        std::function<void()> slot;
        bool close;
    };

    class ProgressMonitor
    {
    public:
        ProgressMonitor(int total) : mTotal(total) {}
        virtual ~ProgressMonitor() {}
        void increaseProgress()
        {
            QMutexLocker locker(&mMutex);
            ++mProgress;
        }
        virtual int getProgress() const
        {
            QMutexLocker locker(&mMutex);
            return (mProgress * 100) / mTotal;
        }
        virtual void cancel()
        {
            QMutexLocker locker(&mMutex);
            mCancelled = true;
        }
        bool cancelled() const
        {
            QMutexLocker locker(&mMutex);
            return mCancelled;
        }

    protected:
        mutable QMutex mMutex;
        const int mTotal;
        int mProgress = 0;
        bool mCancelled = false;
    };

    typedef void* Notification;

    static MainWindow* instance();
    static void signalHandler(int signal);
    static void tesseractCrash(int signal);

    MainWindow();
    ~MainWindow();

    ConfigDialog* config() const;
    Displayer* displayer() const;
    OutputEditor* outputEditor() const;
    Recognizer* recognizer() const;
    SourceManager* sourceManager() const;
    RecognitionMenu* recognitionMenu() const;

    void addNotification(const QString& title, const QString& message, const QList<NotificationAction>& actions, Notification* handle = nullptr);
    void setOutputPaneVisible(bool visible);
    void showProgress(ProgressMonitor* monitor, int updateInterval = 500);
    void hideProgress();

public slots:
    void popState();
    void pushState(MainWindow::State state, const QString& msg);
    void showHelp();
    void hideNotification(Notification handle = nullptr);
    void onSelectedPageChanged();
    void showAbout();
    void showAboutOCR();
    void showConfig();
    void progressCancel();
    void progressUpdate();
    void onSelectedLanguagesChanged(const QList<FlyOcr::Lang>& langs);
    void changeScannerDevice();
    void scanDone(int status, const QString& strStatus);
    void alertUser(int type, const QString& strStatus);

private slots:
    void on_actionSaveAs_triggered();
    void pasteImage();
    void updateLanguages();
    void updateActions();

private:
    void closeEvent(QCloseEvent* ev) override;
    void setState(State state);
    QMap<QString, QString> readScannerOptions();
    void saveScannerOptions();
    void openScanner();
    void exportAs(const QString& path, OcrEngineFactory::Format format);

    static void signalHandlerExec(int signal, bool tesseractCrash);

    friend class BusyEventFilter;

    static MainWindow* s_instance;

    MainWindowUi* m_ui;
    ConfigDialog* m_config = nullptr;
    Displayer* m_displayer = nullptr;
    DisplayerTool* m_displayerTool = nullptr;
    OutputEditor* m_outputEditor = nullptr;
    RecognitionMenu* m_recognitionMenu = nullptr;
    Recognizer* m_recognizer = nullptr;
    SourceManager* m_sourceManager = nullptr;

    QActionGroup m_idleActions;
    QList<QWidget*> m_idleWidgets;
    QStack<QPair<State, QString>> m_stateStack;

    MainWindow::Notification m_notifierHandle = nullptr;

    QWidget* m_progressWidget = nullptr;
    QProgressBar* m_progressBar = nullptr;
    QToolButton* m_progressCancelButton = nullptr;
    QLabel* m_engineInfoLabel = nullptr;
    QTimer m_progressTimer;
    ProgressMonitor* m_progressMonitor = nullptr;
    KSaneIface::KSaneWidget* m_saneWidget;

    QFutureWatcher<QString> m_versionWatcher;
    QString m_saneDeviceName, m_saneDeviceLabel;
    QList<QAction*> m_extraActions;
};

Q_DECLARE_METATYPE(MainWindow::State)

#endif
