/*=========================================================================
 *
 *  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 itkParametricSpaceToImageSpaceMeshFilter_hxx
#define itkParametricSpaceToImageSpaceMeshFilter_hxx

#include "itkMacro.h"
#include "itkProgressReporter.h"

namespace itk
{

template <typename TInputMesh, typename TOutputMesh>
ParametricSpaceToImageSpaceMeshFilter<TInputMesh, TOutputMesh>::ParametricSpaceToImageSpaceMeshFilter()
{
  this->SetNumberOfRequiredInputs(1);
}

template <typename TInputMesh, typename TOutputMesh>
void
ParametricSpaceToImageSpaceMeshFilter<TInputMesh, TOutputMesh>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
}

template <typename TInputMesh, typename TOutputMesh>
void
ParametricSpaceToImageSpaceMeshFilter<TInputMesh, TOutputMesh>::GenerateData()
{
  using InputPointsContainer = typename TInputMesh::PointsContainer;
  using OutputPointsContainer = typename TOutputMesh::PointsContainer;

  using OutputPointsContainerPointer = typename TOutputMesh::PointsContainerPointer;

  using InputPointDataContainer = typename TInputMesh::PointDataContainer;
  using OutputPointDataContainer = typename TOutputMesh::PointDataContainer;

  using OutputPointDataContainerPointer = typename TOutputMesh::PointDataContainerPointer;

  const InputMeshType * inputMesh = this->GetInput();
  OutputMeshPointer     outputMesh = this->GetOutput();

  if (!inputMesh)
  {
    itkExceptionMacro("Missing Input Mesh");
  }

  if (!outputMesh)
  {
    itkExceptionMacro("Missing Output Mesh");
  }

  outputMesh->SetBufferedRegion(outputMesh->GetRequestedRegion());

  const InputPointsContainer * inPoints = inputMesh->GetPoints();
  OutputPointsContainerPointer outPoints = OutputPointsContainer::New();

  outPoints->Reserve(inputMesh->GetNumberOfPoints());

  const InputPointDataContainer * inData = inputMesh->GetPointData();
  OutputPointDataContainerPointer outData = OutputPointDataContainer::New();

  outData->Reserve(inputMesh->GetNumberOfPoints());

  outputMesh->SetPoints(outPoints);
  outputMesh->SetPointData(outData);

  if (!inData)
  {
    return;
  }

  if (!inPoints)
  {
    return;
  }

  typename InputPointsContainer::ConstIterator    inputPointIt = inPoints->Begin();
  typename InputPointsContainer::ConstIterator    inputPointEnd = inPoints->End();
  typename InputPointDataContainer::ConstIterator inputDataIt = inData->Begin();

  typename OutputPointsContainer::Iterator    outputPointIt = outPoints->Begin();
  typename OutputPointDataContainer::Iterator outputDataIt = outData->Begin();

  // support progress methods/callbacks
  ProgressReporter progress(this, 0, inPoints->Size());

  const unsigned int OutputDimension = TOutputMesh::PointDimension;

  typename TOutputMesh::PointType point;

  while (inputPointIt != inputPointEnd)
  {
    for (unsigned int i = 0; i < OutputDimension; ++i)
    {
      // Convert Index coordinates to MeshSpace
      point[i] = inputDataIt.Value()[i];
    }

    outputPointIt.Value() = point;
    outputDataIt.Value() = inputPointIt.Value();

    ++inputDataIt;
    ++inputPointIt;
    ++outputPointIt;
    ++outputDataIt;
    progress.CompletedPixel();
  }
}

template <typename TInputMesh, typename TOutputMesh>
void
ParametricSpaceToImageSpaceMeshFilter<TInputMesh, TOutputMesh>::GenerateOutputInformation()
{
  // No additional information needs to be copied
}
} // end namespace itk

#endif
