/*=========================================================================
 *
 *  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.
 *
 *=========================================================================*/
/*=========================================================================
 *
 *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
 *
 *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 *
 *  For complete copyright, license and disclaimer of warranty information
 *  please refer to the NOTICE file at the top of the ITK source tree.
 *
 *=========================================================================*/
#ifndef itkImageToImageFilterDetail_h
#define itkImageToImageFilterDetail_h

#include "itkImageRegion.h"
#include "itkSmartPointer.h"


/** ImageToImageFilterDetail namespace to house implementations of
 * functions that are dependent on dimension. These functions are
 * overloaded based on type unique for each different dimension.
 * These functions cannot be member functions of a class like
 * ImageToImageFilter since explicit instantiation of an
 * ImageToImageFilter would force the instantiation of all versions of
 * these overloaded functions.
 */
namespace itk::ImageToImageFilterDetail
{

/** \struct  DispatchBase
 * \brief Base class for a class used to dispatch to dimension specific implementations.
 *
 * DispatchBase is base class used as the default case when implementations
 * are dispatched to overloaded routines based on dimension.
 *
 * \sa Dispatch
 */
struct DispatchBase
{};

/** \brief Templated class to produce a unique type "true" and "false".
 *
 * BooleanDispatch is a templated class that produce a unique type for
 * for "true" and for "false".  These types may be used to decide which
 * version of an overloaded function to call.
 */
template <bool>
struct BooleanDispatch
{};

/** \brief Templated class to produce a unique type for each integer
 *
 * IntDispatch is a templated class that produces a unique
 * type for each integer.  IntDispatch is typically
 * used as a parameter to an overloaded function where a different
 * version of the routine would need to be called for each integer value.
 */
template <int>
struct IntDispatch : public DispatchBase
{};

/** \brief Templated class to produce a unique type for each unsigned integer (usually a dimension).
 *
 * UnsignedIntDispatch is a templated class that produces a unique
 * type for each unsigned integer.  UnsignedIntDispatch is typically
 * used as a parameter to an overloaded function where each version
 * of the overloaded function is for a unique dimension.  For
 * instance, an algorithm may provide two implementations: one
 * optimized for two-dimensional images and another for any of other
 * data dimension.  For instance:
 *
 *     void Calculate(const DispatchBase&);           // General ND version
 *     void Calculate(const UnsignedIntDispatch<2>&); // 2D optimized version
 */
template <unsigned int>
struct UnsignedIntDispatch : public DispatchBase
{};

/** \brief Templated class to produce a unique type for a pairing of booleans.
 *
 * BinaryBooleanDispatch is a templated class that produces a unique type
 * for each pairing of two boolean values ((true, true), (true, false),
 * (false, true), (false, false)).
 */
template <bool B1, bool B2>
struct BinaryBooleanDispatch
{
  /** Typedefs to extract the unique types for the first and second
        template parameters (true/false) */
  using FirstType = BooleanDispatch<B1>;
  using SecondType = BooleanDispatch<B2>;
};

/** \brief Templated class to produce a unique type for a pairing of integers.
 *
 * IntBooleanDispatch is a templated class that produces a unique type
 * for each pairing of two integer values.
 */
template <int D1, int D2>
struct BinaryIntDispatch
{
  /** Typedefs to extract the unique types for the first and second
        template parameters (unique type for the integer value) */
  using FirstType = IntDispatch<D1>;
  using SecondType = IntDispatch<D2>;
};

/** \brief Templated class to produce a unique type for a pairing of unsigned integers (usually two dimensions).
 *
 * BinaryUnsignedIntDispatch is a templated class that produces a
 * unique type for a pairing of unsigned integers.
 * BinaryUnsignedIntDispatch is typically used a parameter to an
 * overloaded function where each version of the overloaded function
 * is for a different pairing of unsigned integers.  This type may
 * be used to produce a unique type for an (input dimension, output
 * dimension) pairing.
 */
template <unsigned int D1, unsigned int D2>
struct BinaryUnsignedIntDispatch : public DispatchBase
{
  /** Typedefs to extract the unique types for the first and second
      template parameters (unique type for the unsigned integer value) */
  using FirstType = UnsignedIntDispatch<D1>;
  using SecondType = UnsignedIntDispatch<D2>;

