//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Main/ActionManager.cpp
//! @brief     Implements class ActionManager.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2021
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/View/Main/ActionManager.h"
#include "Base/Util/Assert.h"
#include "Base/Util/SysUtil.h"
#include "GUI/Model/Descriptor/ComboProperty.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Util/Path.h"
#include "GUI/View/Base/mainwindow_constants.h"
#include "GUI/View/Main/AboutDialog.h"
#include "GUI/View/Main/CentralWidget.h"
#include "GUI/View/Main/ProjectManager.h"
#include "GUI/View/Numeric/ComboUtil.h"
#include "GUI/View/View/InstrumentView.h"
#include "GUI/View/View/JobView.h"
#include "GUI/View/View/SampleView.h"
#include "GUI/View/Widget/AppConfig.h"
#include <QBoxLayout>
#include <QButtonGroup>
#include <QCheckBox>
#include <QDesktopServices>
#include <QDir>
#include <QLabel>
#include <QRadioButton>
#include <QSettings>
#include <QUrl>
#include <QWidgetAction>

ActionManager::ActionManager(QMainWindow* parent, ProjectManager* pm)
    : QObject(parent)
    , m_main_window(parent)
    , m_project_manager(pm)
{
    createActions();
    createMenus();
    // createGlobalShortcuts();

    updateActionEnabling();
}

void ActionManager::updateActionEnabling()
{
    const bool documentExists = (bool)gDoc;
    m_save_action->setEnabled(documentExists);
    m_save_as_action->setEnabled(documentExists);
}

void ActionManager::createActions()
{
    ASSERT(m_project_manager);

    // new project action
    m_new_action = new QAction("&New project", m_main_window);
    m_new_action->setShortcuts(QKeySequence::New);
    m_new_action->setStatusTip("Create a new project");
    connect(m_new_action, &QAction::triggered, m_project_manager, &ProjectManager::newProject);

    // open project action
    m_open_action = new QAction("&Open project", m_main_window);
    m_open_action->setShortcuts(QKeySequence::Open);
    m_open_action->setStatusTip("Open an existing project");
    connect(m_open_action, &QAction::triggered, [this] { m_project_manager->openProject(); });

    // save project action
    m_save_action = new QAction("&Save project", m_main_window);
    m_save_action->setShortcuts(QKeySequence::Save);
    m_save_action->setStatusTip("Save project");
    m_save_action->setShortcutContext(Qt::ApplicationShortcut);
    connect(m_save_action, &QAction::triggered, [this] { m_project_manager->saveProject(); });

    // save-as project action
    m_save_as_action = new QAction("Save project &as...", m_main_window);
    m_save_as_action->setShortcuts(QKeySequence::SaveAs);
    m_save_as_action->setStatusTip("Save project under different name");
    connect(m_save_as_action, &QAction::triggered, m_project_manager,
            &ProjectManager::saveProjectAs);

    // exit application action
    m_exit_action = new QAction("&Quit", this);
    m_exit_action->setShortcuts(QKeySequence::Quit);
    m_exit_action->setStatusTip("Exit the application");
    connect(m_exit_action, &QAction::triggered, m_main_window, &QMainWindow::close);

    // visit web doc action
    m_webdoc_action = new QAction("&Visit web docs", this);
    m_webdoc_action->setStatusTip("Open BornAgain documentation in default browser");
    connect(m_webdoc_action, &QAction::triggered,
            [] { QDesktopServices::openUrl(QUrl("https://www.bornagainproject.org/latest")); });

    // about application action
    m_about_action = new QAction("&About BornAgain", this);
    m_about_action->setStatusTip("About the application");
    connect(m_about_action, &QAction::triggered, this, &ActionManager::onAboutApplication);
}

