//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Mask/MaskResultsPresenter.cpp
//! @brief     Implements class MaskResultsPresenter
//!
//! @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/Mask/MaskResultsPresenter.h"
#include "Device/Data/Datafield.h"
#include "Device/Mask/DetectorMask.h"
#include "Device/Mask/IShape2D.h"
#include "GUI/Model/Data/IntensityDataItem.h"
#include "GUI/Model/Data/MaskItems.h"
#include <QVBoxLayout>

MaskResultsPresenter::MaskResultsPresenter(QWidget* parent)
    : QObject(parent)
    , m_interpolation_flag_backup(false)
{
}

MaskResultsPresenter::~MaskResultsPresenter() = default;

void MaskResultsPresenter::setMaskContext(IntensityDataItem* intensityItem)
{
    m_intensityDataItem = intensityItem;
}

void MaskResultsPresenter::resetContext()
{
    setMaskContext(nullptr);
}

void MaskResultsPresenter::updatePresenter(MaskEditorFlags::PresentationType presentationType)
{
    if (!m_intensityDataItem->maskContainerItem()) // TODO: check!
        return;

    if (presentationType == MaskEditorFlags::MASK_PRESENTER)
        setShowMaskMode();
    else if (presentationType == MaskEditorFlags::MASK_EDITOR)
        setOriginalMode();
}

//! Update IntensityDataItem to represent masked areas. Corresponding
//! bins of Datafield will be put to zero.

void MaskResultsPresenter::setShowMaskMode()
{
    if (Datafield* maskedData = createMaskPresentation()) {
        backup_data();
        m_intensityDataItem->setDatafield(maskedData);
        m_intensityDataItem->setInterpolated(false);
    } else {
        m_dataBackup.reset();
    }
}

//! Restores original state of IntensityDataItem

void MaskResultsPresenter::setOriginalMode()
{
    if (m_dataBackup) {
        m_intensityDataItem->setDatafield(m_dataBackup->clone());
        m_intensityDataItem->setInterpolated(m_interpolation_flag_backup);
    }
}

void MaskResultsPresenter::backup_data()
{
    m_interpolation_flag_backup = m_intensityDataItem->isInterpolated();
    m_dataBackup.reset(m_intensityDataItem->c_field()->clone());
}

//! Constructs Datafield which contains original intensity data except masked areas,
//! and areas outside of ROI, where bin content is set to zero.

Datafield* MaskResultsPresenter::createMaskPresentation() const
{
    // Requesting mask information
    std::unique_ptr<IShape2D> roi;
    Datafield* result = m_intensityDataItem->c_field()->clone();
    DetectorMask detectorMask(result->axis(0), result->axis(1));
    const double scale = 1.0;
    const QVector<MaskItem*> maskItems = m_intensityDataItem->maskContainerItem()->maskItems();
    for (auto maskIter = maskItems.rbegin(); maskIter != maskItems.rend(); maskIter++)
        if ((*maskIter)->isVisible()) {
            if (auto* roiItem = dynamic_cast<RegionOfInterestItem*>((*maskIter)))
                roi = roiItem->createShape(scale);
            else {
                std::unique_ptr<IShape2D> shape((*maskIter)->createShape(scale));
                detectorMask.addMask(*shape, (*maskIter)->maskValue());
            }
        }

    // ROI mask has to be the last one, it can not be "unmasked" by other shapes
    if (roi)
        detectorMask.addMask(*roi, true);

    if (!detectorMask.hasMasks())
        return nullptr;

    for (size_t i = 0; i < result->size(); ++i)
        if (detectorMask.isMasked(i))
            (*result)[i] = 0.0;

    return result;
}