  /** Helper types to determine whether the two integers are the same,
   * the first greater than the second, or the first less than the second.
   *
   * ComparisonType will be either IntDispatch<0>, IntDispatch<1>, or
   * IntDispatch<-1>.
   *
   * IntDispatch<0> means the two specified integers are the same.
   * IntDispatch<1> means the first integer is greater than the second
   * IntDispatch<-1> means the first integer is less than the second
   *
   * The FirstEqualsSecondType, FirstGreaterThanSecondType,
   * FirstLessThanSecondType type alias are provided as convenience types
   * which can be used to declare arguments to functions.  They themselves
   * do not indicate the relationship between D1 and D2.
   */
  using ComparisonType = IntDispatch<(D1 > D2) - (D1 < D2)>;
  using FirstEqualsSecondType = IntDispatch<0>;
  using FirstGreaterThanSecondType = IntDispatch<1>;
  using FirstLessThanSecondType = IntDispatch<-1>;
};

/**
 * Copy an image region (start index and size) for the case where
 * the source and destination region are the same dimension.  This
 * is a trivial copy of a region. This is an overloaded function.
 * The other versions of this functions handle the case where the
 * destination dimension is greater than the source dimension and
 * the case where the destination dimension is less than the source
 * dimension.
 *
 * Note that the use of source and destination reflect where where
 * is information is coming from and going to.  When used in the
 * pipeline mechanism, the region requested by the output of a
 * filter is used to define the region required on the input.  In
 * this case the output of the filter is the source region and the
 * input of the filter is the destination region.
 */
template <unsigned int D1, unsigned int D2>
void
ImageToImageFilterDefaultCopyRegion(const typename BinaryUnsignedIntDispatch<D1, D2>::FirstEqualsSecondType &,
                                    ImageRegion<D1> &       destRegion,
                                    const ImageRegion<D2> & srcRegion)
{
  destRegion = srcRegion;
}

/**
 * Copy an image region (start index and size) for the case where
 * the source region has a greater dimension than the destination
 * region.  This copies the first portion of the source into
 * destination. This is an overloaded function.  The other versions
 * of this functions handle the case where the source dimension is
 * less than the destination dimension and the case where the
 * destination and source are the same dimension.
 *
 * Note that the use of source and destination reflect where
 * where is information is coming from and going to.  When used
 * in the pipeline mechanism, the region requested by the output
 * of a filter is used to define the region required on the input.
 * In this case the output of the filter is the source and the
 * input of the filter is the destination.
 */
template <unsigned int D1, unsigned int D2>
void
ImageToImageFilterDefaultCopyRegion(const typename BinaryUnsignedIntDispatch<D1, D2>::FirstLessThanSecondType &,
                                    ImageRegion<D1> &       destRegion,
                                    const ImageRegion<D2> & srcRegion)
{
  // Source dimension is greater than the destination dimension, copy the
  // first part of the source into the destination


  Index<D1>         destIndex;
  Size<D1>          destSize;
  const Index<D2> & srcIndex = srcRegion.GetIndex();
  const Size<D2> &  srcSize = srcRegion.GetSize();

  // copy what we can
  for (unsigned int dim = 0; dim < D1; ++dim)
  {
    destIndex[dim] = srcIndex[dim];
    destSize[dim] = srcSize[dim];
  }

  destRegion = { destIndex, destSize };
}

/**
 * Copy an image region (start index and size) for the case where
 * the source region has a lesser dimension than the destination
 * region.  This copies the source into the first part of the
 * destination. This is an overloaded function.  The other versions
 * of this functions handle the case where the source dimension is
 * less than the destination dimension and the case where the
 * destination and source are the same dimension.
 *
 * Note that the use of source and destination reflect where
 * where is information is coming from and going to.  When used
 * in the pipeline mechanism, the region requested by the output
 * of a filter is used to define the region required on the input.
 * In this case the output of the filter is the source and the
 * input of the filter is the destination.
 */
template <unsigned int D1, unsigned int D2>
void
ImageToImageFilterDefaultCopyRegion(const typename BinaryUnsignedIntDispatch<D1, D2>::FirstGreaterThanSecondType &,
                                    ImageRegion<D1> &       destRegion,
                                    const ImageRegion<D2> & srcRegion)
{
  // Source dimension is less than the destination dimension, copy source
  // into the first part of the destination and set zeros elsewhere.
  Index<D1>         destIndex;
  Size<D1>          destSize;
  const Index<D2> & srcIndex = srcRegion.GetIndex();
  const Size<D2> &  srcSize = srcRegion.GetSize();

  // copy what we can
  {
    unsigned int dim = 0;
    for (; dim < D2; ++dim)
    {
      destIndex[dim] = srcIndex[dim];
      destSize[dim] = srcSize[dim];
    }
    // fill in the rest of the dimensions with zero/one
    for (; dim < D1; ++dim)
    {
      destIndex[dim] = 0;
      destSize[dim] = 1;
    }
  }
  destRegion = { destIndex, destSize };
}

/** \class ImageRegionCopier
 * \brief A Function object used to dispatching to a routine to copy a region
 *        (start index and size).
 *
 * Function object used for dispatching to various routines to copy
 * a region (start index and size).  Most filters use this function
 * object as trivial copy because they require the input image
 * dimension to match the output image dimension.  However, some
 * filters like itk::ExtractImageFilter can support output images of
 * a lower dimension that the input.
 *
 * This function object is used by the default implementation of
 * ImageToImageFilter::GenerateInputRequestedRegion(). It can also
 * be used in routines like ImageSource::ThreadedGenerateData()
 * where a filter may need to map the output region for a
 * particular thread to an input region.
 *
 * This copier uses a "dispatch pattern" to call one of three
 * overloaded functions depending on whether the source and
 * destination dimensions are the same, the source is of a higher
 * dimension than the destination, or the source is of a lower
 * dimension than the destination. The use of an overloaded function
 * is required for proper compilation of the various cases.
 *
 * For the latter two cases, trivial implementations are used.  If
 * the source dimension is a lower dimension than the destination,
 * the output region information is copied into the first portion of
 * the destination region and the rest of the input region is set to
 * zero.  If the source region is a higher dimension than the
 * destination, the first portion of the source region is copied to
 * the destination region.
 *
 * If a filter needs a different behavior is will need to override
 * the CallCopyOutputRegionToInputRegion() method or the
 * CallCopyInputRegionToOutputRegion() method and delegate to the
 * appropriate RegionCopier class.
 * \ingroup ITKCommon
 */
template <unsigned int D1, unsigned int D2>
class ImageRegionCopier
{
public:
  virtual void
  operator()(ImageRegion<D1> & destRegion, const ImageRegion<D2> & srcRegion) const
  {
    using ComparisonType = typename BinaryUnsignedIntDispatch<D1, D2>::ComparisonType;
    ImageToImageFilterDefaultCopyRegion<D1, D2>(ComparisonType(), destRegion, srcRegion);
  }

