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

#include "GUI/View/Job/JobView.h"
#include "GUI/Support/Data/ID.h"
#include "GUI/View/Fit/FitActivityPanel.h"
#include "GUI/View/Fit/JobMessagePanel.h"
#include "GUI/View/Fit/JobRealTimeWidget.h"
#include "GUI/View/Job/JobProgressAssistant.h"
#include "GUI/View/Job/JobResultsPresenter.h"
#include "GUI/View/Job/JobSelectorWidget.h"
#include "GUI/View/Job/JobViewActivities.h"
#include "GUI/View/Project/ProjectManager.h"
#include "GUI/View/Widget/DocksController.h"
#include <QMenu>

JobView::JobView(QProgressBar* progressBar, ProjectDocument* document)
    : m_docks(new DocksController(this))
    , m_progressAssistant(new JobProgressAssistant(progressBar, document->jobModel()))
    , m_activityActions(this)
    , m_document(document)
{
    setObjectName("JobView");
    createActions();
    createSubWindows();
    connectJobRelated();
}

void JobView::fillViewMenu(QMenu* menu)
{
    menu->addActions(m_activityActions.actions());
    menu->addSeparator();

    m_docks->addDockActionsToMenu(menu);

    menu->addSeparator();

    auto* action = new QAction(menu);
    action->setText("Reset to default layout");
    connect(action, &QAction::triggered, this, &JobView::resetLayout);
    menu->addAction(action);
}

void JobView::onFocusRequest(JobItem* jobItem)
{
    if (jobItem->runInBackground())
        return;

    // only for the new job
    if (jobItem->activity().isEmpty()) {
        m_jobSelector->makeJobItemSelected(jobItem);
        setActivityAndPresentationForNewJob(jobItem);
        emit focusRequest(GUI::ID::Job);
    }
}

//! Sets docks visibility in accordance with required activity.

void JobView::setActivity(JobViewActivity activity)
{
    QVector<JobViewFlags::Dock> docksToShow = JobViewActivities::activeDocks(activity);

    std::vector<int> docks_id;
    for (auto x : docksToShow)
        docks_id.push_back(static_cast<int>(x));

    m_docks->setVisibleDocks(docks_id);
    m_activityActions.actions()[static_cast<int>(activity)]->setChecked(true);
}

//! Propagates change in JobItem's selection down into main widgets.

void JobView::onSelectionChanged()
{
    m_jobResultsPresenter->setJobItem(selectedJobItem());
    m_jobRealTimeWidget->setJobItem(selectedJobItem());
    m_fitActivityPanel->setJobItem(selectedJobItem());

    if (!selectedJobItem())
        return;

    // apply activity (show docks) for the currently selected job
    QString jobActivity = selectedJobItem()->activity();
    if (!jobActivity.isEmpty())
        setActivity(JobViewActivities::activityFromName(jobActivity));
}

JobItem* JobView::selectedJobItem()
{
    const QVector<JobItem*>& jobs = m_jobSelector->selectedJobItems();

    if (jobs.size() == 1)
        return jobs.front();

    return nullptr;
}

void JobView::createSubWindows()
{
    m_jobResultsPresenter = new JobResultsPresenter(this);
    m_jobSelector = new JobSelectorWidget(m_document->jobModel(), this);
    m_jobRealTimeWidget = new JobRealTimeWidget(m_document->jobModel(), this);
    m_fitActivityPanel = new FitActivityPanel(this);
    m_jobMessagePanel = new JobMessagePanel(this);

    m_docks->addWidget(JobViewFlags::JOB_LIST_DOCK, m_jobSelector, Qt::LeftDockWidgetArea);
    m_docks->addWidget(JobViewFlags::REAL_TIME_DOCK, m_jobRealTimeWidget, Qt::RightDockWidgetArea);
    m_docks->addWidget(JobViewFlags::FIT_PANEL_DOCK, m_fitActivityPanel, Qt::RightDockWidgetArea);
    m_docks->addWidget(JobViewFlags::JOB_MESSAGE_DOCK, m_jobMessagePanel, Qt::BottomDockWidgetArea);

    m_fitActivityPanel->setRealTimeWidget(m_jobRealTimeWidget);

    setCentralWidget(m_jobResultsPresenter);

    resetLayout();
}

void JobView::createActions()
{
    for (JobViewActivity activity : JobViewActivities::all()) {
        auto* action = new QAction(this);
        action->setText(JobViewActivities::nameFromActivity(activity));
        action->setCheckable(true);
        connect(action, &QAction::triggered, [this, activity]() {
            // apply activity to JobView
            setActivity(activity);
            // store activity in JobItem
            if (selectedJobItem()) {
                selectedJobItem()->setActivity(JobViewActivities::nameFromActivity(activity));
                gProjectDocument.value()->setModified();
            }
        });
        m_activityActions.addAction(action);
    }
}

//! Connects signals related to JobItem
void JobView::connectJobRelated()
{
    // Focus request: JobModel -> this
    connect(m_document->jobModel(), &JobModel::focusRequest, this, &JobView::onFocusRequest);

    // JobItem selection: JobSelectorWidget -> this
    connect(m_jobSelector, &JobSelectorWidget::selectedJobsChanged, this,
            &JobView::onSelectionChanged);

    connect(m_fitActivityPanel, &FitActivityPanel::showLog, m_jobMessagePanel,
            &JobMessagePanel::setLog);
}

//! Sets appropriate activity and presentation for new JobItem

void JobView::setActivityAndPresentationForNewJob(JobItem* jobItem)
{
    if (!jobItem)
        return;

    JobViewActivity newActivity = JobViewActivity::JobView;

    // save in current job
    jobItem->setActivity(JobViewActivities::nameFromActivity(newActivity));

    // apply activity to show the corresponding docks
    setActivity(newActivity);

    // set presentation and save it for job
    m_jobResultsPresenter->setPresentationForActivity(newActivity);

    // set fitting presentation if the simulation has data
    if (jobItem->isValidForFitting())
        m_jobResultsPresenter->setPresentationForActivity(JobViewActivity::Fitting);
}

void JobView::resetLayout()
{
    m_docks->resetLayout();
    setActivity(JobViewActivity::JobView);
}
