/*
 * Copyright (c) 2008, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef OGRE_TOOLS_CAMERA_BASE_H_
#define OGRE_TOOLS_CAMERA_BASE_H_

#include <rviz/ogre_helpers/ogre_vector.h>
#include <OgreQuaternion.h>

namespace Ogre
{
class Camera;
class SceneNode;
class SceneManager;
} // namespace Ogre

namespace rviz
{
/**
 * \class CameraBase
 * \brief Generic interface for a camera
 *
 * Provides a interface that a camera can override, providing interchangeability between different camera
 * types.
 * Specific implementation is left to the child class.
 */
class CameraBase
{
public:
  /**
   * \brief Constructor
   * @param scene_manager Scene manager this camera is displaying
   */
  CameraBase(Ogre::SceneManager* scene_manager);
  virtual ~CameraBase();

  /**
   * \brief Get the Ogre camera associated with this camera object
   * @return The Ogre camera associated with this camera object
   */
  Ogre::Camera* getOgreCamera()
  {
    return camera_;
  }

  /**
   * \brief Set a scene node that all camera transformations should be relative to
   * @param node The node
   */
  void setRelativeNode(Ogre::SceneNode* node);
  /**
   * \brief Called when the relative node changes
   */
  virtual void relativeNodeChanged()
  {
  }

  virtual void update() = 0;

  /**
   * \brief Set the position of the camera
   */
  virtual void setPosition(const Ogre::Vector3& position);
  /**
   * \brief Set the orientation of the camera
   */
  virtual void setOrientation(const Ogre::Quaternion& orientation);

  /**
   * \brief Yaw the camera.
   *
   * Calls to yaw are cumulative, so:
   *   yaw(PI);
   *   yaw(PI);
   *
   * is equivalent to
   *  yaw(2*PI);
   *
   * @param angle Angle to yaw, in radians
   */
  virtual void yaw(float angle) = 0;
  /**
   * \brief Pitch the camera.
   *
   * Calls to pitch are cumulative, so:
   *   pitch(PI);
   *   pitch(PI);
   *
   * is equivalent to
   *  pitch(2*PI);
   *
   * @param angle Angle to pitch, in radians
   */
  virtual void pitch(float angle) = 0;
  /**
   * \brief Roll the camera.
   *
   * Calls to roll are cumulative, so:
   *   roll(PI);
   *   roll(PI);
   *
   * is equivalent to
   *  roll(2*PI);
   *
   * @param angle Angle to roll, in radians
   */
  virtual void roll(float angle) = 0;

  /**
   * \brief Set the orientation of the camera from a quaternion
   */
  virtual void setOrientation(float x, float y, float z, float w) = 0;
  /**
   * \brief Set the position of the camera
   */
  virtual void setPosition(float x, float y, float z) = 0;

  /**
   * \brief Set the position/orientation of this camera from another camera.
   *
   * @param camera The camera to set from
   */
  virtual void setFrom(CameraBase* camera) = 0;

  /**
   * \brief Get the position of this camera
   * @return The position of this camera
   */
  virtual Ogre::Vector3 getPosition() = 0;
  /**
   * \brief Get the orientation of this camera
   * @return The orientation of this camera
   */
  virtual Ogre::Quaternion getOrientation() = 0;

  /**
   * \brief Point the camera at the specified point
   * @param point The point to look at
   */
  virtual void lookAt(const Ogre::Vector3& point) = 0;

  /**
   * \brief Move the camera relative to its orientation
   *
   * @param x Distance to move along the X-axis
   * @param y Distance to move along the Y-axis
   * @param z Distance to move along the Z-axis
   */
  virtual void move(float x, float y, float z) = 0;

  virtual void mouseLeftDown(int /*x*/, int /*y*/)
  {
  }
  virtual void mouseMiddleDown(int /*x*/, int /*y*/)
  {
  }
  virtual void mouseRightDown(int /*x*/, int /*y*/)
  {
  }
  virtual void mouseLeftUp(int /*x*/, int /*y*/)
  {
  }
  virtual void mouseMiddleUp(int /*x*/, int /*y*/)
  {
  }
  virtual void mouseRightUp(int /*x*/, int /*y*/)
  {
  }

  /**
   * \brief Handle a left mouse button drag
   *
   * @param diff_x Pixels the mouse has moved in the (window space) x direction
   * @param diff_y Pixels the mouse has moved in the (window space) y direction
   */
  virtual void mouseLeftDrag(int diff_x, int diff_y, bool ctrl, bool alt, bool shift) = 0;
  /**
   * \brief Handle a middle mouse button drag
   *
   * @param diff_x Pixels the mouse has moved in the (window space) x direction
   * @param diff_y Pixels the mouse has moved in the (window space) y direction
   */
  virtual void mouseMiddleDrag(int diff_x, int diff_y, bool ctrl, bool alt, bool shift) = 0;
  /**
   * \brief Handle a right mouse button drag
   *
   * @param diff_x Pixels the mouse has moved in the (window space) x direction
   * @param diff_y Pixels the mouse has moved in the (window space) y direction
   */
  virtual void mouseRightDrag(int diff_x, int diff_y, bool ctrl, bool alt, bool shift) = 0;
  /**
   * \brief Handle a scrollwheel change
   *
   * @param diff Number of "units" the scrollwheel has moved
   * @todo Probably need to pass in how many units there are in a "click" of the wheel
   */
  virtual void scrollWheel(int diff, bool ctrl, bool alt, bool shift) = 0;

  /**
   * \brief Loads the camera's configure from the supplied string (generated through toString())
   * @param str The string to load from
   */
  virtual void fromString(const std::string& str) = 0;
  /**
   * \brief Returns a string representation of the camera's configuration
   */
  virtual std::string toString() = 0;

protected:
  Ogre::Camera* camera_;              ///< Ogre camera associated with this camera object
  Ogre::SceneManager* scene_manager_; ///< Scene manager this camera is part of

  Ogre::SceneNode* relative_node_;
};

} // namespace rviz

#endif /*OGRE_TOOLS_CAMERA_BASE_H_*/