  virtual ~ImageRegionCopier() = default;
};

/** Stream operator for ImageRegionCopier objects. Just prints the RTTI
    typename. */
template <unsigned int D1, unsigned int D2>
std::ostream &
operator<<(std::ostream & os, const ImageRegionCopier<D1, D2> &)
{
  os << "ImageRegionCopier: " << typeid(ImageRegionCopier<D1, D2>).name() << std::endl;
  return os;
}

/** operator!= for ImageRegionCopier objects. */
template <unsigned int D1, unsigned int D2>
bool
operator!=(const ImageRegionCopier<D1, D2> & c1, const ImageRegionCopier<D1, D2> & c2)
{
  return &c1 != &c2;
}


template <unsigned int D1, unsigned int D2>
void
ImageToImageFilterDefaultCopyInformation(const typename BinaryUnsignedIntDispatch<D1, D2>::FirstEqualsSecondType &,
                                         ImageBase<D1> *       destImage,
                                         const ImageBase<D2> * srcImage)
{
  destImage->CopyInformation(srcImage);
}


template <unsigned int D1, unsigned int D2>
void
ImageToImageFilterDefaultCopyInformation(const typename BinaryUnsignedIntDispatch<D1, D2>::FirstGreaterThanSecondType &,
                                         ImageBase<D1> *       destImage,
                                         const ImageBase<D2> * srcImage)
{
  using DestinationImageType = ImageBase<D1>;
  using SourceImageType = ImageBase<D2>;

  // Copy what we can from the image from spacing and origin of the input
  // This logic needs to be augmented with logic that select which
  // dimensions to copy
  const typename SourceImageType::SpacingType &   inputSpacing = srcImage->GetSpacing();
  const typename SourceImageType::PointType &     inputOrigin = srcImage->GetOrigin();
  const typename SourceImageType::DirectionType & inputDirection = srcImage->GetDirection();

  typename DestinationImageType::SpacingType   destSpacing;
  typename DestinationImageType::PointType     destOrigin;
  typename DestinationImageType::DirectionType destDirection;

  // copy the input to the output and fill the rest of the
  // output with zeros.
  unsigned int i = 0;
  for (; i < SourceImageType::ImageDimension; ++i)
  {
    destSpacing[i] = inputSpacing[i];
    destOrigin[i] = inputOrigin[i];
    for (unsigned int j = 0; j < DestinationImageType::ImageDimension; ++j)
    {
      if (j < SourceImageType::ImageDimension)
      {
        destDirection[j][i] = inputDirection[j][i];
      }
      else
      {
        destDirection[j][i] = 0.0;
      }
    }
  }
  for (; i < DestinationImageType::ImageDimension; ++i)
  {
    destSpacing[i] = 1.0;
    destOrigin[i] = 0.0;
    for (unsigned int j = 0; j < DestinationImageType::ImageDimension; ++j)
    {
      if (j == i)
      {
        destDirection[j][i] = 1.0;
      }
      else
      {
        destDirection[j][i] = 0.0;
      }
    }
  }

  // set the spacing and origin
  destImage->SetSpacing(destSpacing);
  destImage->SetOrigin(destOrigin);
  destImage->SetDirection(destDirection);
  // propagate vector length info
  destImage->SetNumberOfComponentsPerPixel(srcImage->GetNumberOfComponentsPerPixel());
}


/** \class ImageInformationCopier
 * \brief A Function object used to copy image meta-data of an image.
 *
 * This function objects dispatches to Specialized versions. When the
 * images match dimensionality the ImageBase::CopyInformation method
 * is used. But for expanding dimensional of images, the
 * functor copies the common dimensions then fills the extra meta-data
 * with "identity" information.
 *
 * \ingroup ITKCommon
 */
template <unsigned int D1, unsigned int D2>
class ImageInformationCopier
{
public:
  virtual void
  operator()(ImageBase<D1> * destImage, const ImageBase<D2> * srcImage) const
  {
    using ComparisonType = typename BinaryUnsignedIntDispatch<D1, D2>::ComparisonType;
    ImageToImageFilterDefaultCopyInformation<D1, D2>(ComparisonType(), destImage, srcImage);
  }

  virtual ~ImageInformationCopier() = default;
};


} // namespace itk::ImageToImageFilterDetail

#endif
