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

//G includes
#include <GForm.h>
#include <GVector3.h>
#include <GElementID.h>
#include <GElement.h>

#include <GweController.h>

//Qt includes
#include <qgl.h>
#include <qmutex.h>
#include <qthread.h>
#include <qvaluelist.h>
#include <qtimer.h>


//this belongs to the client engine
namespace GCE
{

/**
 * \class CViewProperties GOpenGLFrame.h
 * \brief Holds information that is relevant for camera settings
 * @author Raphael Langerhorst
 * @todo Move this class into GGOpenGLFrame as it needs not really be independent.
 */
class CViewProperties
{
  public:
  
    CViewProperties()
    : ViewPosition(0,0,-1),
      ViewTarget(0,0,0),
      ViewUp(0,1,0),
      fov(90),
      NearClippingPlane(0.01),
      FarClippingPlane(10),
      width(768),
      height(1024),
      CameraElement(0)
    {
    }
  
    GCS::GVector3 ViewPosition;
    GCS::GVector3 ViewTarget;
    GCS::GVector3 ViewUp;
    double fov;
    double NearClippingPlane;
    double FarClippingPlane;
    
    int width, height;
    
    /**
     * Holds the ID of the element for which the
     * view properties apply since this modifies
     * the view transformation.
     * @note If CameraElement.getID() returns 0 then
     *       there is no parent transformation.
     */
    GCS::GElementID CameraElement;
    
    void update()
    {          
      glViewport(0,0,width,height);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(fov,(double)width/(double)height,NearClippingPlane,FarClippingPlane);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
    }
};

/**
 * \class GGOpenGLFrame GGOpenGLFrame.h
 * \brief A frame that can draw GOpenGLForm objects.
 * @author Raphael Langerhorst
 *
 * @todo The client interface could use MUCH improvement, its currently a very basic implementation.
 * This class represents the 3D interface. It allows for hierarchical rendering
 * of elements, specifically elements that use GOpenGLForm.
 */

class GOpenGLFrame : public QGLWidget, public QMutex
{
  Q_OBJECT
  private:
    
    const GWE::GweController* Gwe;
    
    QValueList<GCS::GElementID> TopElements;
    
    /**
     * Used to know for which elements the respective server name should be shown.
     * It's determined at the beginning of each render cycle and stored here.
     */
    GCS::GElementID CameraParent;
    
    /**
     * Stores the time for the last rendering periods.
     * This is used for frames per second calculations.
     * On each FPS update the list is cleared.
     */
    QValueList<double> RenderingPeriods;
    
    /**
     * The calculated fps value.
     */
    double FramesPerSecond;
    
    /**
     * Time since last fps update. This is summed up in each
     * rendering cycle, if it reaches 1 second, the FPS value
     * is updated. The unit is milliseconds.
     */
    double TimeSinceFpsUpdate;
    
    /**
     * Used to measure rendering periods.
     */
    QTime FpsTimer;
    
  protected:
    void renderElement(const GCS::GElement* element);
    
  protected:
    
    /**
     * Transforms the matrix on the stack according to given form.
     * Position, rotation and scale (ellipsoid) are taken into account.
     */
    void transform(const GCS::GForm* form) const;
    
    /**
     * Renders given form
     * @param RGBA an array with 4 float values, red, green, blue and alpha
     */
    void render(const GCS::GForm* form, float* RGBA) const;
  
    /**
     * Started as singleshot timer after each redraw,
     * connected to update()
     */
    QTimer RedrawTimer;
  
    bool StopRendering;
  
    /**
     * How many milliseconds to wait after finishing drawing
     * before redrawing.
     */
    int RedrawSleepTime_ms;
  
  public:
    
    CViewProperties ViewProperties;

    GOpenGLFrame(const GWE::GweController* gwe, QWidget* parent = 0, const char* name = 0);

    ~GOpenGLFrame()
    {}
    
  public slots:
    
    /**
     * starts a thread that constantly sends paint events to this OpenGL frame
     * to ensure repainting;
     * a value of 20 means a framerate of 50 fps under optimum conditions;
     */
    virtual void startRendering(int sleep_ms_between_updates_min = 20);
    
    virtual void stopRendering();
    
    virtual void initializeGL();
    
    virtual void resizeGL(int w, int h);
    
    virtual void paintGL();
    
    virtual void addTopElement(const GCS::GElementID& element);
    
    virtual void removeTopElement(const GCS::GElementID& element);
    
  signals:
    
    /**
     * Emitted just before starting the rendering cycle,
     * no transformation,... done before emitting this signal.
     */
    void beforeRendering();
    
    /**
     * Emitted after rendering, all content is rendered.
     */
    void afterRendering();

};

}

#endif