void ActionManager::createMenus()
{
    m_menu_bar = new QMenuBar(nullptr); // No parent (System menu bar on Mac OS X)
#ifndef Q_OS_MAC
    m_main_window->setMenuBar(m_menu_bar);
#endif

    // File Menu
    m_file_menu = m_menu_bar->addMenu("&File");
    m_file_menu->addAction(m_new_action);
    m_file_menu->addAction(m_open_action);
    connect(m_file_menu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowFileMenu);

    m_recent_projects_menu = m_file_menu->addMenu("&Recent Projects");

    m_file_menu->addSeparator();
    m_file_menu->addAction(m_save_action);
    m_file_menu->addAction(m_save_as_action);

    m_file_menu->addSeparator();
    m_file_menu->addAction(m_exit_action);

    // Settings Menu
    m_settings_menu = new QMenu("&Settings", m_main_window);
    m_menu_bar->addMenu(m_settings_menu);
    m_settings_menu->setToolTipsVisible(true);

    {
        auto* action = new QWidgetAction(m_settings_menu);
        action->setText("&Enable autosave");
        auto* box = new QCheckBox("&Enable autosave", m_settings_menu);
        action->setDefaultWidget(box);
        action->setToolTip("Project will be saved periodically in project's autosave directory.\n"
                           "When opening project, recover option will be suggested, if possible.");
        action->setCheckable(true);
        box->setChecked(gApp->autosave_enabled);
        connect(box, &QCheckBox::toggled, [=] { gApp->autosave_enabled = box->isChecked(); });
        m_settings_menu->addAction(action);
    }

    {
        auto* action = new QWidgetAction(m_settings_menu);
        action->setToolTip("Color gradient for 2d plots");
        auto* w = new QWidget;
        auto* l = new QHBoxLayout(w);
        l->addWidget(new QLabel("2D plot color scheme"));
        QComboBox* box =
            GUI::Util::createComboBox([] { return *gApp->color_gradient_combo; },
                                      [](const QString& s) {
                                          gApp->color_gradient_combo->setCurrentValue(s);
                                          emit gApp->gradientChanged();
                                      },
                                      false);
        l->addWidget(box);
        action->setDefaultWidget(w);
        m_settings_menu->addAction(action);
    }

    // View menu
    m_view_menu = new QMenu("&View", m_main_window);
    onAboutToShowViewMenu(); // MacOS feature: action should exist already, otherwise menuBar will
                             // not add menu
    connect(m_view_menu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowViewMenu);
    m_menu_bar->addMenu(m_view_menu);

    // Help Menu
    m_help_menu = m_menu_bar->addMenu("&Help");
    m_help_menu->addAction(m_webdoc_action);
    m_help_menu->addAction(m_about_action);

    onCurrentViewChanged();
}

void ActionManager::onAboutToShowFileMenu()
{
    m_recent_projects_menu->clear();

    bool hasRecentProjects = false;
    int orderNr = 1;
    for (const QString& file : m_project_manager->recentProjects()) {
        hasRecentProjects = true;
        QString actionText = GUI::Path::withTildeHomePath(QDir::toNativeSeparators(file));
        if (orderNr < 10)
            actionText = QString("&%1 ").arg(orderNr) + actionText;
        QAction* action = m_recent_projects_menu->addAction(actionText);
        action->setData(QVariant::fromValue(file));
        connect(action, &QAction::triggered,
                [this, file] { m_project_manager->openProject(file); });
        orderNr++;
    }
    m_recent_projects_menu->setEnabled(hasRecentProjects);

    if (hasRecentProjects) {
        m_recent_projects_menu->addSeparator();
        QAction* action = m_recent_projects_menu->addAction("&Clear Menu");
        connect(action, &QAction::triggered, m_project_manager,
                &ProjectManager::clearRecentProjects);
    }
}

void ActionManager::onAboutToShowViewMenu()
{
    m_view_menu->clear();

    if (auto* cw = dynamic_cast<CentralWidget*>(m_main_window->centralWidget())) {
        QWidget* view = cw->currentView();
        if (auto* jobView = dynamic_cast<JobView*>(view); jobView != nullptr)
            jobView->fillViewMenu(m_view_menu);
    }
}

void ActionManager::onAboutApplication()
{
    AboutDialog dialog(m_main_window);
    dialog.exec();
}

void ActionManager::onCurrentViewChanged()
{
    // not every view support view menu entries -> hide it, if empty
    onAboutToShowViewMenu();
    m_view_menu->menuAction()->setVisible(!m_view_menu->actions().isEmpty());
}
