//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/View/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/View/JobView.h"
#include "GUI/Model/Data/DataItem.h"
#include "GUI/Model/Job/JobItem.h"
#include "GUI/Model/Job/JobsSet.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sim/SimulationOptionsItem.h"
#include "GUI/View/Dock/DocksController.h"
#include "GUI/View/Fit/FitActivityPanel.h"
#include "GUI/View/FitMessage/JobMessagePanel.h"
#include "GUI/View/Frame/StackedFrames.h"
#include "GUI/View/Job/JobViewActivities.h"
#include "GUI/View/Job/JobsPanel.h"
#include "GUI/View/Tuning/JobRealTimeWidget.h"

JobView::JobView(QProgressBar* progressBar)
    : m_docks(new DocksController(this))
    , m_data_frames(new StackedJobFrames)
    , m_fit_activity_panel(new FitActivityPanel)
    , m_job_message_panel(new JobMessagePanel)
    , m_activity_actions(this)
{
    //... Actions

    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));
                gDoc->setModified();
            }
        });
        m_activity_actions.addAction(action);
    }

    //... Subwindows

    m_jobs_panel = new JobsPanel(this);
    m_job_real_time_widget = new JobRealTimeWidget(gDoc->jobsRW(), this);

    m_docks->addWidget(JobViewFlags::JOB_LIST_DOCK, m_jobs_panel, Qt::LeftDockWidgetArea);
    m_docks->addWidget(JobViewFlags::REAL_TIME_DOCK, m_job_real_time_widget,
                       Qt::RightDockWidgetArea);
    m_docks->addWidget(JobViewFlags::FIT_PANEL_DOCK, m_fit_activity_panel, Qt::RightDockWidgetArea);
    m_docks->addWidget(JobViewFlags::JOB_MESSAGE_DOCK, m_job_message_panel,
                       Qt::BottomDockWidgetArea);

    m_fit_activity_panel->setRealTimeWidget(m_job_real_time_widget);

    setCentralWidget(m_data_frames);

    resetLayout();

    //... Connects signals related to JobItem

    // Focus request: JobsSet -> this
    connect(gDoc->jobs(), &JobsSet::newJobFinished,
            [this](JobItem* job_item) { switchToNewJob(job_item); });

    // JobItem selection: JobsPanel -> this
    connect(m_jobs_panel, &JobsPanel::selectedJobsChanged, this, &JobView::switchWidgetsToJob);

    connect(m_fit_activity_panel, &FitActivityPanel::showLog, m_job_message_panel,
            &JobMessagePanel::setLog);

    connect(gDoc->jobs(), &JobsSet::globalProgress, [pb = progressBar](int progress) {
        if (progress < 0 || progress >= 100)
            pb->hide();
        else {
            pb->show();
            pb->setValue(progress);
        }
    });
}

void JobView::fillViewMenu(QMenu* menu)
{
    menu->addActions(m_activity_actions.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);
}

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

void JobView::switchWidgetsToJob()
{
    m_data_frames->showCurrentFrame();
    m_job_real_time_widget->setJobItem(selectedJobItem());
    m_fit_activity_panel->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));
}

void JobView::switchToNewJob(JobItem* job_item)
{
    ASSERT(job_item);
    if (!job_item->simulationOptionsItem()->runImmediately())
        return;

    if (!job_item->activity().isEmpty())
        return;
    // only for new job:

    m_jobs_panel->makeNewJobItemSelected(job_item);

    JobViewActivity newActivity = JobViewActivity::JobView;

    job_item->setActivity(JobViewActivities::nameFromActivity(newActivity));

    setActivity(newActivity);

    m_data_frames->showCurrentFrame();

    emit requestSwitchToJobView();
}


//! 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_activity_actions.actions()[static_cast<int>(activity)]->setChecked(true);
}

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

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

    return nullptr;
}

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