#ifndef EDITOR_H
#define EDITOR_H

#include "db/db.h"
#include "mdichild.h"
#include "common/extactioncontainer.h"
#include "gui_global.h"
#include <QModelIndexList>
#include <QWidget>


class QToolButton;
namespace Ui {
    class EditorWindow;
}

class SqlQueryModel;
class QComboBox;
class QActionGroup;
class DbListModel;
class QLabel;
class QLineEdit;
class ExtLineEdit;
class IntValidator;
class FormView;
class SqlQueryItem;
class SqlEditor;
class DbComboBox;

CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"),
     CFG_KEY_ENTRY(EXEC_QUERY,                Qt::Key_F5,                 QObject::tr("Execute query"))
     CFG_KEY_ENTRY(EXEC_ONE_QUERY,            Qt::CTRL | Qt::Key_F5,      QObject::tr("Execute single query under cursor"))
     CFG_KEY_ENTRY(EXEC_ALL_QUERIES,          Qt::SHIFT | Qt::Key_F5,     QObject::tr("Execute all queries in editor"))
     CFG_KEY_ENTRY(EXPLAIN_QUERY,             Qt::Key_F6,                 QObject::tr("Execute EXPLAIN query"))
     CFG_KEY_ENTRY(EXPORT_RESULTS,            Qt::CTRL | Qt::Key_E,       QObject::tr("Export query results"))
     CFG_KEY_ENTRY(PREV_DB,                   Qt::CTRL | Qt::Key_Up,      QObject::tr("Switch current working database to previous on the list"))
     CFG_KEY_ENTRY(NEXT_DB,                   Qt::CTRL | Qt::Key_Down,    QObject::tr("Switch current working database to next on the list"))
     CFG_KEY_ENTRY(SHOW_NEXT_TAB,             Qt::ALT | Qt::Key_Right,    QObject::tr("Go to next editor tab"))
     CFG_KEY_ENTRY(SHOW_PREV_TAB,             Qt::ALT | Qt::Key_Left,     QObject::tr("Go to previous editor tab"))
     CFG_KEY_ENTRY(FOCUS_RESULTS_BELOW,       Qt::ALT | Qt::Key_PageDown, QObject::tr("Move keyboard input focus to the results view below"))
     CFG_KEY_ENTRY(FOCUS_EDITOR_ABOVE,        Qt::ALT | Qt::Key_PageUp,   QObject::tr("Move keyboard input focus to the SQL editor above"))
     CFG_KEY_ENTRY(DELETE_SINGLE_HISTORY_SQL, QKeySequence::Delete,       QObject::tr("Delete selected SQL history entries"))
)

class GUI_API_EXPORT EditorWindow : public MdiChild
{
    Q_OBJECT

    public:
        enum class ResultsDisplayMode
        {
            SEPARATE_TAB = 0,
            BELOW_QUERY = 1,
            RIGHT_SIDE = 2
        };

        enum Action
        {
            EXEC_QUERY,
            EXEC_ONE_QUERY,
            EXEC_ALL_QUERIES,
            EXPLAIN_QUERY,
            EXPLAIN_MODE_EXPLAIN,
            EXPLAIN_MODE_QUERY_PLAN,
            RESULTS_IN_TAB,
            RESULTS_BELOW,
            RESULTS_ON_RIGHT,
            CURRENT_DB,
            NEXT_DB,
            PREV_DB,
            SHOW_NEXT_TAB,
            SHOW_PREV_TAB,
            FOCUS_RESULTS_BELOW,
            FOCUS_EDITOR_ABOVE,
            CLEAR_HISTORY,
            EXPORT_HISTORY,
            EXPORT_RESULTS,
            CREATE_VIEW_FROM_QUERY,
            DELETE_SELECTED_HISTORY_SQL,
            EXPORT_SELECTED_HISTORY_SQL,
            SETTINGS,
            AUTO_COMMIT,
            QUERIES_TX_SEP,
            COMMIT_MANUAL_TX,
            ROLLBACK_MANUAL_TX
        };
        Q_ENUM(Action)

        enum QueryExecMode
        {
            DEFAULT,
            SINGLE,
            ALL
        };

        enum ToolBar
        {
            TOOLBAR_MAIN
        };

        enum class ActionGroup
        {
            RESULTS_POSITIONING
        };

        explicit EditorWindow(QWidget *parent = 0);
        EditorWindow(const EditorWindow& editor);
        ~EditorWindow();

        static void staticInit();
        static void insertAction(ExtActionPrototype* action, ToolBar toolbar);
        static void insertAction(ExtActionPrototype* action);
        static void insertActionBefore(ExtActionPrototype* action, Action beforeAction, ToolBar toolbar = TOOLBAR_MAIN);
        static void insertActionAfter(ExtActionPrototype* action, Action afterAction, ToolBar toolbar = TOOLBAR_MAIN);
        static void removeAction(ExtActionPrototype* action, ToolBar toolbar);
        static void removeAction(ExtActionPrototype* action);

