//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Data/MaskUnitsConverter.cpp
//! @brief     Implements class MaskUnitsConverter
//!
//! @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/Model/Data/MaskUnitsConverter.h"
#include "Base/Axis/FrameUtil.h"
#include "Device/Data/Datafield.h"
#include "GUI/Model/Data/IntensityDataItem.h"
#include "GUI/Model/Data/ProjectionItems.h"
MaskUnitsConverter::MaskUnitsConverter()
    : m_data(nullptr)
    , m_direction(UNDEFINED)
{
}

//! Converts all masks on board of IntensityDataItem into bin-fraction coordinates.

void MaskUnitsConverter::convertToNbins(IntensityDataItem* intensityData)
{
    m_direction = TO_NBINS;
    convertIntensityDataItem(intensityData);
}

//! Converts all masks on board of IntensityDataItem from bin-fraction coordinates to coordinates
//! of axes currently defined in Datafield.

void MaskUnitsConverter::convertFromNbins(IntensityDataItem* intensityData)
{
    m_direction = FROM_NBINS;
    convertIntensityDataItem(intensityData);
}

//! Converts all masks on board of IntensityDataItem from/to bin-fraction coordinates

void MaskUnitsConverter::convertIntensityDataItem(IntensityDataItem* intensityData)
{
    if (!intensityData || !intensityData->c_field())
        return;

    m_data = intensityData->c_field();

    if (intensityData->maskContainerItem())
        for (auto* maskItem : intensityData->maskContainerItem()->maskItems())
            convertMask(maskItem);

    if (intensityData->projectionContainerItem())
        for (auto* maskItem : intensityData->projectionContainerItem()->maskItems())
            convertMask(maskItem);
}

//! Converts single mask from/to bin-fraction coordinates

void MaskUnitsConverter::convertMask(MaskItem* maskItem)
{
    if (auto* rectItem = dynamic_cast<RectangleItem*>(maskItem)) {
        rectItem->setXLow(convert(rectItem->xLow(), Axis::X));
        rectItem->setYLow(convert(rectItem->yLow(), Axis::Y));
        rectItem->setXUp(convert(rectItem->xUp(), Axis::X));
        rectItem->setYUp(convert(rectItem->yUp(), Axis::Y));
    } else if (auto* poly = dynamic_cast<PolygonItem*>(maskItem)) {
        for (PolygonPointItem* pointItem : poly->points()) {
            pointItem->setPosX(convert(pointItem->posX(), Axis::X));
            pointItem->setPosY(convert(pointItem->posY(), Axis::Y));
        }
    } else if (auto* vlineItem = dynamic_cast<VerticalLineItem*>(maskItem)) {
        vlineItem->setPosX(convert(vlineItem->posX(), Axis::X));
    } else if (auto* hlineItem = dynamic_cast<HorizontalLineItem*>(maskItem)) {
        hlineItem->setPosY(convert(hlineItem->posY(), Axis::Y));
    } else if (auto* ellItem = dynamic_cast<EllipseItem*>(maskItem)) {
        double xc = ellItem->xCenter();
        double yc = ellItem->yCenter();
        double xR = ellItem->xRadius();
        double yR = ellItem->yRadius();

        double x2 = xc + xR;
        double y2 = yc + yR;

        if (m_direction == TO_NBINS) {
            FrameUtil::coordinatesToBinf(xc, yc, m_data->frame());
            FrameUtil::coordinatesToBinf(x2, y2, m_data->frame());
        } else {
            FrameUtil::coordinatesFromBinf(xc, yc, m_data->frame());
            FrameUtil::coordinatesFromBinf(x2, y2, m_data->frame());
        }
        ellItem->setXCenter(xc);
        ellItem->setYCenter(yc);
        ellItem->setXRadius(x2 - xc);
        ellItem->setYRadius(y2 - yc);
    }
}

//! Convert value of axis from/to bin-fraction coordinates.

double MaskUnitsConverter::convert(double value, Axis axis)
{
    auto axis_index = static_cast<size_t>(axis);

    ASSERT(m_data);
    ASSERT(axis_index == 0 || axis_index == 1);

    if (m_direction == TO_NBINS)
        return FrameUtil::coordinateToBinf(value, m_data->axis(axis_index));
    if (m_direction == FROM_NBINS)
        return FrameUtil::coordinateFromBinf(value, m_data->axis(axis_index));
    ASSERT(false);
}
