/*=========================================================================
 *
 *  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
 *
 *         https://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 itkValarrayImageContainer_h
#define itkValarrayImageContainer_h

#include "itkObject.h"
#include "itkObjectFactory.h"

#include <utility>
#include <valarray>

namespace itk
{
/** \class ValarrayImageContainer
 *  \brief Defines a front-end to the std\\::\<valarray\> container that conforms to the
 *         ImageContainerInterface.
 *
 * This is a full-fledged Object, so
 * there is modification time, debug, and reference count information.
 *
 * \tparam TElementIdentifier
 *    An INTEGRAL type for use in indexing the valarray.
 *    It must have a \< operator defined for ordering.
 *
 * \tparam TElement
 *    The element type stored in the container.
 *
 * \ingroup ImageObjects
 * \ingroup DataRepresentation
 * \ingroup ITKCommon
 */
template <typename TElementIdentifier, typename TElement>
class ITK_TEMPLATE_EXPORT ValarrayImageContainer
  : public Object
  , private std::valarray<TElement>
{
public:
  /** Standard class type aliases. */
  using Self = ValarrayImageContainer;
  using Superclass = Object;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Save the template parameters. */
  using ElementIdentifier = TElementIdentifier;
  using Element = TElement;

private:
  /** Quick access to the STL valarray type that was inherited. */
  using ValarrayType = std::valarray<Element>;

protected:
  /** Provide pass-through constructors corresponding to all the STL
   * valarray constructors.  These are for internal use only since
   * this is also an Object which must be constructed through the
   * "New()" routine. */
  ValarrayImageContainer()
    : ValarrayType()
  {}
  ValarrayImageContainer(unsigned long n)
    : ValarrayType(n)
  {}
  ValarrayImageContainer(unsigned long n, const Element & x)
    : ValarrayType(n, x)
  {}
  ValarrayImageContainer(const Self & r)
    : ValarrayType(r)
  {}

public:
  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** \see LightObject::GetNameOfClass() */
  itkOverrideGetNameOfClassMacro(ValarrayImageContainer);

  /** Index operator. This version can be an lvalue. */
  TElement & operator[](const ElementIdentifier id) { return this->ValarrayType::operator[](id); }

  /** Index operator. This version can only be an rvalue */
  const TElement & operator[](const ElementIdentifier id) const { return this->ValarrayType::operator[](id); }

  /** Return a pointer to the beginning of the buffer.  This is used by
   * the image iterator class. */
  TElement *
  GetBufferPointer()
  {
    if (this->Size() > 0)
    {
      return &(this->ValarrayType::operator[](0));
    }
    else
    {
      return nullptr;
    }
  }

  /** Get the number of elements currently stored in the container. */
  unsigned long
  Size() const
  {
    return static_cast<unsigned long>(this->ValarrayType::size());
  }

  /** Tell the container to allocate enough memory to allow at least
   * as many elements as the size given to be stored.  This is NOT
   * guaranteed to actually allocate any memory, but is useful if the
   * implementation of the container allocates contiguous storage. */
  void
  Reserve(ElementIdentifier num)
  {
    this->ValarrayType::resize(num);
  }

  /** Tell the container to try to minimize its memory usage for storage of
   * the current number of elements.  This is NOT guaranteed to decrease
   * memory usage. */
  void
  Squeeze()
  {
    this->ValarrayType::resize(this->ValarrayType::size());
  }

  /** Tell the container to release any of its allocated memory. */
  void
  Initialize()
  {
    this->ValarrayType::resize(0);
  }

  /** Tell the container to release any of its allocated memory. */
  void
  Fill(const TElement & value)
  {
    this->ValarrayType::operator=(value);
  }

public:
  /** PrintSelf routine. Normally this is a protected internal method. It is
   * made public here so that Image can call this method.  Users should not
   * call this method but should call Print() instead.  */
  void
  PrintSelf(std::ostream & os, Indent indent) const override
  {
    Object::PrintSelf(os, indent);
    // Print out the pointer to bulk data memory. We use const_cast<> to
    // cast away the constness so we can call GetBufferPointer()
    os << indent << "Pointer: " << const_cast<ValarrayImageContainer *>(this)->GetBufferPointer() << std::endl;

    os << indent << "Size: " << this->Size() << std::endl;
  }
};
} // end namespace itk

#endif
