#ifndef SQLQUERYVIEW_H
#define SQLQUERYVIEW_H

#include "csvformat.h"
#include "common/extactioncontainer.h"
#include "db/queryexecutor.h"
#include "gui_global.h"
#include "sqlqueryitem.h"
#include <QTableView>
#include <QHeaderView>

class SqlQueryItemDelegate;
class SqlQueryItem;
class WidgetCover;
class SqlQueryModel;
class SqlQueryModelColumn;
class QPushButton;
class QProgressBar;
class QMenu;


class Plugin;

class PluginType;
CFG_KEY_LIST(SqlQueryView, QObject::tr("Data grid view"),
    CFG_KEY_ENTRY(EDIT_CURRENT,      Qt::Key_F2,                              QObject::tr("Edit current cell inline"))
    CFG_KEY_ENTRY(CUT,               QKeySequence::Cut,                       QObject::tr("Cut cell(s) contents to clipboard"))
    CFG_KEY_ENTRY(COPY,              QKeySequence::Copy,                      QObject::tr("Copy cell(s) contents to clipboard"))
    CFG_KEY_ENTRY(COPY_WITH_HEADER,  Qt::CTRL | Qt::SHIFT | Qt::Key_C,        QObject::tr("Copy cell(s) contents together with header to clipboard"))
//    CFG_KEY_ENTRY(COPY_AS,           Qt::CTRL | Qt::ALT | Qt::Key_C,        QObject::tr(""))
    CFG_KEY_ENTRY(PASTE,             QKeySequence::Paste,                     QObject::tr("Paste cell(s) contents from clipboard"))
//    CFG_KEY_ENTRY(PASTE_AS,          Qt::CTRL | Qt::ALT | Qt::Key_V,        QObject::tr(""))
    CFG_KEY_ENTRY(ERASE,             Qt::ALT | Qt::Key_Backspace,             QObject::tr("Set empty value to selected cell(s)"))
    CFG_KEY_ENTRY(SET_NULL,          Qt::Key_Backspace,                       QObject::tr("Set NULL value to selected cell(s)"))
    CFG_KEY_ENTRY(COMMIT,            QKeySequence::Save,                      QObject::tr("Commit changes to cell(s) contents"))
    CFG_KEY_ENTRY(ROLLBACK,          Qt::ALT | Qt::SHIFT | Qt::Key_Backspace, QObject::tr("Rollback changes to cell(s) contents"))
    CFG_KEY_ENTRY(DELETE_ROW,        Qt::Key_Delete,                          QObject::tr("Delete selected data row"))
    CFG_KEY_ENTRY(INSERT_ROW,        Qt::Key_Insert,                          QObject::tr("Insert new data row"))
    CFG_KEY_ENTRY(OPEN_VALUE_EDITOR, Qt::Key_F4,                              QObject::tr("Open contents of selected cell in a separate editor"))
    CFG_KEY_ENTRY(ADJUST_ROWS_SIZE,  Qt::ALT | Qt::Key_Z,                     QObject::tr("Toggle the height adjustment of rows"))
    CFG_KEY_ENTRY(INCR_FONT_SIZE,    Qt::CTRL | Qt::Key_Plus,                 QObject::tr("Increase font size", "data view"))
    CFG_KEY_ENTRY(DECR_FONT_SIZE,    Qt::CTRL | Qt::Key_Minus,                QObject::tr("Decrease font size", "data view"))
)

class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer
{
    Q_OBJECT

    public:
        enum Action
        {
            CUT,
            COPY,
            COPY_WITH_HEADER,
            COPY_AS,
            PASTE,
            PASTE_AS,
            SET_NULL,
            ERASE,
            RESET_VALUE,
            ROLLBACK,
            COMMIT,
            INSERT_ROW,
            INSERT_MULTIPLE_ROWS,
            DELETE_ROW,
            SELECTIVE_COMMIT,
            SELECTIVE_ROLLBACK,
            EDIT_CURRENT,
            OPEN_VALUE_EDITOR,
            SORT_DIALOG,
            RESET_SORTING,
            GENERATE_SELECT,
            GENERATE_INSERT,
            GENERATE_UPDATE,
            GENERATE_DELETE,
            INVERT_SELECTION,
            ADJUST_ROWS_SIZE,
            INCR_FONT_SIZE,
            DECR_FONT_SIZE,
            NEXT_ROW_ON_ENTER
        };
        Q_ENUM(Action)

        enum ToolBar
        {
        };

        explicit SqlQueryView(QWidget* parent = 0);
        virtual ~SqlQueryView();
        QList<SqlQueryItem*> getSelectedItems();
        SqlQueryItem* getCurrentItem();
        SqlQueryModel* getModel();
        void setModel(QAbstractItemModel *model) override;
        SqlQueryItem *itemAt(const QPoint& pos);
        QToolBar* getToolBar(int toolbar) const override;
        void addAdditionalAction(QAction* action);
        void addHeaderAdditionalAction(QAction* action);
        QModelIndex getCurrentIndex() const;
        bool getSimpleBrowserMode() const;
        void setSimpleBrowserMode(bool value);
        void setIgnoreColumnWidthChanges(bool ignore);
        QMenu* getHeaderContextMenu() const;
        void setReadOnly(bool value);
        int getColumnCustomDelegateWidth(int colIdx);
        int getColumnCustomDelegateHeight(int colIdx);
        QSize getColumnCustomDelegateCellSize(int colIdx);
        QVariant getCustomDelegatesForSession() const;
        void restoreCustomDelegatesFromSession(const QVariant& sessionValue);
        void scrollTo(const QModelIndex & index, ScrollHint hint = EnsureVisible) override;
        QList<int> getPinnedColumns() const;

