/*
 * Copyright (C) 2015 Open Source Robotics Foundation
 *
 * 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
 *
 * 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 GZ_MATH_FRUSTUM_HH_
#define GZ_MATH_FRUSTUM_HH_

#include <gz/math/Angle.hh>
#include <gz/math/AxisAlignedBox.hh>
#include <gz/math/Plane.hh>
#include <gz/math/Pose3.hh>
#include <gz/math/config.hh>
#include <gz/utils/ImplPtr.hh>

namespace gz::math
{
  // Inline bracket to help doxygen filtering.
  inline namespace GZ_MATH_VERSION_NAMESPACE {

  /// \brief Mathematical representation of a frustum and related functions.
  /// This is also known as a view frustum.
  class GZ_MATH_VISIBLE Frustum
  {
    /// \brief Planes that define the boundaries of the frustum.
    public: enum FrustumPlane
    {
      /// \brief Near plane
      FRUSTUM_PLANE_NEAR   = 0,

      /// \brief Far plane
      FRUSTUM_PLANE_FAR    = 1,

      /// \brief Left plane
      FRUSTUM_PLANE_LEFT   = 2,

      /// \brief Right plane
      FRUSTUM_PLANE_RIGHT  = 3,

      /// \brief Top plane
      FRUSTUM_PLANE_TOP    = 4,

      /// \brief Bottom plane
      FRUSTUM_PLANE_BOTTOM = 5
    };

    /// \brief Default constructor. With the following default values:
    ///
    /// * near: 0.0
    /// * far: 1.0
    /// * fov: 0.78539 radians (45 degrees)
    /// * aspect ratio: 1.0
    /// * pose: Pose3d::Zero
    public: Frustum();

    /// \brief Constructor
    /// \param[in] _near Near plane distance. This is the distance from
    /// the frustum's vertex to the closest plane
    /// \param[in] _far Far plane distance. This is the distance from the
    /// frustum's vertex to the farthest plane.
    /// \param[in] _fov Field of view. The field of view is the
    /// angle between the frustum's vertex and the edges of the near or far
    /// plane. This value represents the horizontal angle.
    /// \param[in] _aspectRatio The aspect ratio, which is the width divided
    /// by height of the near or far planes.
    /// \param[in] _pose Pose of the frustum, which is the vertex (top of
    /// the pyramid).
    public: Frustum(double _near,
                    double _far,
                    const math::Angle &_fov,
                    double _aspectRatio,
                    const math::Pose3d &_pose = math::Pose3d::Zero);

    /// \brief Get the near distance. This is the distance from the
    /// frustum's vertex to the closest plane.
    /// \return Near distance.
    /// \sa SetNear
    public: double Near() const;

    /// \brief Set the near distance. This is the distance from the
    /// frustum's vertex to the closest plane.
    /// \param[in] _near Near distance.
    /// \sa Near
    public: void SetNear(double _near);

    /// \brief Get the far distance. This is the distance from the
    /// frustum's vertex to the farthest plane.
    /// \return Far distance.
    /// \sa SetFar
    public: double Far() const;

    /// \brief Set the far distance. This is the distance from the
    /// frustum's vertex to the farthest plane.
    /// \param[in] _far Far distance.
    /// \sa Far
    public: void SetFar(double _far);

    /// \brief Get the horizontal field of view. The field of view is the
    /// angle between the frustum's vertex and the edges of the near or far
    /// plane. This value represents the horizontal angle.
    /// \return The field of view.
    /// \sa SetFOV
    public: math::Angle FOV() const;

    /// \brief Set the horizontal field of view. The field of view is the
    /// angle between the frustum's vertex and the edges of the near or far
    /// plane. This value represents the horizontal angle.
    /// \param[in] _fov The field of view.
    /// \sa FOV
    public: void SetFOV(const math::Angle &_fov);

    /// \brief Get the aspect ratio, which is the width divided by height
    /// of the near or far planes.
    /// \return The frustum's aspect ratio.
    /// \sa SetAspectRatio
    public: double AspectRatio() const;

    /// \brief Set the aspect ratio, which is the width divided by height
    /// of the near or far planes.
    /// \param[in] _aspectRatio The frustum's aspect ratio.
    /// \sa AspectRatio
    public: void SetAspectRatio(double _aspectRatio);

    /// \brief Get a plane of the frustum.
    /// \param[in] _plane The plane to return.
    /// \return Plane of the frustum.
    public: Planed Plane(const FrustumPlane _plane) const;

    /// \brief Check if a box lies inside the pyramid frustum.
    /// \param[in] _b Box to check.
    /// \return True if the box is inside the pyramid frustum.
    public: bool Contains(const AxisAlignedBox &_b) const;

    /// \brief Check if a point lies inside the pyramid frustum.
    /// \param[in] _p Point to check.
    /// \return True if the point is inside the pyramid frustum.
    public: bool Contains(const Vector3d &_p) const;

    /// \brief Get the pose of the frustum
    /// \return Pose of the frustum
    /// \sa SetPose
    public: Pose3d Pose() const;

    /// \brief Set the pose of the frustum
    /// \param[in] _pose Pose of the frustum, top vertex.
    /// \sa Pose
    public: void SetPose(const Pose3d &_pose);

    /// \brief Compute the planes of the frustum. This is called whenever
    /// a property of the frustum is changed.
    private: void ComputePlanes();

    /// \brief Private data pointer
    GZ_UTILS_IMPL_PTR(dataPtr)
  };
  }  // GZ_MATH_VERSION_NAMESPACE
}  // namespace gz::math
#endif  // GZ_MATH_FRUSTUM_HH_
