/*=========================================================================
 *
 *  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.
 *
 *=========================================================================*/

#include <iostream>

#include "itkIdentityTransform.h"
#include "itkTestingMacros.h"

int
itkIdentityTransformTest(int, char *[])
{

  std::cout << "==================================" << std::endl;
  std::cout << "Testing Identity Transform " << std::endl << std::endl;

  constexpr double       epsilon{ 1e-10 };
  constexpr unsigned int N{ 2 };
  bool                   Ok = true;

  using IdentityTransformType = itk::IdentityTransform<double>;
  auto transform = IdentityTransformType::New();

  std::cout << "FixedParameters: " << transform->GetFixedParameters() << std::endl;

  std::cout << "Testing TransformPoint: ";
  IdentityTransformType::InputPointType p(10);

  IdentityTransformType::OutputPointType r = transform->TransformPoint(p);
  for (unsigned int i = 0; i < N; ++i)
  {
    if (itk::Math::Absolute(p[i] - r[i]) > epsilon)
    {
      Ok = false;
      break;
    }
  }
  if (!Ok)
  {
    std::cerr << "Error Transforming Point" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << " [ PASSED ] " << std::endl;


  // Test TransformVector
  std::cout << "Testing TransformVector: ";
  IdentityTransformType::InputVectorType vin;
  vin[0] = 1;
  vin[1] = 2;

  IdentityTransformType::OutputVectorType vout = transform->TransformVector(vin);
  for (unsigned int i = 0; i < N; ++i)
  {
    if (itk::Math::Absolute(vout[i] - vin[i]) > epsilon)
    {
      Ok = false;
      break;
    }
  }
  if (!Ok)
  {
    std::cerr << "Error with TransformVector itk::Vector" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << " [ PASSED ] " << std::endl;


  // Test TransformVector vnl_vector
  std::cout << "Testing TransformVector (vnl): ";
  IdentityTransformType::InputVnlVectorType vnlin;
  vnlin[0] = 1;
  vnlin[1] = 2;

  IdentityTransformType::OutputVnlVectorType vnlout = transform->TransformVector(vnlin);
  for (unsigned int i = 0; i < N; ++i)
  {
    if (itk::Math::Absolute(vnlout[i] - vnlin[i]) > epsilon)
    {
      Ok = false;
      break;
    }
  }
  if (!Ok)
  {
    std::cerr << "Error with TransformVector vnlVector" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << " [ PASSED ] " << std::endl;


  // Test TransformCovariantVector
  std::cout << "Testing TransformCovariantVector: ";
  IdentityTransformType::InputCovariantVectorType vcin;
  vcin[0] = 1;
  vcin[1] = 2;

  IdentityTransformType::OutputCovariantVectorType vcout = transform->TransformCovariantVector(vcin);
  for (unsigned int i = 0; i < N; ++i)
  {
    if (itk::Math::Absolute(vcout[i] - vcin[i]) > epsilon)
    {
      Ok = false;
      break;
    }
  }
  if (!Ok)
  {
    std::cerr << "Error with TransformVector CovariantVector" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << " [ PASSED ] " << std::endl;


  const IdentityTransformType::ParametersType params(0);
  transform->SetParameters(params);
  ITK_TEST_SET_GET_VALUE(params, transform->GetParameters());

  // The number of parameters is 0 for the identity transformation
  ITK_TEST_SET_GET_VALUE(0, transform->GetNumberOfParameters());

  // Testing the Jacobian
  std::cout << "Testing Jacobian: ";
  IdentityTransformType::JacobianType jacobian;
  transform->ComputeJacobianWithRespectToParameters(p, jacobian);

  if (jacobian.rows() != 3 || jacobian.columns() != 0)
  {
    std::cerr << "Error with Jacobian: " << jacobian << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << " [ PASSED ] " << std::endl;


  IdentityTransformType::JacobianPositionType jacobianWrtPos;
  transform->ComputeJacobianWithRespectToPosition(p, jacobianWrtPos);
  IdentityTransformType::JacobianPositionType identity;
  identity.set_identity();
  ITK_TEST_EXPECT_EQUAL(identity, jacobianWrtPos);

  auto inv = IdentityTransformType::New();
  ITK_TEST_EXPECT_TRUE(transform->GetInverse(inv));
  ITK_TEST_EXPECT_TRUE(!transform->GetInverse(nullptr));

  return EXIT_SUCCESS;
}
