//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Detector/DetectorItem.cpp
//! @brief     Implements classes DetectorItems.
//!
//! @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/Detector/DetectorItem.h"
#include "Base/Axis/Scale.h"
#include "Base/Const/Units.h"
#include "Base/Util/Assert.h"
#include "Device/Detector/SphericalDetector.h"
#include "Device/Mask/IShape2D.h"
#include "Device/Resolution/ResolutionFunction2DGaussian.h"
#include "GUI/Model/Mask/MasksSet.h"
#include "GUI/Model/Util/Backup.h"

namespace {
namespace Tag {

const QString ExpandResolutionFuncGroupbox("ExpandResolutionFuncGroupbox");
const QString AlphaAxis("AlphaAxis");
const QString PhiAxis("PhiAxis");
const QString ResolutionFunction("ResolutionFunction");
const QString MaskContainer("MaskContainer");

} // namespace Tag
} // namespace


DetectorItem::DetectorItem()
    : m_phi_axis("phi", "deg", -1, 1, RealLimits::limited(-90, 90))
    , m_alpha_axis("alpha", "deg", 0, 2, RealLimits::limited(-90, 90))
    , m_masks(std::make_unique<MasksSet>())
{
    m_resolution_function.simpleInit("Resolution function", "Detector resolution function",
                                     ResolutionFunctionCatalog::Type::None);
}

DetectorItem::~DetectorItem() = default;

std::unique_ptr<IDetector> DetectorItem::createDetectorFromAxes() const
{
    return std::make_unique<SphericalDetector>(
        m_phi_axis.nbins(), Units::deg2rad(m_phi_axis.min().dVal()),
        Units::deg2rad(m_phi_axis.max().dVal()), m_alpha_axis.nbins(),
        Units::deg2rad(m_alpha_axis.min().dVal()), Units::deg2rad(m_alpha_axis.max().dVal()));
}

Frame DetectorItem::createFrame() const
{
    return {createDetectorFromAxes()->frame()};
}

std::unique_ptr<IDetector> DetectorItem::createDetector() const
{
    auto result = createDetectorFromAxes();

    for (const MaskItem* t : *m_masks) {
        if (t->isVisible()) {
            if (const auto* ii = dynamic_cast<const RegionOfInterestItem*>(t)) {
                result->setRegionOfInterest(ii->xLow().dVal(), ii->yLow().dVal(), ii->xUp().dVal(),
                                            ii->yUp().dVal());
            } else {
                std::unique_ptr<IShape2D> shape(t->createShape());
                result->addMask(*shape, t->maskValue());
            }
        }
    }

    if (auto resFunc = createResolutionFunction())
        result->setResolutionFunction(*resFunc);

    return result;
}

void DetectorItem::setMasks(const MasksSet* source)
{
    GUI::Util::copyContents(source, m_masks.get());
}

std::unique_ptr<IResolutionFunction2D> DetectorItem::createResolutionFunction() const
{
    return m_resolution_function.certainItem()->createResolutionFunction();
}

void DetectorItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedElement(w, Tag::PhiAxis, m_phi_axis);
    XML::writeTaggedElement(w, Tag::AlphaAxis, m_alpha_axis);
    XML::writeTaggedElement(w, Tag::ResolutionFunction, m_resolution_function);
    XML::writeTaggedValue(w, Tag::ExpandResolutionFuncGroupbox, expandResolutionFunc);
    XML::writeTaggedElement(w, Tag::MaskContainer, *m_masks);
}

void DetectorItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::PhiAxis)
            XML::readTaggedElement(r, tag, m_phi_axis);
        else if (tag == Tag::AlphaAxis)
            XML::readTaggedElement(r, tag, m_alpha_axis);
        else if (tag == Tag::ResolutionFunction)
            XML::readTaggedElement(r, tag, m_resolution_function);
        else if (tag == Tag::ExpandResolutionFuncGroupbox)
            expandResolutionFunc = XML::readTaggedBool(r, tag);
        else if (tag == Tag::MaskContainer)
            XML::readTaggedElement(r, tag, *m_masks);
        else
            r->skipCurrentElement();
    }
}
