/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    $RCSfile: itkMirrorPadImageFilter.h,v $
  Language:  C++
  Date:      $Date: 2006/03/22 16:26:42 $
  Version:   $Revision: 1.15 $

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef __itkMirrorPadImageFilter_h
#define __itkMirrorPadImageFilter_h

#include "itkPadImageFilter.h"
#include <vector>

namespace itk
{

/** \class MirrorPadImageFilter
 * \brief Increase the image size by padding with replicants of the 
 * input image value.
 *
 * MirrorPadImageFilter changes the image bounds of an image. Any
 * added pixels are filled in with a mirrored replica of the input
 * image.  For instance, if the output image needs a pixel that is <b>two
 * pixels to the left of the LargestPossibleRegion</b> of the input image,
 * the value assigned will be from the pixel <b>two pixels inside the
 * left boundary of the LargestPossibleRegion</b>.  The image bounds of
 * the output must be specified.
 *
 * This filter is implemented as a multithreaded filter.  It provides a 
 * ThreadedGenerateData() method for its implementation.
 *
 * \ingroup GeometricTransforms 
 * \sa WrapPadImageFilter, ConstantPadImageFilter
 */
template <class TInputImage, class TOutputImage>
class ITK_EXPORT MirrorPadImageFilter:
    public PadImageFilter<TInputImage,TOutputImage>
{
public:
  /** Standard class typedefs. */
  typedef MirrorPadImageFilter         Self;
  typedef PadImageFilter<TInputImage,TOutputImage>  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(MirrorPadImageFilter, PadImageFilter);
  
  typedef TInputImage InputImageType;
  typedef TOutputImage OutputImageType;
  
  /** Typedef to describe the output image region type. */
  typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
  typedef typename Superclass::InputImageRegionType InputImageRegionType;
  
  /** Typedef to describe the type of pixel. */
  typedef typename Superclass::OutputImagePixelType OutputImagePixelType;
  typedef typename Superclass::InputImagePixelType InputImagePixelType;
  
  /** Typedef to describe the output and input image index and size types. */
  typedef typename Superclass::OutputImageIndexType OutputImageIndexType;
  typedef typename Superclass::InputImageIndexType InputImageIndexType;
  typedef typename Superclass::OutputImageSizeType OutputImageSizeType;
  typedef typename Superclass::InputImageSizeType InputImageSizeType;
  
  /** ImageDimension enumeration. */
  itkStaticConstMacro(ImageDimension, unsigned int,
                      TInputImage::ImageDimension);

#ifdef ITK_USE_CONCEPT_CHECKING
  /** Begin concept checking */
  itkConceptMacro(InputConvertibleToOutputCheck,
    (Concept::Convertible<InputImagePixelType, OutputImagePixelType>));
  /** End concept checking */
#endif

protected:
  MirrorPadImageFilter() {};
  ~MirrorPadImageFilter() {};
  
  /** Convert from the output index to the input index taking 
   * into consideration mirrored and normal regions. */
  void ConvertOutputIndexToInputIndex(OutputImageIndexType & outputIndex, 
                                      InputImageIndexType & inputIndex, 
                                      OutputImageRegionType & outputRegion,
                                      InputImageRegionType & inputRegion,
                                      int* oddRegionArray);
  
  /** Decide whether test falls within an odd or even number
   * of size regions from base. */
  int RegionIsOdd(long base, long test, long size);
  
  /** MirrorPadImageFilter can be implemented as a multithreaded filter.
   * Therefore, this implementation provides a ThreadedGenerateData()
   * routine which is called for each processing thread. The output
   * image data is allocated automatically by the superclass prior to
   * calling ThreadedGenerateData().  ThreadedGenerateData can only
   * write to the portion of the output image specified by the
   * parameter "outputRegionForThread"
   *
   * \sa ImageToImageFilter::ThreadedGenerateData(),
   *     ImageToImageFilter::GenerateData()  */
  void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
                            int threadId );
  /** Given an n dimensional list of input region breakpoints in indices
   * and size (where the current region and maximum region for each dimension
   * is encoded in regIndices and regLimit), choose the next input region. */ 
  int GenerateNextInputRegion(long *regIndices, long *regLimit, 
                              std::vector<long>* indices, 
                              std::vector<long>* sizes, 
                              InputImageRegionType& outputRegion);

