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

#include "itkFrustumSpatialFunction.h"

namespace itk
{
template< unsigned int VImageDimension, typename TInput >
FrustumSpatialFunction< VImageDimension, TInput >::FrustumSpatialFunction()
{
  for ( unsigned int i = 0; i < m_Apex.GetPointDimension(); i++ )
    {
    m_Apex[i] = 0.0f;
    }
  m_AngleZ = 0.0f;
  m_ApertureAngleX = 0.0f;
  m_ApertureAngleY = 0.0f;
  m_TopPlane = 0.0f;
  m_BottomPlane = 0.0f;
  m_RotationPlane = RotateInXZPlane;
}

template< unsigned int VImageDimension, typename TInput >
FrustumSpatialFunction< VImageDimension, TInput >::~FrustumSpatialFunction()
{}

template< unsigned int VImageDimension, typename TInput >
typename FrustumSpatialFunction< VImageDimension, TInput >::OutputType
FrustumSpatialFunction< VImageDimension, TInput >
::Evaluate(const InputType & position) const
{
  typedef InputType                      PointType;
  typedef typename PointType::VectorType VectorType;

  VectorType   relativePosition = position - m_Apex;
  const double distanceToApex = relativePosition.GetNorm();

  // Check Top and Bottom planes.. If the angle is negative, the
  // top plane may be less than the bottom plane, but is ok.
  if ( m_TopPlane <= m_BottomPlane )
    {
    if ( distanceToApex < m_TopPlane
         || distanceToApex > m_BottomPlane )
      {
      return 0;
      }
    }
  else
    {
    if ( distanceToApex > m_TopPlane
         || distanceToApex < m_BottomPlane )
      {
      return 0;
      }
    }

  if ( m_RotationPlane == RotateInXZPlane )
    {
    const double dx = relativePosition[0];
    const double dy = relativePosition[1];
    const double dz = relativePosition[2];

    const double distanceXZ = std::sqrt(dx * dx + dz * dz);

    const double deg2rad = std::atan(1.0f) / 45.0;

    //  Check planes along Y
    const double angleY = std::atan2(dy, distanceXZ);
    if ( std::fabs(angleY) > m_ApertureAngleY * deg2rad )
      {
      return 0;
      }

    //  Check planes along X
    const double angleX = std::atan2(dx, dz);

    if ( std::cos(angleX  + ( 180.0 + m_AngleZ ) * deg2rad)  <
         std::cos(deg2rad * m_ApertureAngleX) )
      {
      return 0;
      }

    return 1;
    }
  else if ( m_RotationPlane == RotateInYZPlane )
    {
    const double dx = relativePosition[0];
    const double dy = relativePosition[1];
    const double dz = relativePosition[2];

    const double distanceYZ = std::sqrt(dy * dy + dz * dz);

    const double deg2rad = std::atan(1.0f) / 45.0;

    //  Check planes along X
    const double angleX = std::atan2(dx, distanceYZ);
    if ( std::fabs(angleX) > m_ApertureAngleX * deg2rad )
      {
      return 0;
      }

    //  Check planes along Y
    const double angleY = std::atan2(dy, dz);

    if ( std::cos(angleY  + ( 180.0 + m_AngleZ ) * deg2rad)  <
         std::cos(deg2rad * m_ApertureAngleY) )
      {
      return 0;
      }

    return 1;
    }
  else
    {
    itkExceptionMacro(<< "Rotation plane not set!");
    }
  return 0;
}

template< unsigned int VImageDimension, typename TInput >
void
FrustumSpatialFunction< VImageDimension, TInput >::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "Apex: " << m_Apex << std::endl;
  os << indent << "AngleZ: " << m_AngleZ << std::endl;
  os << indent << "ApertureAngleX: " << m_ApertureAngleX << std::endl;
  os << indent << "ApertureAngleY: " << m_ApertureAngleY << std::endl;
  os << indent << "TopPlane: " << m_TopPlane << std::endl;
  os << indent << "BottomPlane: " << m_BottomPlane << std::endl;
  os << indent << "RotationPlane: " << m_RotationPlane << std::endl;
}
} // end namespace itk

#endif
