#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "ConnectionConfig.h"
#include "catalog/widgets/CatalogInspector.h"
#include "OpenDatabase.h"
#include "Pgsql_Connection.h"
#include "IDatabaseWindow.h"
#include <QMainWindow>
#include <memory>

namespace Ui {
    class DatabaseWindow;
}
namespace Pgsql {
	class Connection;
}

class CrudTab;
class MasterController;
class QCloseEvent;
class OpenDatabase;
class QueryTool;
class PgClass;

class QAction;
class QMenu;
class QTabWidget;
class QToolBar;

/** This is the class for windows that handle tasks for a specific database/catalog
 *
 */
class DatabaseWindow : public QMainWindow, public IDatabaseWindow {
	Q_OBJECT
public:
	DatabaseWindow(MasterController *master, QWidget *parent);
	~DatabaseWindow();

	void setConfig(const ConnectionConfig &config);

	std::shared_ptr<OpenDatabase> getDatabase() { return m_database; }

	void setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint);
	void setTabIcon(QWidget *widget, const QString &iconname);

	/// Called when a newly created page is added to the QTabWidget
	void addPage(QWidget* page, QString caption);

	virtual void newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres) override;

	QueryTool *GetActiveQueryTool();
	CrudTab *GetActiveCrud();

protected:
	virtual void closeEvent(QCloseEvent *event) override;

private:
    Ui::DatabaseWindow *ui;

	ConnectionConfig m_config;
	std::shared_ptr<OpenDatabase> m_database;

	MasterController *m_masterController;

	void newCreateTablePage();
	void newCrudPage(Oid tableoid);
	void newCatalogInspectorPage(QString caption, NamespaceFilter filter);
    void newServerInspectorPage();
	void closeTab(int index);
    bool canCloseTab(int index) const;
    void openSqlFile(QString file_name);

    /// Uses introspection to determine if the specific widget has a copy function.
    /// If it has it invokes this method using reflection.
    static void InvokeCopyIfPresent(QWidget *w);

private slots:
    void catalogLoaded(OpenDatabase::OpenDatabaseSPtr db);
	void tableSelected(Oid tableoid);
    void mainTabCloseRequested(int index);
    void currentMainTabChanged(int index);

    void on_actionAbout_triggered();
    void on_actionCancel_query_triggered();
    void on_actionClose_triggered();
    void on_actionCopy_triggered();
    void on_actionCopy_as_C_string_triggered();
    void on_actionCopy_as_raw_C_string_triggered();
    void on_actionExecute_query_triggered();
    void on_actionExplain_triggered();
    void on_actionExplain_analyze_triggered();
    void on_actionExport_data_triggered();
    void on_actionGenerate_code_triggered();
    void on_actionInspect_information_schema_triggered();
    void on_actionInspect_pg_catalog_triggered();
    void on_actionInspect_user_schemas_triggered();
    void on_actionInspect_server_triggered();
    void on_actionNew_Query_triggered();
    void on_actionOpen_Query_triggered();
    void on_actionSave_Query_triggered();
    void on_actionPaste_lang_string_triggered();
    void on_actionRefreshCatalog_triggered();
    void on_actionRefreshCrud_triggered();
    void on_actionSave_query_as_triggered();
    void on_actionSave_copy_of_query_as_triggered();
    void on_actionShow_connection_manager_triggered();
    void on_actionSave_connection_triggered();

    void on_actionManual_triggered();

public:
	virtual void setTitleForWidget(QWidget *widget, QString title, QString hint) override;
	virtual void setIconForWidget(QWidget *widget, QIcon icon) override;
	virtual std::shared_ptr<OpenDatabase> openDatabase() override;
	virtual void showStatusBarMessage(QString message) override;

    // QWidget interface
protected:
    virtual void dragEnterEvent(QDragEnterEvent *event) override;
    virtual void dropEvent(QDropEvent *event) override;

private:

    template <typename T, class... Params>
    void CallOnActiveQueryTool(T (QueryTool::*Func)(Params...), Params... args)
    {
        auto query_tool = GetActiveQueryTool();
        if (query_tool)
            (query_tool->*Func)(&args...);
    }

    template <typename T, class... Params>
    void CallOnActiveCrud(T (CrudTab::*Func)(Params...), Params... args)
    {
        auto crud = GetActiveCrud();
        if (crud)
            (crud->*Func)(&args...);
    }
};

#endif // MAINWINDOW_H
