//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Projection/ProjectionsEditorCanvas.cpp
//! @brief     Implements class ProjectionsEditorCanvas
//!
//! @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/Projection/ProjectionsEditorCanvas.h"
#include "GUI/Model/Data/IntensityDataItem.h"
#include "GUI/Model/Data/MaskItems.h"
#include "GUI/Model/Data/ProjectionItems.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/View/Mask/MaskGraphicsScene.h"
#include "GUI/View/Mask/MaskGraphicsView.h"
#include "GUI/View/PlotUtil/ColorMap.h"
#include "GUI/View/PlotUtil/PlotStatusLabel.h"
#include "GUI/View/PlotUtil/ScientificPlotEvent.h"
#include <QItemSelectionModel>
#include <QVBoxLayout>

ProjectionsEditorCanvas::ProjectionsEditorCanvas(QWidget* parent)
    : QWidget(parent)
    , m_scene(new MaskGraphicsScene(this))
    , m_view(new MaskGraphicsView(m_scene))
    , m_statusLabel(new PlotStatusLabel(nullptr, this))
    , m_currentActivity(MaskEditorFlags::HORIZONTAL_LINE_MODE)
{
    setObjectName("MaskEditorCanvas");
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    auto* mainLayout = new QVBoxLayout;
    mainLayout->addWidget(m_view);
    mainLayout->addWidget(m_statusLabel);
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);
    setLayout(mainLayout);

    connect(m_view, &MaskGraphicsView::changeActivityRequest, this,
            &ProjectionsEditorCanvas::changeActivityRequest);
    connect(m_view, &MaskGraphicsView::deleteSelectedRequest, this,
            &ProjectionsEditorCanvas::deleteSelectedRequest);

    // automatically switch to the appropriate projection tab
    connect(m_scene, &MaskGraphicsScene::lineItemMoved, this,
            &ProjectionsEditorCanvas::onLineItemMoved, Qt::UniqueConnection);
}

void ProjectionsEditorCanvas::setContext(IntensityDataItem* intensityItem)
{
    ProjectionContainerItem* containerItem = intensityItem->getOrCreateProjectionContainerItem();
    ASSERT(containerItem);

    m_scene->setMaskContext(intensityItem, containerItem);
    m_scene->setSelectionModel(containerItem->selectionModel());

    // notify ProjectionPlot about the changes
    connect(m_scene, &MaskGraphicsScene::lineItemProcessed, intensityItem,
            &IntensityDataItem::projectionCreated, Qt::UniqueConnection);
    connect(m_scene, &MaskGraphicsScene::lineItemMoved, intensityItem,
            &IntensityDataItem::projectionPositionChanged, Qt::UniqueConnection);
    connect(m_scene, &MaskGraphicsScene::lineItemDeleted, intensityItem,
            &IntensityDataItem::projectionGone, Qt::UniqueConnection);

    m_view->updateSize(m_view->size());

    m_intensityDataItem = intensityItem;

    setColorMap(m_scene->colorMap());

    onLeavingColorMap();
    m_scene->onActivityModeChanged(m_currentActivity);
}

void ProjectionsEditorCanvas::resetContext()
{
    m_intensityDataItem = nullptr;
    setConnected(false);
    m_colorMap = nullptr;
    m_scene->resetContext();
}

void ProjectionsEditorCanvas::onEnteringColorMap()
{
    if (m_liveProjection || m_block_update)
        return;

    m_block_update = true;

    if (m_currentActivity == MaskEditorFlags::HORIZONTAL_LINE_MODE)
        m_liveProjection = std::make_unique<HorizontalLineItem>();
    else if (m_currentActivity == MaskEditorFlags::VERTICAL_LINE_MODE)
        m_liveProjection = std::make_unique<VerticalLineItem>();

    if (m_liveProjection) {
        m_liveProjection->setIsVisible(false);
        m_liveProjection->setParent(this);

        // notify ProjectionPlot about the changes
        connect(m_liveProjection.get(), &MaskItem::maskGeometryChanged, m_intensityDataItem,
                &IntensityDataItem::projectionPositionChanged, Qt::UniqueConnection);
    }

    m_block_update = false;
}

