//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Mask/IShape2DView.cpp
//! @brief     Implements interface IShape2DView.
//!
//! @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/IShape2DView.h"
#include "GUI/Model/Data/MaskItems.h"
#include "GUI/View/Mask/ISceneAdaptor.h"
#include <QAction>
#include <QGraphicsScene>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>

namespace {

bool itemMaskValue(const MaskItemObject* item)
{
    if (const auto* maskItem = dynamic_cast<const MaskItem*>(item))
        return maskItem->maskValue();
    return false;
}

} // namespace

IShape2DView::IShape2DView(MaskItemObject* item)
    : m_adaptor(nullptr)
    , m_block_on_property_change(false)
{
    ASSERT(item);

    connect(this, &IShape2DView::xChanged, this, &IShape2DView::onChangedX, Qt::UniqueConnection);
    connect(this, &IShape2DView::yChanged, this, &IShape2DView::onChangedY, Qt::UniqueConnection);

    connect(item, &MaskItemObject::maskGeometryChanged, this, &IShape2DView::onGeometryChange,
            Qt::UniqueConnection);
    connect(item, &MaskItemObject::maskVisibilityChanged, this, &IShape2DView::onVisibilityChange,
            Qt::UniqueConnection);
}

QRectF IShape2DView::boundingRect() const
{
    return m_bounding_rect;
}

void IShape2DView::setSceneAdaptor(const ISceneAdaptor* adaptor)
{
    ASSERT(adaptor);

    if (m_adaptor != adaptor) {
        if (m_adaptor)
            disconnect(m_adaptor, &ISceneAdaptor::update_request, this, &IShape2DView::update_view);

        m_adaptor = adaptor;
        connect(m_adaptor, &ISceneAdaptor::update_request, this, &IShape2DView::update_view,
                Qt::UniqueConnection);
        update_view();
    }
}

void IShape2DView::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    const bool isMasked = itemMaskValue(parameterizedItem());
    painter->setBrush(MaskEditorHelper::getMaskBrush(isMasked));
    painter->setPen(MaskEditorHelper::getMaskPen(isMasked));
    painter->setRenderHints(QPainter::Antialiasing);
    painter->drawPath(maskedShape());
}

QPainterPath IShape2DView::maskedShape() const
{
    static const QSet<MaskEditorHelper::EViewTypes> relevantMaskTypes = {
        MaskEditorHelper::RECTANGLE, MaskEditorHelper::POLYGON, MaskEditorHelper::VERTICALLINE,
        MaskEditorHelper::HORIZONTALLINE, MaskEditorHelper::ELLIPSE};

    QPainterPath resultingShape = mapToScene(shape());
    for (const QGraphicsItem* item : scene()->items()) {
        const auto* view = dynamic_cast<const IShape2DView*>(item);
        if (!view)
            continue;
        if (!relevantMaskTypes.contains((MaskEditorHelper::EViewTypes)view->type()))
            continue;
        if (itemMaskValue(view->parameterizedItem()))
            continue;
        if (const auto* maskItem = dynamic_cast<const MaskItem*>(view->parameterizedItem()))
            if (!maskItem->isVisible())
                continue;
        if (!(view->zValue() > zValue()))
            continue;

        const QPainterPath maskItemShape = view->mapToScene(view->shape());
        if (!maskItemShape.intersects(resultingShape))
            continue;

        // Item lays on top and is non-masking -> subtract the path of the item
        resultingShape = resultingShape.subtracted(maskItemShape);
    }

    return mapFromScene(resultingShape);
}

qreal IShape2DView::toSceneX(qreal value) const
{
    return m_adaptor ? m_adaptor->toSceneX(value) : value;
}

qreal IShape2DView::toSceneY(qreal value) const
{
    return m_adaptor ? m_adaptor->toSceneY(value) : value;
}

qreal IShape2DView::fromSceneX(qreal value) const
{
    return m_adaptor ? m_adaptor->fromSceneX(value) : value;
}

qreal IShape2DView::fromSceneY(qreal value) const
{
    return m_adaptor ? m_adaptor->fromSceneY(value) : value;
}

void IShape2DView::addView(IShape2DView* childView)
{
    if (!childItems().contains(childView))
        childView->setParentItem(this);
}

void IShape2DView::setBlockOnProperty(bool value)
{
    m_block_on_property_change = value;
}

bool IShape2DView::blockOnProperty() const
{
    return m_block_on_property_change;
}

void IShape2DView::onGeometryChange()
{
    if (m_block_on_property_change)
        return;

    m_block_on_property_change = true;
    onPropertyChange();
    m_block_on_property_change = false;
}

void IShape2DView::onVisibilityChange()
{
    if (m_block_on_property_change)
        return;

    m_block_on_property_change = true;

    if (auto* maskItem = dynamic_cast<MaskItem*>(parameterizedItem()))
        this->setVisible(maskItem->isVisible());
    else
        this->setVisible(false);

    onPropertyChange();
    update();

    m_block_on_property_change = false;
}