        static void staticInit();

    protected:
        void scrollContentsBy(int dx, int dy) override;
        void keyPressEvent(QKeyEvent *e) override;
        void resizeEvent(QResizeEvent *event) override;
        QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;

    private:
        class Header : public QHeaderView
        {
            public:
                explicit Header(SqlQueryView* parent);

                QSize sectionSizeFromContents(int section) const override;
                void mousePressEvent(QMouseEvent *e) override;
                void mouseDoubleClickEvent(QMouseEvent *e) override;
                void mouseMoveEvent(QMouseEvent *e) override;
                void leaveEvent(QEvent* e) override;

            private:
                void handleSectionResize(int logicalIndex, int oldSize, int newSize);

                bool dblClickResizing = false;
                bool ignoreResizing = false;
                QHash<int, int> lastSectionSizes;
        };

        void init();
        void initPinnedView(QAbstractItemModel* model);
        void setupWidgetCover();
        void createActions() override;
        void setupDefShortcuts() override;
        void refreshShortcuts();
        void setupActionsForMenu(SqlQueryItem* currentItem, const QList<SqlQueryItem*>& selectedItems);
        void setupHeaderMenu();
        bool editInEditorIfNecessary(SqlQueryItem* item);
        void paste(const QList<QList<QVariant>>& data);
        bool validatePasting(QSet<QString>& warnedColumns, bool& warnedRowDeletion, SqlQueryItem* item);
        void addFkActionsToContextMenu(SqlQueryItem* currentItem);
        void goToReferencedRow(const QString& table, const QString& column, const QVariant& value);
        void copy(bool withHeaders, bool cutOperation);
        void changeFontSize(int factor);
        void headerMiddleClicked(int colIdx);
        void requestColumnSorting(int colIdx);
        void setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate);
        void moveToNextRowAndEdit();

        constexpr static const char* mimeDataId = "application/x-letos-data-view-data";
        constexpr static const int minHeaderWidth = 15;
        constexpr static const int separatorTolerance = 8;

        SqlQueryItemDelegate* itemDelegate = nullptr;
        SqlQueryItemDelegate* pinnedViewItemDelegate = nullptr;
        QMenu* contextMenu = nullptr;
        QMenu* headerContextMenu = nullptr;
        QMenu* referencedTablesMenu = nullptr;
        WidgetCover* widgetCover = nullptr;
        QPushButton* cancelButton = nullptr;
        QProgressBar* busyBar = nullptr;
        QList<QAction*> additionalActions;
        QList<QAction*> headerAdditionalActions;
        bool simpleBrowserMode = false;
        bool ignoreColumnWidthChanges = false;
        int beforeExecutionHorizontalPosition = -1;
        int beforeExecutionVerticalPosition = -1;
        QHash<int, CellRendererPlugin*> customColumnDelegates;
        QHash<QString, CellRendererPlugin*> manualCustomDelegates;
        QTableView* pinnedView = nullptr;
        QList<int> pinnedColumns;

    private slots:
        void handlePluginLoaded(Plugin* plugin, PluginType* pluginType);
        void handlePluginUnload(Plugin* plugin, PluginType* pluginType);
        void updateCommitRollbackActions(bool enabled);
        void customContextMenuRequested(const QPoint& pos);
        void headerContextMenuRequested(const QPoint& pos);
        void openSortDialog();
        void resetSorting();
        void sortingUpdated(const QueryExecutor::SortList& sortOrder);
        void updateFont();
        void itemActivated(const QModelIndex& index);
        void generateSelect();
        void generateSelectFunction();
        void generateInsert();
        void generateUpdate();
        void generateDelete();
        void editCurrent();
        void toggleRowsHeightAdjustment(bool enabled);
        void toggleNextRowOnEnter(bool enabled);
        void adjustRowToContents(int section);
        void fontSizeChangeRequested(int delta);
        void incrFontSize();
        void decrFontSize();
        void invertSelection();
        void syncPinnedSectionHeight(int logicalIndex, int oldSize, int newSize);
        void updatePinnedViewColumns();
        void updatePinnedViewRowSizes();

    protected slots:
        void closeEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint) override;

    public slots:
        void syncPinnedSectionWidth(int logicalIndex, int oldSize, int newSize);
        void updatePinnedViewGeometry();
        void executionStarted();
        void executionEnded();
        void setCurrentRow(int row);
        void refreshColumnDelegates();
        void cut();
        void copy();
        void copyWithHeader();
        void paste();
        void copyAs();
        void pasteAs();
        void setNull();
        void erase();
        void resetValue();
        void commit();
        void rollback();
        void selectiveCommit();
        void selectiveRollback();
        void openValueEditor(SqlQueryItem* item);
        void openValueEditor();

    signals:
        void contextMenuRequested(SqlQueryItem* currentItem, const QList<SqlQueryItem*>& selectedItems);
        void requestForRowInsert();
        void requestForMultipleRowInsert();
        void requestForRowDelete();
        void scrolledBy(int dx, int dy);
        void headerMiddleButtonClicked(int section);
        void newFontMetricsApplied();
        void pinnedColumnsChanged();
        void pinnedSectionResized(int logicalIndex, int oldSize, int newSize);
        void columnSortingRequested(int section);
};

GUI_API_EXPORT size_t qHash(SqlQueryView::Action action);

#endif // SQLQUERYVIEW_H