void ProjectionsEditorCanvas::onLeavingColorMap()
{
    if (m_block_update)
        return;

    m_block_update = true;

    if (m_liveProjection) {
        disconnect(m_liveProjection.get(), nullptr, m_intensityDataItem, nullptr);
        emit m_intensityDataItem->projectionGone(m_liveProjection.get());
        m_liveProjection.reset();
    }

    m_block_update = false;
}

void ProjectionsEditorCanvas::onPositionChanged(double x, double y)
{
    if (m_block_update)
        return;

    m_block_update = true;

    if (m_liveProjection) {
        if (m_currentActivity == MaskEditorFlags::HORIZONTAL_LINE_MODE)
            dynamic_cast<HorizontalLineItem*>(m_liveProjection.get())->setPosY(y);
        else if (m_currentActivity == MaskEditorFlags::VERTICAL_LINE_MODE)
            dynamic_cast<VerticalLineItem*>(m_liveProjection.get())->setPosX(x);
    }

    m_block_update = false;
}

void ProjectionsEditorCanvas::onResetViewRequest()
{
    m_view->onResetViewRequest();
    m_intensityDataItem->resetView();
    gProjectDocument.value()->setModified();
}

void ProjectionsEditorCanvas::onActivityModeChanged(MaskEditorFlags::Activity value)
{
    m_currentActivity = value;
    m_scene->onActivityModeChanged(value);
    onLeavingColorMap();
}

void ProjectionsEditorCanvas::onLineItemMoved(MaskItemObject* sender)
{
    if (dynamic_cast<HorizontalLineItem*>(sender)
        && (m_currentActivity != MaskEditorFlags::VERTICAL_LINE_MODE))
        emit changeProjectionsTabRequest(MaskEditorFlags::HORIZONTAL_LINE_MODE);
    if (dynamic_cast<VerticalLineItem*>(sender)
        && (m_currentActivity != MaskEditorFlags::HORIZONTAL_LINE_MODE))
        emit changeProjectionsTabRequest(MaskEditorFlags::VERTICAL_LINE_MODE);
}

void ProjectionsEditorCanvas::setColorMap(ColorMap* colorMap)
{
    ASSERT(colorMap);

    m_colorMap = colorMap;
    setConnected(true);

    m_statusLabel->reset();
    m_statusLabel->addPlot(colorMap);
}

void ProjectionsEditorCanvas::setConnected(bool isConnected)
{
    if (!m_colorMap)
        return;

    if (isConnected) {
        connect(m_colorMap->plotEvent(), &ScientificPlotEvent::enteringPlot, this,
                &ProjectionsEditorCanvas::onEnteringColorMap, Qt::UniqueConnection);
        connect(m_colorMap->plotEvent(), &ScientificPlotEvent::leavingPlot, this,
                &ProjectionsEditorCanvas::onLeavingColorMap, Qt::UniqueConnection);
        connect(m_colorMap->plotEvent(), &ScientificPlotEvent::positionChanged, this,
                &ProjectionsEditorCanvas::onPositionChanged, Qt::UniqueConnection);
        connect(m_colorMap, &ColorMap::marginsChanged, this,
                &ProjectionsEditorCanvas::marginsChanged, Qt::UniqueConnection);
    }

    else {
        disconnect(m_colorMap->plotEvent(), &ScientificPlotEvent::enteringPlot, this,
                   &ProjectionsEditorCanvas::onEnteringColorMap);
        disconnect(m_colorMap->plotEvent(), &ScientificPlotEvent::leavingPlot, this,
                   &ProjectionsEditorCanvas::onLeavingColorMap);
        disconnect(m_colorMap->plotEvent(), &ScientificPlotEvent::positionChanged, this,
                   &ProjectionsEditorCanvas::onPositionChanged);
        disconnect(m_colorMap, &ColorMap::marginsChanged, this,
                   &ProjectionsEditorCanvas::marginsChanged);
    }
}