  /** Given an n dimensional list of output region breakpoints in indices
   * and size (where the current region and maximum region for each dimension
   * is encoded in regIndices and regLimit), choose the next output region. */ 
  int GenerateNextOutputRegion(long *regIndices, long *regLimit, 
                               std::vector<long>* indices, 
                               std::vector<long>* sizes, 
                               OutputImageRegionType& outputRegion);
  /** Given the start and end indices of a region, determine how many
   * instances of size fit within the region.  The variable offset provides
   * a way to adjust width of the area while forcing alignment to the
   * start or end location. */
  int FindRegionsInArea(long start, long end, long size, long offset);

  /** Generate region 0 (inter-region) information.  Based on the indices
   * of the input and the output for this dimension, decide what are the 
   * starting points and the lengths of the output region directly 
   * corresponding to the input region.  Padding will be on either 
   * side of this region.  The algorithmic complications are necessary
   * to support the streaming interface and multithreading. */
  int BuildInterRegions(std::vector<long>& inputRegionStart, 
                        std::vector<long>& outputRegionStart,
                        std::vector<long>& inputRegionSizes, 
                        std::vector<long>& outputRegionSizes,
                        long inputIndex, long outputIndex,
                        long inputSize, long outputSize, int numRegs, 
                        int & regCtr);

  /** Generate region 1 (pre-region) information.  Based on the indices
   * of the input and the output for this dimension, decide what are the 
   * starting points and the lengths of the output region directly 
   * preceding the input region in this dimension.  This may require
   * more than one region be defined if the padding is larger than the
   * size of the input image in this dimension.  Other algorithmic 
   * complications are necessary to support the streaming interface 
   * and multithreading. */
  int BuildPreRegions(std::vector<long>& inputRegionStart, 
                      std::vector<long>& outputRegionStart,
                      std::vector<long>& inputRegionSizes, 
                      std::vector<long>& outputRegionSizes,
                      long inputIndex, long outputIndex,
                      long inputSize, long outputSize, int numRegs, 
                      int & regCtr);

  /** Generate region 2 (post-region) information.  Based on the indices
   * of the input and the output for this dimension, decide what are the 
   * starting points and the lengths of the output region directly 
   * succeeding the input region in this dimension.  This may require
   * more than one region be defined if the padding is larger than the
   * size of the input image in this dimension.  Other algorithmic 
   * complications are necessary to support the streaming interface 
   * and multithreading. */
  int BuildPostRegions(std::vector<long>& inputRegionStart, 
                       std::vector<long>& outputRegionStart,
                       std::vector<long>& inputRegionSizes, 
                       std::vector<long>& outputRegionSizes,
                       long inputIndex, long outputIndex,
                       long inputSize, long outputSize, 
                       int numRegs, int & regCtr);

  /** MirrorPadImageFilter needs a different input requested region than
   * output requested region.  As such, MirrorPadImageFilter needs to
   * provide an implementation for GenerateInputRequestedRegion() in
   * order to inform the pipeline execution model.
   *
   * \sa ProcessObject::GenerateInputRequestedRegion() 
   * \sa PadImageFilter::GenerateInputRequestedRegion() */
  virtual void GenerateInputRequestedRegion();

private:
  MirrorPadImageFilter(const Self&); //purposely not implemented
  void operator=(const Self&); //purposely not implemented
  
};
  

} // end namespace itk

#ifndef ITK_MANUAL_INSTANTIATION
#include "itkMirrorPadImageFilter.txx"
#endif

#endif
