/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkImageRegistrationMethodImageSource_h
#define itkImageRegistrationMethodImageSource_h
#include "itkImage.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkCommandIterationUpdate.h"
#include "itkOptimizerParameters.h"

/**
 *  This class generates two 2D-Gaussians (standard deviation RegionSize/2)
 *  One is shifted by 7,3 pixels from the other.
 *
 *  These two images are provided to the registration methods in this
 *  directory in order to obtain a uniform test input data.
 *
 *  Therefore the solution of the registration is |-7 -3|
 */
namespace itk
{

namespace testhelper
{

template< typename TFixedPixelType,
          typename TMovingPixelType,
          unsigned int NDimension     >
class ImageRegistrationMethodImageSource : public itk::Object
{
public:

  using Self = ImageRegistrationMethodImageSource;
  using Superclass = Object;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;
  using ParametersType = OptimizerParameters<double>;

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

  /** Run-time type information (and related methods). */
  itkTypeMacro(Image, Object);


  using MovingImageType = itk::Image<TMovingPixelType,NDimension>;
  using FixedImageType = itk::Image<TFixedPixelType,NDimension >;

const MovingImageType * GetMovingImage() const
  {
  return m_MovingImage.GetPointer();
  }

const FixedImageType * GetFixedImage() const
  {
  return m_FixedImage.GetPointer();
  }

const ParametersType & GetActualParameters() const
{
  return m_Parameters;
}


void GenerateImages( const typename MovingImageType::SizeType & size )
{
  typename MovingImageType::IndexType index;
  index.Fill(0);
  typename MovingImageType::RegionType region;
  region.SetSize( size );
  region.SetIndex( index );

  m_MovingImage->SetLargestPossibleRegion( region );
  m_MovingImage->SetBufferedRegion( region );
  m_MovingImage->SetRequestedRegion( region );
  m_MovingImage->Allocate();

  m_FixedImage->SetLargestPossibleRegion( region );
  m_FixedImage->SetBufferedRegion( region );
  m_FixedImage->SetRequestedRegion( region );
  m_FixedImage->Allocate();

  /* Fill images with a 2D gaussian*/
  using MovingImageIteratorType = itk::ImageRegionIteratorWithIndex<MovingImageType>;

  using FixedImageIteratorType = itk::ImageRegionIteratorWithIndex<FixedImageType>;


  itk::Point<double,2> center;
  center[0] = (double)region.GetSize()[0]/2.0;
  center[1] = (double)region.GetSize()[1]/2.0;

  const double s = (double)region.GetSize()[0]/2.0;

  itk::Point<double,2>  p;
  itk::Vector<double,2> d;

  /* Set the displacement */
  itk::Vector<double,2> displacement;
  displacement[0] = m_Parameters[0];
  displacement[1] = m_Parameters[1];


  MovingImageIteratorType ri(m_MovingImage,region);
  FixedImageIteratorType ti(m_FixedImage,region);
  while(!ri.IsAtEnd())
  {
    p[0] = ri.GetIndex()[0];
    p[1] = ri.GetIndex()[1];
    d = p-center;
    d += displacement;
    const double x = d[0];
    const double y = d[1];
    const double value = 200.0 * std::exp( - ( x*x + y*y )/(s*s) );
    ri.Set( static_cast<typename MovingImageType::PixelType>(value) );
    ++ri;
  }


  while(!ti.IsAtEnd())
  {
    p[0] = ti.GetIndex()[0];
    p[1] = ti.GetIndex()[1];
    d = p-center;
    const double x = d[0];
    const double y = d[1];
    const double value = 200.0 * std::exp( - ( x*x + y*y )/(s*s) );
    ti.Set( static_cast<typename FixedImageType::PixelType>(value) );
    ++ti;
  }


}

protected:

ImageRegistrationMethodImageSource()
{
  m_MovingImage = MovingImageType::New();
  m_FixedImage  = FixedImageType::New();
  m_Parameters  = ParametersType(2);
  m_Parameters[0] = 7.0;
  m_Parameters[1] = 3.0;
}

private:

  typename FixedImageType::Pointer     m_FixedImage;
  typename MovingImageType::Pointer    m_MovingImage;

  ParametersType                       m_Parameters;

};

}  // end namespace testhelper

}  // end namespace itk
#endif
