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

#include "itkParametricPath.h"

namespace itk
{
/**
 * Constructor
 */
template< unsigned int VDimension >
ParametricPath< VDimension >
::ParametricPath()
{
  m_DefaultInputStepSize = 0.3;
}

template< unsigned int VDimension >
typename ParametricPath< VDimension >::IndexType
ParametricPath< VDimension >
::EvaluateToIndex(const InputType & input) const
{
  ContinuousIndexType continuousIndex;
  IndexType           index;

  continuousIndex = this->Evaluate(input);

  // Round each coordinate to the nearest integer value
  for ( unsigned int i = 0; i < VDimension; i++ )
    {
    index[i] = static_cast< IndexValueType >( continuousIndex[i] + 0.5 );
    }

  return index;
}

template< unsigned int VDimension >
typename ParametricPath< VDimension >::OffsetType
ParametricPath< VDimension >
::IncrementInput(InputType & input) const
{
  int        iterationCount;
  bool       tooSmall;
  bool       tooBig;
  InputType  inputStepSize;
  InputType  finalInputValue;
  OffsetType offset;
  IndexType  currentImageIndex;
  IndexType  nextImageIndex;
  IndexType  finalImageIndex;

  iterationCount    = 0;
  inputStepSize     = m_DefaultInputStepSize;

  // Are we already at (or past) the end of the input?
  finalInputValue   = this->EndOfInput();
  currentImageIndex = this->EvaluateToIndex(input);
  finalImageIndex   = this->EvaluateToIndex(finalInputValue);
  offset            = finalImageIndex - currentImageIndex;
  if ( ( offset == this->GetZeroOffset() && input != this->StartOfInput() )
       || ( input >= finalInputValue ) )
    {
    return this->GetZeroOffset();
    }

  do
    {
    if ( iterationCount++ > 10000 ) { itkExceptionMacro(<< "Too many iterations"); }

    nextImageIndex    = this->EvaluateToIndex(input + inputStepSize);
    offset            = nextImageIndex - currentImageIndex;

    tooBig = false;
    tooSmall = ( offset == this->GetZeroOffset() );
    if ( tooSmall )
      {
      // double the input step size, but don't go past the end of the input
      inputStepSize *= 2;
      if ( ( input + inputStepSize ) >= finalInputValue )
        {
        inputStepSize = finalInputValue - input;
        }
      }
    else
      {
      // Search for an offset dimension that is too big
      for ( unsigned int i = 0; i < VDimension && !tooBig; i++ )
        {
        tooBig = ( offset[i] >= 2 || offset[i] <= -2 );
        }

      if ( tooBig )
        {
        inputStepSize /= 1.5;
        }
      }
    }
  while ( tooSmall || tooBig );

  input += inputStepSize;
  return offset;
}

template< unsigned int VDimension >
typename ParametricPath< VDimension >::VectorType
ParametricPath< VDimension >
::EvaluateDerivative(const InputType & input) const
{
  InputType inputStepSize;

  inputStepSize = m_DefaultInputStepSize;
  if ( ( input + inputStepSize ) >= this->EndOfInput() )
    {
    inputStepSize = this->EndOfInput() - input;
    }

  return ( this->Evaluate(input + inputStepSize) - this->Evaluate(input) ) / inputStepSize;
}

template< unsigned int VDimension >
void
ParametricPath< VDimension >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "DefaultInputSize: " << m_DefaultInputStepSize << std::endl;
}
} // end namespaceitk

#endif
