/***************************************************************************
 *   Copyright (C) 2004 by Raphael Langerhorst                             *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#ifndef GCECAMERAH
#define GCECAMERAH

#include <GForm.h>
#include <GVector3.h>

#include <qobject.h>
#include <qthread.h>
#include <qevent.h>
#include <qdatetime.h>

#include "GOpenGLFrame.h"

namespace GCE 
{

/**
  \class GCamera GCamera.h
  \brief Listens for user input and changes the view accordingly.
  @author Raphael Langerhorst
  
  Listens for user input events and changes view parameters of
  an GOpenGLFrame object accordingly.

  Currently it is very primitive and is intended to just meet the
  requirements of the demo application.
  
  @todo The camera currently only works correctly when the attached
        form has no transformation from a parent element as the
        transformation is not recognised. Maybe this needs to be
        handled in GOpenGLFrame. DONE, but needs testing.
 
  @todo do NOT depend / use GOpenGLFrame directly, instead use
        signals / slots where appropriate. (But how to handle key
        presses?)
*/
class GCamera : public QObject
{
  Q_OBJECT
  
  protected:
  
    /**
     * Used to determine elapsed time to determine correct speed updates (when keys are pressed)
     */
    QTime KeyUpdateTime;
  
    /**
     * the Camera object will use this frame for setting the
     * viewing properties.
     */
    GOpenGLFrame* Frame;
    
    /**
     * The camera will track exactly this form in terms of position.
     */
    GCS::GForm* Form;
    
    /**
     * This determines where the camera position is placed in relation to
     * the targeted form. the given distance is multiplied with
     * the (max) radius of the form to get the distance between view
     * position and view target.
     * A value of 100 would for example place the camera exactly at the
     * border of the form, a value of 200 should give a nice view.
     */
    double ViewDistancePercent;

    /**
     * Determines the actual speed of the camera.
     */
    double SpeedFactor;
    
    /**
     * Positive value means right.
     */
    double speedx;
    
    /**
     * Positive value means up.
     */
    double speedy;
    
    /**
     * If true then the thread will stop executing.
     */
    bool shutdown;
    
    
    // KEY STATES
    bool left_key_down;
    bool right_key_down;
    bool up_key_down;
    bool down_key_down;
    bool space_key_down;
  
  public:
    /**
     * Constructor.
     */
    GCamera(GOpenGLFrame* frame = NULL, GCS::GForm* form = NULL, QObject* parent = 0, const char* name = 0);

    /**
     * Destructor.
     */
    ~GCamera();
    
  protected:
    
    /**
     * Filters user input.
     */
    virtual bool eventFilter(QObject* watched, QEvent* e);
   
  public slots:
  
    /**
     * Updates the view settings.
     */
    virtual void update();
    
    /**
     * Sets the OpenGL frame that the camera uses.
     * @note The Camera thread should NOT be running when changing this attribute.
     * @see Frame.
     */
    virtual void setFrame(GOpenGLFrame* frame);
    
    /**
     * Sets the Form that will be followed with camera position.
     * @note The Camera thread should NOT be running when changing this attribute.
     */
    virtual void setForm(GCS::GForm* form);
    
    /**
     * Starts the camera thread.
     */
    virtual void startCamera();
    
    /**
     * Stops the camera thread.
     * @note Blocking call until camera thread is stopped.
     */
    virtual void stopCamera();
    
    /**
     * Sets the ViewDistancePercent attribute.
     * @see ViewDistancePercent.
     */
    virtual void setViewDistance(int new_distance_percent);

    /**
     * Sets the speed factor.
     * @see SpeedFactor
     */
    virtual void setSpeedFactor(double speed_factor);
    
  signals:
    
    /**
     * Emitted when the camera, usually on behalf of the user,
     * wants to change the speed. Since a camera is connected
     * to a form, this signal should probably be connected to
     * a GBE::GMoveAgent::addTranslationSpeedImpulse()
     */
    void translationSpeedImpulse(const GCS::GVector3& v);
    
    /**
     * Emitted when the camera, usually on behalf of the user,
     * wants to change the speed. Since a camera is connected
     * to a form, this signal should probably be connected to
     * a GBE::GMoveAgent::addRotationSpeedImpulse()
     */
    void rotationSpeedImpulse(const GCS::GVector3& v);
    
    /**
     * Emitted when all movement of the camera element should stop.
     */
    void stopMovement();

};

};

#endif