        QSize sizeHint() const;
        QAction* getAction(Action action);
        QString getQueryToExecute(QueryExecMode querySelectionMode = DEFAULT);
        bool setCurrentDb(Db* db);
        void setContents(const QString& sql);
        QString getContents() const;
        void execute();
        QToolBar* getToolBar(int toolbar) const;
        SqlEditor* getEditor() const;
        bool isUncommitted() const;
        QString getQuitUncommittedConfirmMessage() const;
        Db* getCurrentDb();
        QPair<Db*, QString> getSoftDbObjectAssociation() const;
        bool getAutoCommit() const;
        bool setAutoCommit(bool newAutoCommit, bool askForWal);
        bool hasPendingManualTx(bool includeDataViewChanges = true) const;
        bool isManualCommitMode() const;
        void clearReadOnlyManualTx(bool respectSavepoint);

    protected:
        void changeEvent(QEvent *e);
        QVariant saveSession();
        bool restoreSession(const QVariant& sessionValue);
        Icon* getIconNameForMdiWindow();
        QString getTitleForMdiWindow();

    private:
        void init();
        void createActions();
        QToolButton* createSettingsDropdown();
        void createDbCombo();
        void setupDefShortcuts();
        void selectCurrentQuery(bool fallBackToPreviousIfNecessary = false);
        void updateShortcutTips();
        void setupSqlHistoryMenu();
        bool processBindParams(QString& sql, QHash<QString, QVariant>& queryParams);
        void exportHistory(const QModelIndexList& idxList);
        bool initManualCommitForCurrentDb(bool askForWal);
        bool cleanUpManualCommitForCurrentDb();
        void updateManualCommitIconsAndLabels(bool manualCommit);
        bool validateDbChange(Db* db);
        void rollbackManualTx(bool allowReloadIfFeasible);
        bool confirmPendingManualTx(const QString& dbName);
        void useAutoCommitForCurrentDb();
        bool hasExplicitSavepoint(const QString& query) const;
        void setNewResultsDisplayMode(ResultsDisplayMode mode);

        static const int queryLimitForSmartExecution = 100;

        static const int histDbNameColumn = 1;
        static const int histDatetimeColumn = 2;
        static const int histSqlColumn = 5;

        Ui::EditorWindow *ui = nullptr;
        SqlQueryModel* resultsModel = nullptr;
        QHash<ActionGroup,QActionGroup*> actionGroups;
        DbComboBox* dbCombo = nullptr;
        int sqlEditorNum = 1;
        qint64 lastQueryHistoryId = 0;
        qint64 pendingQueryChanges = 0;
        QString lastSuccessfulQuery;
        QMenu* sqlHistoryMenu = nullptr;
        bool settingSqlContents = false;
        ResultsDisplayMode resultsPrevDisplayMode = ResultsDisplayMode::BELOW_QUERY;
        ResultsDisplayMode resultsDisplayMode = ResultsDisplayMode::BELOW_QUERY;
        Db* txDb = nullptr;
        bool currentlyAutoCommit = true;

    private slots:
        void execQuery(int explain = -1, EditorWindow::QueryExecMode querySelectionMode = EditorWindow::DEFAULT);
        void execOneQuery();
        void execAllQueries();
        void explainQuery();
        void changeExplainMode();
        void changeResultsLayout();
        void dbChanged();
        void executionSuccessful();
        void executionFailed(const QString& errorText);
        void storeExecutionInHistory();
        void updateResultsDisplayMode(bool enforce = false);
        void prevDb();
        void nextDb();
        void showNextTab();
        void showPrevTab();
        void focusResultsBelowOrOnSide();
        void focusEditorAboveOrOnSide();
        void historyEntrySelected(const QModelIndex& current, const QModelIndex& previous);
        void historyEntryActivated(const QModelIndex& current);
        void deleteSelectedSqlHistory();
        void exportSelectedSqlHistory();
        void clearHistory();
        void exportHistory();
        void sqlHistoryContextMenuRequested(const QPoint &pos);
        void createViewFromQuery();
        void updateState();
        void checkTextChangedForSession();
        void queryHighlightingConfigChanged(const QVariant& enabled);
        void renameForFile(const QString fileName);
        void exportResults();
        void toggleAutoCommit();
        void updateManualCommitStatus();
        void openManualTxForDataCommit(int dataRows);
        void commitManualTx();
        void rollbackManualTx();
        void handleDbAboutToDisconnect(Db* db, bool& deny);

    public slots:
        void openFile(const QString& fileName);
        void refreshValidDbObjects();
};

GUI_API_EXPORT size_t qHash(EditorWindow::ActionGroup action);

#endif // EDITOR_H
