/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  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 __ImageRandomCoordinateSampler_h
#define __ImageRandomCoordinateSampler_h

#include "itkImageRandomSamplerBase.h"
#include "itkInterpolateImageFunction.h"
#include "itkBSplineInterpolateImageFunction.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"

namespace itk
{

/** \class ImageRandomCoordinateSampler
 *
 * \brief Samples an image by randomly composing a set of physical coordinates
 *
 * This image sampler generates not only samples that correspond with
 * pixel locations, but selects points in physical space.
 *
 * \ingroup ImageSamplers
 */

template< class TInputImage >
class ImageRandomCoordinateSampler :
  public ImageRandomSamplerBase< TInputImage >
{
public:

  /** Standard ITK-stuff. */
  typedef ImageRandomCoordinateSampler          Self;
  typedef ImageRandomSamplerBase< TInputImage > Superclass;
  typedef SmartPointer< Self >                  Pointer;
  typedef SmartPointer< const Self >            ConstPointer;

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

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

  /** Typedefs inherited from the superclass. */
  typedef typename Superclass::DataObjectPointer            DataObjectPointer;
  typedef typename Superclass::OutputVectorContainerType    OutputVectorContainerType;
  typedef typename Superclass::OutputVectorContainerPointer OutputVectorContainerPointer;
  typedef typename Superclass::InputImageType               InputImageType;
  typedef typename Superclass::InputImagePointer            InputImagePointer;
  typedef typename Superclass::InputImageConstPointer       InputImageConstPointer;
  typedef typename Superclass::InputImageRegionType         InputImageRegionType;
  typedef typename Superclass::InputImagePixelType          InputImagePixelType;
  typedef typename Superclass::ImageSampleType              ImageSampleType;
  typedef typename Superclass::ImageSampleContainerType     ImageSampleContainerType;
  typedef typename Superclass::ImageSampleContainerPointer  ImageSampleContainerPointer;
  typedef typename Superclass::MaskType                     MaskType;
  typedef typename Superclass::InputImageSizeType           InputImageSizeType;
  typedef typename InputImageType::SpacingType              InputImageSpacingType;
  typedef typename Superclass::InputImageIndexType          InputImageIndexType;
  typedef typename Superclass::InputImagePointType          InputImagePointType;
  typedef typename Superclass::InputImagePointValueType     InputImagePointValueType;
  typedef typename Superclass::ImageSampleValueType         ImageSampleValueType;

  /** The input image dimension. */
  itkStaticConstMacro( InputImageDimension, unsigned int,
    Superclass::InputImageDimension );

  /** This image sampler samples the image on physical coordinates and thus
   * needs an interpolator. */
  typedef double CoordRepType;
  typedef InterpolateImageFunction<
    InputImageType, CoordRepType >                            InterpolatorType;
  typedef typename InterpolatorType::Pointer InterpolatorPointer;
  typedef BSplineInterpolateImageFunction<
    InputImageType, CoordRepType, double >                    DefaultInterpolatorType;

  /** The random number generator used to generate random coordinates. */
  typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType;
  typedef typename RandomGeneratorType::Pointer                  RandomGeneratorPointer;

  /** Set/Get the interpolator. A 3rd order B-spline interpolator is used by default. */
  itkSetObjectMacro( Interpolator, InterpolatorType );
  itkGetModifiableObjectMacro( Interpolator, InterpolatorType );

  /** Set/Get the sample region size (in mm). Only needed when UseRandomSampleRegion==true;
   * default: filled with ones.  */
  itkSetMacro( SampleRegionSize, InputImageSpacingType );
  itkGetConstReferenceMacro( SampleRegionSize, InputImageSpacingType );

  /** Set/Get whether to use randomly selected sample regions, or just the whole image
   * Default: false. */
  itkGetConstMacro( UseRandomSampleRegion, bool );
  itkSetMacro( UseRandomSampleRegion, bool );

protected:

  typedef typename InterpolatorType::ContinuousIndexType InputImageContinuousIndexType;

  /** The constructor. */
  ImageRandomCoordinateSampler();
  /** The destructor. */
  ~ImageRandomCoordinateSampler() override {}

  /** PrintSelf. */
  void PrintSelf( std::ostream & os, Indent indent ) const override;

  /** Function that does the work. */
  void GenerateData( void ) override;

  /** Multi-threaded functionality that does the work. */
  void BeforeThreadedGenerateData( void ) override;

  void ThreadedGenerateData(
    const InputImageRegionType & inputRegionForThread,
    ThreadIdType threadId ) override;

  /** Generate a point randomly in a bounding box. */
  virtual void GenerateRandomCoordinate(
    const InputImageContinuousIndexType & smallestContIndex,
    const InputImageContinuousIndexType & largestContIndex,
    InputImageContinuousIndexType &       randomContIndex );

  InterpolatorPointer    m_Interpolator;
  RandomGeneratorPointer m_RandomGenerator;
  InputImageSpacingType  m_SampleRegionSize;

  /** Generate the two corners of a sampling region, given the two corners
  * of an image. If UseRandomSampleRegion=false, the smallesPoint and largestPoint
  * are just copies of the smallestImagePoint and largestImagePoint
  * Otherwise, the midpoint of the sample region is randomly selected and
  * the two corners are computed using the SampleRegionSize */
  virtual void GenerateSampleRegion(
    const InputImageContinuousIndexType & smallestImageContIndex,
    const InputImageContinuousIndexType & largestImageContIndex,
    InputImageContinuousIndexType & smallestContIndex,
    InputImageContinuousIndexType & largestContIndex );

private:

  /** The private constructor. */
  ImageRandomCoordinateSampler( const Self & );   // purposely not implemented
  /** The private copy constructor. */
  void operator=( const Self & );                 // purposely not implemented

  bool m_UseRandomSampleRegion;

};

} // end namespace itk

#ifndef ITK_MANUAL_INSTANTIATION
#include "itkImageRandomCoordinateSampler.hxx"
#endif

#endif // end #ifndef __ImageRandomCoordinateSampler_h
