/*=========================================================================
 *
 *  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.
 *
 *=========================================================================*/
/*
 * WARNING: DO NOT EDIT THIS FILE!
 * THIS FILE IS AUTOMATICALLY GENERATED BY THE SIMPLEITK BUILD PROCESS.
 * Please look at sitkImageFilterTemplate.cxx.in to make changes.
 */

#include <memory>

#include "itkImage.h"
#include "itkVectorImage.h"
#include "itkLabelMap.h"
#include "itkLabelObject.h"
#include "itkNumericTraits.h"
#include "itkNumericTraitsVariableLengthVectorPixel.h"
#include "itkVectorIndexSelectionCastImageFilter.h"
#include "itkComposeImageFilter.h"
#include "itkPasteImageFilter.h"

#include "sitkPasteImageFilter.h"

#include "sitkToPixelType.hxx"


namespace itk::simple
{

//-----------------------------------------------------------------------------

//
// Default constructor that initializes parameters
//
PasteImageFilter::PasteImageFilter()
{
  this->m_MemberFactory = std::make_unique<detail::MemberFunctionFactory<MemberFunctionType>>(this);

  this->m_MemberFactory->RegisterMemberFunctions<PixelIDTypeList, 2, SITK_MAX_DIMENSION>();

  this->m_MemberFactory2 = std::make_unique<detail::MemberFunctionFactory<MemberFunction2Type>>(this);
  this->m_MemberFactory2->RegisterMemberFunctions<PixelIDTypeList, 2, SITK_MAX_DIMENSION>();
}

//
// Destructor
//
PasteImageFilter::~PasteImageFilter() = default;


//
// ToString
//
std::string
PasteImageFilter::ToString() const
{
  std::ostringstream out;
  out << "itk::simple::PasteImageFilter\n";
  out << "  SourceSize: ";
  this->ToStringHelper(out, this->m_SourceSize);
  out << std::endl;
  out << "  SourceIndex: ";
  this->ToStringHelper(out, this->m_SourceIndex);
  out << std::endl;
  out << "  DestinationIndex: ";
  this->ToStringHelper(out, this->m_DestinationIndex);
  out << std::endl;
  out << "  DestinationSkipAxes: ";
  this->ToStringHelper(out, this->m_DestinationSkipAxes);
  out << std::endl;

  out << ProcessObject::ToString();
  return out.str();
}

//
// Execute
//
Image
PasteImageFilter::Execute(const Image & destinationImage, const Image & sourceImage)
{
  const PixelIDValueEnum type = destinationImage.GetPixelID();
  const unsigned int     dimension = destinationImage.GetDimension();
  CheckImageMatchingPixelType(destinationImage, sourceImage, "sourceImage");

  return this->m_MemberFactory->GetMemberFunction(type, dimension)(&destinationImage, &sourceImage);
}
Image
PasteImageFilter::Execute(const Image & destinationImage, double constant)
{
  const PixelIDValueEnum type = destinationImage.GetPixelID();
  const unsigned int     dimension = destinationImage.GetDimension();

  return this->m_MemberFactory2->GetMemberFunction(type, dimension)(&destinationImage, constant);
}
Image
PasteImageFilter::Execute(Image && destinationImage, const Image & sourceImage)
{
  Image & temp = destinationImage;
  auto    autoResetInPlace = make_scope_exit([this, &temp] {
    this->m_InPlace = false;
    Image moved(std::move(temp));
  });
  if (temp.IsUnique())
  {
    m_InPlace = true;
  }
  return this->Execute(destinationImage, sourceImage);
}
Image
PasteImageFilter::Execute(Image && destinationImage, double constant)
{
  Image & temp = destinationImage;
  auto    autoResetInPlace = make_scope_exit([this, &temp] {
    this->m_InPlace = false;
    Image moved(std::move(temp));
  });
  if (temp.IsUnique())
  {
    m_InPlace = true;
  }
  return this->Execute(destinationImage, constant);
}

//-----------------------------------------------------------------------------

//
// Custom Casts
//
namespace
{}

//-----------------------------------------------------------------------------

sitkClangDiagnosticPush();
sitkClangWarningIgnore("-Wunused-local-typedef");

//
// ExecuteInternal
//
template <class TImageType>
Image
PasteImageFilter::ExecuteInternal(const Image * inDestinationImage, const Image * inSourceImage)
{
  assert(inDestinationImage != nullptr);
  assert(inSourceImage != nullptr);


  // Define the input and output image types
  using InputImageType = TImageType;


  // Get the pointer to the ITK image contained in image1
  typename InputImageType::ConstPointer destinationImage = this->CastImageToITK<InputImageType>(*inDestinationImage);

  return this->ExecuteInternal<TImageType>(
    destinationImage, inSourceImage, std::integral_constant<unsigned int, TImageType::ImageDimension>());
}

template <class TImageType, unsigned int SourceDimension>
Image
PasteImageFilter::ExecuteInternal(const TImageType * destinationImage,
                                  const Image *      inSourceImage,
                                  std::integral_constant<unsigned int, SourceDimension>)
{
  if (inSourceImage->GetDimension() != SourceDimension)
  {
    return this->ExecuteInternal(
      destinationImage, inSourceImage, std::integral_constant<unsigned int, SourceDimension - 1>());
  }

  // Define the input and output image types
  using InputImageType = TImageType;
  using SourceImageType =
    typename InputImageType::template RebindImageType<typename InputImageType::PixelType, SourceDimension>;

  using SourceIndexType = typename SourceImageType::IndexType;
  using SourceSizeType = typename SourceImageType::SizeType;

  using OutputImageType = InputImageType;

  using FilterType = itk::PasteImageFilter<InputImageType, SourceImageType, OutputImageType>;
  // Set up the ITK filter
  typename FilterType::Pointer filter = FilterType::New();


  assert(destinationImage != nullptr);
  filter->SetInput(destinationImage);
  assert(inSourceImage != nullptr);
  filter->SetSourceImage(this->CastImageToITK<typename FilterType::SourceImageType>(*inSourceImage));

  typename SourceImageType::RegionType itkRegion(sitkSTLVectorToITK<SourceIndexType>(m_SourceIndex),
                                                 sitkSTLVectorToITK<SourceSizeType>(m_SourceSize));

  filter->SetSourceRegion(itkRegion);
  typename InputImageType::IndexType itkVecDestinationIndex =
    sitkSTLVectorToITK<typename InputImageType::IndexType>(this->GetDestinationIndex());
  filter->SetDestinationIndex(itkVecDestinationIndex);
  if (!m_DestinationSkipAxes.empty())
  {
    filter->SetDestinationSkipAxes(
      sitkSTLVectorToITK<typename FilterType::InputSkipAxesArrayType>(m_DestinationSkipAxes));
  }
  filter->SetInPlace(m_InPlace);


  this->PreUpdate(filter.GetPointer());


  // Run the ITK filter and return the output as a SimpleITK image
  filter->Update();

  typename FilterType::OutputImageType::Pointer itkOutImage{ filter->GetOutput() };
  filter = nullptr;
  this->FixNonZeroIndex(itkOutImage.GetPointer());
  return Image{ this->CastITKToImage(itkOutImage.GetPointer()) };
}

template <class TImageType>
Image
PasteImageFilter::ExecuteInternal(const TImageType *,
                                  const Image * sourceImage,
                                  std::integral_constant<unsigned int, 1>)
{
  sitkExceptionMacro("Unable to use the sourceImage of dimension"
                     << sourceImage->GetDimension() << " with destination dimension of " << TImageType::ImageDimension
                     << ".");
}

template <class TImageType>
Image
PasteImageFilter::ExecuteInternal(const Image * inDestinationImage, double constant)
{
  // Define the input and output image types
  using InputImageType = TImageType;
  using SourceImageType = InputImageType;

  using SourceIndexType = typename SourceImageType::IndexType;
  using SourceSizeType = typename SourceImageType::SizeType;

  using OutputImageType = InputImageType;


  using FilterType = itk::PasteImageFilter<InputImageType, SourceImageType, OutputImageType>;
  // Set up the ITK filter
  typename FilterType::Pointer filter = FilterType::New();


  // Get the pointer to the ITK image contained in image1
  typename InputImageType::ConstPointer destinationImage = this->CastImageToITK<InputImageType>(*inDestinationImage);
  assert(destinationImage != nullptr);
  filter->SetInput(destinationImage);

  typename SourceImageType::PixelType c;
  NumericTraits<typename SourceImageType::PixelType>::SetLength(c, destinationImage->GetNumberOfComponentsPerPixel());
  ToPixelType(constant, c);
  filter->SetConstant(c);

  typename SourceImageType::RegionType itkRegion(sitkSTLVectorToITK<SourceIndexType>(m_SourceIndex),
                                                 sitkSTLVectorToITK<SourceSizeType>(m_SourceSize));

  filter->SetSourceRegion(itkRegion);
  typename InputImageType::IndexType itkVecDestinationIndex =
    sitkSTLVectorToITK<typename InputImageType::IndexType>(this->GetDestinationIndex());
  filter->SetDestinationIndex(itkVecDestinationIndex);
  if (!m_DestinationSkipAxes.empty())
  {
    filter->SetDestinationSkipAxes(
      sitkSTLVectorToITK<typename FilterType::InputSkipAxesArrayType>(m_DestinationSkipAxes));
  }
  filter->SetInPlace(m_InPlace);


  this->PreUpdate(filter.GetPointer());


  // Run the ITK filter and return the output as a SimpleITK image
  filter->Update();

  typename FilterType::OutputImageType::Pointer itkOutImage{ filter->GetOutput() };
  filter = nullptr;
  this->FixNonZeroIndex(itkOutImage.GetPointer());
  return Image{ this->CastITKToImage(itkOutImage.GetPointer()) };
}

sitkClangDiagnosticPop();

//-----------------------------------------------------------------------------


//
// Function to run the Execute method of this filter
//
Image
Paste(const Image &             destinationImage,
      const Image &             sourceImage,
      std::vector<unsigned int> sourceSize,
      std::vector<int>          sourceIndex,
      std::vector<int>          destinationIndex,
      std::vector<bool>         destinationSkipAxes)
{
  PasteImageFilter filter;
  filter.SetSourceSize(sourceSize);
  filter.SetSourceIndex(sourceIndex);
  filter.SetDestinationIndex(destinationIndex);
  filter.SetDestinationSkipAxes(std::move(destinationSkipAxes));
  return filter.Execute(destinationImage, sourceImage);
}
//
// Function to run the Execute method of this filter
//
Image
Paste(Image &&                  destinationImage,
      const Image &             sourceImage,
      std::vector<unsigned int> sourceSize,
      std::vector<int>          sourceIndex,
      std::vector<int>          destinationIndex,
      std::vector<bool>         destinationSkipAxes)
{
  PasteImageFilter filter;
  filter.SetSourceSize(std::move(sourceSize));
  filter.SetSourceIndex(std::move(sourceIndex));
  filter.SetDestinationIndex(std::move(destinationIndex));
  filter.SetDestinationSkipAxes(std::move(destinationSkipAxes));
  return filter.Execute(std::move(destinationImage), sourceImage);
}

} // namespace itk::simple
