//  ************************************************************************************************
//
//  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/Application/ApplicationSettings.h"
#include "GUI/Support/Util/Path.h"
#include "GUI/View/Instrument/InstrumentView.h"
#include "GUI/View/Job/JobView.h"
#include "GUI/View/Main/AboutDialog.h"
#include "GUI/View/Main/MainWindow.h"
#include "GUI/View/Project/ProjectManager.h"
#include "GUI/View/SampleDesigner/SampleView.h"
#include "GUI/View/Tool/mainwindow_constants.h"
#include <QButtonGroup>
#include <QCheckBox>
#include <QDesktopServices>
#include <QDir>
#include <QMenuBar>
#include <QRadioButton>
#include <QSettings>
#include <QShortcut>
#include <QUrl>
#include <QWidgetAction>

ActionManager::ActionManager(MainWindow* parent)
    : QObject(parent)
    , m_mainWindow(parent)
    , m_newAction(nullptr)
    , m_openAction(nullptr)
    , m_saveAction(nullptr)
    , m_saveAsAction(nullptr)
    , m_exitAction(nullptr)
    , m_aboutAction(nullptr)
    , m_menuBar(nullptr)
    , m_fileMenu(nullptr)
    , m_settingsMenu(nullptr)
    , m_viewMenu(nullptr)
    , m_recentProjectsMenu(nullptr)
    , m_helpMenu(nullptr)
    , m_simulateShortcut(nullptr)
{
    createActions();
    createMenus();
    createGlobalShortcuts();

    connect(m_mainWindow, &MainWindow::currentViewChanged, this,
            &ActionManager::onCurrentViewChanged);

    connect(ProjectManager::instance(), &ProjectManager::documentOpenedOrClosed, this,
            &ActionManager::updateActionEnabling);

    updateActionEnabling();
}

void ActionManager::updateActionEnabling()
{
    const bool documentExists = gProjectDocument.has_value();
    m_saveAction->setEnabled(documentExists);
    m_saveAsAction->setEnabled(documentExists);
    m_closeProjectAction->setEnabled(documentExists);
}

