//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/Detector/SimulationAreaIterator.cpp
//! @brief     Implements class SimulationAreaIterator.
//!
//! @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 "Device/Detector/SimulationAreaIterator.h"
#include "Device/Detector/IDetector.h"
#include "Device/Mask/DetectorMask.h"

SimulationAreaIterator::SimulationAreaIterator(const IDetector* detector, size_t start_at_index)
    : m_detector(detector)
    , m_index(start_at_index)
    , m_maxIndex(m_detector->sizeOfRegionOfInterest())
{
    if (m_index > m_maxIndex)
        throw std::runtime_error("SimulationAreaIterator::SimulationAreaIterator() "
                                 "-> Error. Invalid initial index");

    if (m_index != m_maxIndex && isMasked(m_index))
        m_index = nextIndex(m_index);
}

SimulationAreaIterator SimulationAreaIterator::createBegin(const IDetector* detector)
{
    return SimulationAreaIterator(detector, 0);
}

SimulationAreaIterator SimulationAreaIterator::createEnd(const IDetector* detector)
{
    return SimulationAreaIterator(detector, detector->sizeOfRegionOfInterest());
}

SimulationAreaIterator SimulationAreaIterator::createEnd() const
{
    return createEnd(m_detector);
}

size_t SimulationAreaIterator::roiIndex() const
{
    return m_index;
}

size_t SimulationAreaIterator::detectorIndex() const
{
    return m_detector->regionOfInterestIndexToDetectorIndex(m_index);
}

SimulationAreaIterator& SimulationAreaIterator::operator++()
{
    size_t index = nextIndex(m_index);
    if (index != m_index)
        m_index = index;

    return *this;
}

size_t SimulationAreaIterator::nextIndex(size_t currentIndex)
{
    // #baROI + this can be optimized: Check whether a RegionOfInterest is present, then do not
    // check every single point

    size_t result = ++currentIndex;
    if (result >= m_maxIndex)
        return m_maxIndex;

    while (isMasked(result)) {
        ++result;
        if (result == m_maxIndex)
            break;
    }
    return result;
}

bool SimulationAreaIterator::isMasked(size_t index) const
{
    const auto* masks = m_detector->detectorMask();
    const auto detectorIndex = m_detector->regionOfInterestIndexToDetectorIndex(index);
    return (masks && masks->isMasked(detectorIndex));
}
