/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkSliceIterator_h
#define itkSliceIterator_h

#include "itkMacro.h"
#include "itkIntTypes.h"
#include <valarray>

namespace itk
{
/** \class SliceIterator
 * \brief A flexible iterator for itk containers(i.e. itk::Neighborhood)
 * that support pixel access through operator[].
 *
 * SliceIterator allows iteration along a std::slice through the container.
 * A slice is a construct that defines a starting position, stride length
 * (distance between adjacent elements), and a length.
 *
 * Any container with operator[] is supported.  Because it uses this interface
 * the iterator is only as efficient as the implementation of a container's
 * operator[] method.
 *
 * References:
 * Modelled after a slice iterator proposed by Bjarne Stroustrup
 * in C++ Programming Language, Third Edition. Bjarne Stroustrup.  Addison
 * Wesley, Reading, MA. 1997.
 *
 * \ingroup Iterators
 * \ingroup ITKCommon
 */
template <typename TPixel, typename TContainer>
class SliceIterator
{
public:
  /** Constructor. */
  SliceIterator(TContainer * n, std::slice s)
    : m_ContainerPointer(n)
    , m_Pos(0)
    , m_Slice(s)
  {}

  /** Returns a SliceIterator that points to the beginning of the slice. */
  SliceIterator
  Begin()
  {
    SliceIterator ans = *this;

    ans.m_Pos = 0;
    return ans;
  }

  /** Returns a SliceIterator that points to one past the end of the slice. */
  SliceIterator
  End()
  {
    SliceIterator ans = *this;

    ans.m_Pos = static_cast<OffsetValueType>(m_Slice.size());
    return ans;
  }

  /** Increments the iterator. */
  SliceIterator
  operator++()
  {
    m_Pos++;
    return *this;
  }

  /** Increments the iterator. */
  SliceIterator
  operator++(int)
  {
    SliceIterator ans = *this;

    m_Pos++;
    return ans;
  }

  /** Returns the element at position n of the slice. Sets the
   * iterator to point to position n. */
  TPixel & operator[](OffsetValueType n) { return this->Loc(m_Pos = n); }

  /** Dereferences the iterator, returning the value that it points
   * to. */
  TPixel & operator*() { return Loc(m_Pos); }

  /** Returns the logical && of the boolean == of two slice iterator positions,
   * stride, and start locations. */
  bool
  operator==(const SliceIterator & orig)
  {
    return orig.m_Pos == this->m_Pos && orig.m_Slice.stride() == this->m_Slice.stride() &&
           orig.m_Slice.start() == this->m_Slice.start();
  }

  /** Returns the logical inverse of the boolean == of two slice iterators. */
  bool
  operator!=(const SliceIterator & orig)
  {
    return !operator==(orig);
  }

  /** Returns the boolean < of two slice iterator positions.  Result
   * is only true if the slice iterators have the same stride and
   * start location. */
  bool
  operator<(const SliceIterator & orig)
  {
    return this->m_Pos < orig.m_Pos && this->m_Slice.stride() == orig.m_Slice.stride() &&
           this->m_Slice.start() == orig.m_Slice.start();
  }

private:
  /** Returns the value located at position n of the slice. */
  TPixel &
  Loc(OffsetValueType n) const
  {
    const auto start = static_cast<OffsetValueType>(m_Slice.start());
    const auto stride = static_cast<OffsetValueType>(m_Slice.stride());

    return (*m_ContainerPointer)[start + n * stride];
  }

  /** Pointer to the container referenced by the slice iterator. */
  TContainer * m_ContainerPointer;

  /** Current position within the slice. */
  OffsetValueType m_Pos;

  /** Slice structure information. */
  std::slice m_Slice;
};
} // end namespace itk

#endif