void ActionManager::createActions()
{
    ProjectManager* projectManager = m_mainWindow->projectManager();
    ASSERT(projectManager);

    // new project action
    m_newAction = new QAction("&New Project", m_mainWindow);
    m_newAction->setShortcuts(QKeySequence::New);
    m_newAction->setStatusTip("Create a new project");
    connect(m_newAction, &QAction::triggered, projectManager, &ProjectManager::newProject);

    // open project action
    m_openAction = new QAction("&Open Project", m_mainWindow);
    m_openAction->setShortcuts(QKeySequence::Open);
    m_openAction->setStatusTip("Open an existing project");
    connect(m_openAction, &QAction::triggered, projectManager,
            [projectManager]() { projectManager->openProject(); });

    // save project action
    m_saveAction = new QAction("&Save Project", m_mainWindow);
    m_saveAction->setShortcuts(QKeySequence::Save);
    m_saveAction->setStatusTip("Save project");
    m_saveAction->setShortcutContext(Qt::ApplicationShortcut);
    connect(m_saveAction, &QAction::triggered, projectManager,
            [projectManager]() { projectManager->saveProject(); });

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

    // close project action
    m_closeProjectAction = new QAction("Close Project", m_mainWindow);
    m_closeProjectAction->setStatusTip("Save project under different name");
    connect(m_closeProjectAction, &QAction::triggered, projectManager,
            &ProjectManager::closeCurrentProject);

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

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

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

void ActionManager::createMenus()
{
    m_menuBar = new QMenuBar(nullptr); // No parent (System menu bar on Mac OS X)

#ifndef Q_OS_MAC
    m_mainWindow->setMenuBar(m_menuBar);
#endif

    // File Menu
    m_fileMenu = m_menuBar->addMenu("&File");
    m_fileMenu->addAction(m_newAction);
    m_fileMenu->addAction(m_openAction);
    connect(m_fileMenu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowFileMenu);

    m_recentProjectsMenu = m_fileMenu->addMenu("&Recent Projects");

    m_fileMenu->addSeparator();
    m_fileMenu->addAction(m_saveAction);
    m_fileMenu->addAction(m_saveAsAction);
    m_fileMenu->addAction(m_closeProjectAction);

    m_fileMenu->addSeparator();
    m_fileMenu->addAction(m_exitAction);

    // Settings Menu
    m_settingsMenu = new QMenu("&Settings", m_mainWindow);
    onAboutToShowSettingsMenu(); // MacOS feature: action should exist already, otherwise menuBar
                                 //  will not add menu
    connect(m_settingsMenu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowSettingsMenu);
    m_menuBar->addMenu(m_settingsMenu);

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

    // Help Menu
    m_helpMenu = m_menuBar->addMenu("&Help");
    m_helpMenu->addAction(m_webdocAction);
    m_helpMenu->addAction(m_aboutAction);

    onCurrentViewChanged();
}

void ActionManager::createGlobalShortcuts()
{
    m_simulateShortcut = new QShortcut(QKeySequence("Ctrl+r"), m_mainWindow);
    m_simulateShortcut->setContext((Qt::ApplicationShortcut));
    connect(m_simulateShortcut, &QShortcut::activated, m_mainWindow,
            &MainWindow::onRunSimulationShortcut);
}

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

    bool hasRecentProjects = false;
    int orderNr = 1;
    for (const QString& file : m_mainWindow->projectManager()->recentProjects()) {
        hasRecentProjects = true;
        QString actionText = GUI::Base::Path::withTildeHomePath(QDir::toNativeSeparators(file));
        if (orderNr < 10)
            actionText = QString("&%1 ").arg(orderNr) + actionText;
        QAction* action = m_recentProjectsMenu->addAction(actionText);
        action->setData(QVariant::fromValue(file));
        connect(action, &QAction::triggered, m_mainWindow, &MainWindow::openRecentProject);
        orderNr++;
    }
    m_recentProjectsMenu->setEnabled(hasRecentProjects);

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

void ActionManager::onAboutToShowSettingsMenu()
{
    m_settingsMenu->clear();
    m_settingsMenu->setToolTipsVisible(true);
    auto* action = new QWidgetAction(m_settingsMenu);
    auto* checkBox = new QCheckBox("&Enable autosave", m_settingsMenu);
    action->setText("&Enable autosave");
    action->setDefaultWidget(checkBox);
    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);
    checkBox->setChecked(m_mainWindow->projectManager()->isAutosaveEnabled());
    connect(checkBox, &QCheckBox::toggled, m_mainWindow->projectManager(),
            &ProjectManager::setAutosaveEnabled);
    m_settingsMenu->addAction(action);

    action = new QWidgetAction(m_settingsMenu);
    checkBox = new QCheckBox("&Create project on startup", m_settingsMenu);
    action->setText("&Create project on startup");
    action->setDefaultWidget(checkBox);
    action->setCheckable(true);
    checkBox->setChecked(appSettings->createNewProjectOnStartup());
    connect(checkBox, &QCheckBox::toggled,
            [](bool b) { appSettings->setCreateNewProjectOnStartup(b); });
    m_settingsMenu->addAction(action);

    m_settingsMenu->addSeparator();

    auto* styleMenu = m_settingsMenu->addMenu("Interface Style");
    auto* styleGroup = new QButtonGroup(this);
    styleGroup->setExclusive(true);
    const auto addStyleAction = [this, styleGroup, styleMenu](const QString& text,
                                                              ApplicationSettings::Style style) {
        auto* action = new QWidgetAction(styleMenu);
        auto* radioButton = new QRadioButton(text, styleMenu);
        radioButton->setStyleSheet("");
        action->setDefaultWidget(radioButton);
        radioButton->setChecked(appSettings->currentStyle() == style);
        connect(radioButton, &QRadioButton::toggled, this, [style]() {
            appSettings->setStyleToUse(style);
            appSettings->loadStyle(style);
        });
        action->setCheckable(true);
        styleGroup->addButton(radioButton);
        styleMenu->addAction(action);
    };

    addStyleAction("Dark style", ApplicationSettings::Style::dark);
    addStyleAction("Native style", ApplicationSettings::Style::native);
    addStyleAction("Light style", ApplicationSettings::Style::light);
}

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

    auto* view = m_mainWindow->currentView();
    if (auto* sampleView = dynamic_cast<SampleView*>(view); sampleView != nullptr)
        sampleView->fillViewMenu(m_viewMenu);
    if (auto intrumentView = dynamic_cast<InstrumentView*>(view); intrumentView != nullptr)
        intrumentView->fillViewMenu(m_viewMenu);
    if (auto* jobView = dynamic_cast<JobView*>(view); jobView != nullptr)
        jobView->fillViewMenu(m_viewMenu);
}

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

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