//-*-C++-*-
/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2000-2002 by Andreas Zehender
    email                : zehender@kde.org
**************************************************************************

**************************************************************************
*                                                                        *
*  This program is free software; you can redistribute it and/or modify  *
*  it under the terms of the GNU General Public License as published by  *
*  the Free Software Foundation; either version 2 of the License, or     *
*  (at your option) any later version.                                   *
*                                                                        *
**************************************************************************/


#ifndef PMGLVIEW_H
#define PMGLVIEW_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <qwidget.h>
#include <qpoint.h>
#include <qdatetime.h>
#include <qtimer.h>
#include <qpixmap.h>

#include "pmcontrolpoint.h"
#include "pmvector.h"
#include "pmmatrix.h"

const int controlPointSize = 7;

class PMObject;
class PMPart;
class PMCamera;
class PMGLViewConnector;
class KConfig;

/**
 * Widget for rendering the scene with OpenGL
 */
class PMGLView : public QWidget
{
   Q_OBJECT
public:
   /** Type of the view */
   enum PMViewType { PMViewPosX, PMViewNegX, PMViewPosY, PMViewNegY,
                     PMViewPosZ, PMViewNegZ, PMViewCamera };
   /** Constructor */
   PMGLView( PMPart* part, PMGLViewConnector* conn, PMViewType t,
             QWidget* parent = 0, const char* name = 0, WFlags f = 0 );
   /** Destructor */
   ~PMGLView( );
   /** Enables/disables translating the view with the mouse */
   void enableTranslateMode( bool yes = true );
   /** Enables/disables scaling the view with the mouse */
   void enableScaleMode( bool yes = true );

   /** Returns true if the opengl projection is up to date */
   bool isProjectionUpToDate( ) const { return m_projectionUpToDate; }
   /** Sets the projection up to date flag */
   void setProjectionUpToDate( bool yes ) { m_projectionUpToDate = yes; }
   
   /** Sets the scale */
   void setScale( double scale );
   /** Returns the scale */
   double scale( ) const { return m_dScale; }
   /** Sets the views translation in x direction */
   void setTranslationX( double d );
   /** Returns the views translation in x direction */
   double translationX( ) const { return m_dTransX; }
   /** Sets the views translation in y direction */
   void setTranslationY( double d );
   /** Returns the views translation in y direction */
   double translationY( ) const { return m_dTransY; }

   /** Returns the 2D control points position in the view */
   const QPtrList<PMVector>& controlPointsPosition( ) const
   {
      return m_controlPointsPosition;
   }
   /** Returns the last right mouse click position */
   PMVector contextClickPosition( ) const { return m_contextClickPosition; }
   
   /** Returns the view type */
   PMViewType type( ) const { return m_type; }
   /** Sets the view type */
   void setType( PMViewType t );
   /** Sets the camera */
   void setCamera( PMCamera* c );
   /** Returns the camera */
   PMCamera* camera( ) const { return m_pCamera; }

   /** Saves the configuration */
   static void saveConfig( KConfig* cfg );
   /** Restores the configuration */
   static void restoreConfig( KConfig* cfg );

   /** Returns true if the glx stuff was initialized successfully */
   bool isValid( ) const;
   /** Sets this view as the current rendering view */
   void makeCurrent( );
   /** Swaps the opengl buffers */
   void swapBuffers( );

   /** Returns the view type as string */
   static QString viewTypeAsString( PMViewType t );
public slots:
   /** Sets the view normal vector to the positive x-axes */
   void slotSetTypePosX( ) { setType( PMViewPosX ); }
   /** Sets the view normal vector to the negative x-axes */
   void slotSetTypeNegX( ) { setType( PMViewNegX ); }
   /** Sets the view normal vector to the positive y-axes */
   void slotSetTypePosY( ) { setType( PMViewPosY ); }
   /** Sets the view normal vector to the negative y-axes */
   void slotSetTypeNegY( ) { setType( PMViewNegY ); }
   /** Sets the view normal vector to the positive z-axes */
   void slotSetTypePosZ( ) { setType( PMViewPosZ ); }
   /** Sets the view normal vector to the negative z-axes */
   void slotSetTypeNegZ( ) { setType( PMViewNegZ ); }
   
   /** Updates the control points */
   void slotNewControlPoints( const PMControlPointList& list, const PMMatrix& t );
   /** Called when an object is changed.
    * @see PMPart::objectChanged( ) */
   void slotObjectChanged( PMObject* obj, const int mode, QObject* sender );
   /** Called when the control point selection is changed */
   void slotControlPointSelectionChanged( QObject* sender );
   /** Restarts rendering */
   void slotRefresh( );
   /** Clears all data */
   void slotClear( );
   /** Stops rendering */
   void slotStopRendering( );
   /** Repaints the view if it is a camera view */
   void slotActiveRenderModeChanged( );

   /** Connected to the render manager */
   void slotRenderingStarted( PMGLView* view );
   /** Connected to the render manager */
   void slotAboutToUpdate( PMGLView* view );
   /** Connected to the render manager */
   void slotRenderingFinished( PMGLView* view );

private slots:
   void slotMouseChangeTimer( );
   void slotAutoScroll( );

signals:
   /** Emitted when rendering has to be restarted */
   void refresh( PMGLView* w );
   /** Emitted when an object is changed */
   void objectChanged( PMObject* obj, const int mode, QObject* sender );
   /** Emitted when the control point selection is changed */
   void controlPointSelectionChanged( QObject* sender );
   /** Emitted when the view gets the focus */
   void viewActivated( PMGLView* view );
   /** Emitted when a graphical change starts */
   void graphicalChangeStarted( );
   /** Emitted when a graphical change was finished */
   void graphicalChangeFinished( );
   /** Emitted when the mouse is over a control point */
   void controlPointMessage( const QString& msg );
   /** Emitted before the context menu is shown */
   void contextMenuAboutToShow( );
   /** Emitted in the destructor */
   void destroyed( PMGLView* v );
   /** Emitted when the view type changes */
   void viewTypeChanged( const QString& str );
   
protected:
   /** Initializes the glx stuff */
   virtual void initializeGL( );
   /** */
   virtual void resizeEvent( QResizeEvent* e );
   /** */
   virtual void paintEvent( QPaintEvent* e );
   /** */
   virtual void mousePressEvent( QMouseEvent* e );
   /** */
   virtual void mouseReleaseEvent( QMouseEvent* e );
   /** */
   virtual void mouseMoveEvent( QMouseEvent* e );
   /** */
   virtual void keyPressEvent( QKeyEvent* e );
   /** Event to zoom in / zoom out the viewport by mouse wheel */
   virtual void wheelEvent( QWheelEvent* e );

private:
   /** Recalculates the position of the control points on the screen */
   void recalculateControlPointPosition( );
   /** Recalculates m_controlPointsTransformation and
    * m_inversePointsTransformation */
   void recalculateTransformations( );
   /**
    * Returns the mouse 3D position, when the control point cp is selected
    *
    * x and y are the screen coordinates of the mouse.
    */
   PMVector mousePosition( PMControlPoint* cp, int x, int y );
   /** Checks if a control point is under the mouse */
   void checkUnderMouse( int x, int y );
   /** Repaints the view */
   void repaint( bool graphicalChange = false );
   /** Starts a graphical change */
   void startChange( const QPoint& mousePos );
   /** Graphical Change */
   void graphicalChange( const QPoint& mousePos );
   /** Selects/deselecs the control point. If cp is 0, all control points are
    * selected/deselected. */
   void selectControlPoint( PMControlPoint* cp, bool select, bool deselectOthers = true );
   /** Invalidates the projection and repaints the view */
   void invalidateProjection( bool graphicaChange = false );

   /** Starts multiple selection mode */
   void startSelection( );
   /** Restores the widget under the selection rect */
   void restoreSelectionBox( );
   /** Saves the widget under the selection rect */
   void saveSelectionBox( );
   /** Paints the selection box */
   void paintSelectionBox( );
   /** Calculates the selection box */
   void calculateSelectionBox( int& sx, int& sy, int& ex, int& ey, int& w, int& h );

   double screenToInternalX( int x ) const;
   double screenToInternalY( int y ) const;

   /**
    * Returns the top level object for rendering (a declaration or the scene)
    */
   PMObject* topLevelRenderingObject( PMObject* obj ) const;
   
   /** Type of the view (camera, xy, ... ) */
   PMViewType m_type;
   /** Pointer to the part */
   PMPart* m_pPart;
   /** Pointer to the gl view manager object */
   PMGLViewConnector* m_pConnector;
   /** True if "scale view" is active */
   bool m_bScaleMode;
   double m_scaleIntX, m_scaleIntY;
   /** true if "translate view" is active */
   bool m_bTranslateMode;
   /**
     * True if "scale view" or "translate view" is active and the left
     * mouse button is pressed.
     */
   bool m_bMousePressed;
   /** MidButton pressed */
   bool m_bMidMousePressed;
   /** True if a graphical change is active */
   bool m_bGraphicalChangeMode;
   bool m_bMementoCreated;
   /** The old mouse position */
   QPoint m_mousePos;
   QPoint m_changeStartPos;
   QPoint m_currentMousePos;
   QTimer m_startTimer;
   QTime m_changeStartTime;
   bool m_bDeselectUnderMouse;
   bool m_bSelectUnderMouse;

   /** Member variables for multiple selection mode */
   QPixmap m_selectionPixmap[4];
   QPoint m_selectionStart, m_selectionEnd;
   bool m_bMultipleSelectionMode;
   bool m_bSelectionStarted;

   /** Member variables for autoscroll */
   bool m_bAutoScroll;
   double m_autoScrollSpeed;
   QTimer m_autoScrollTimer;
   QTime m_lastAutoScrollUpdate;
   int m_autoScrollDirectionX, m_autoScrollDirectionY;
   /** Rendering */
   bool m_bAboutToUpdate;
   
   /** Scale of the view */
   double m_dScale;
   /** X-translation of the view */
   double m_dTransX;
   /** Y-translation of the view */
   double m_dTransY;

   /** Control points of the active object */
   PMControlPointList m_controlPoints;
   /** Control point under the mouse */
   PMControlPoint* m_pUnderMouse;
   /** Position of the control points on the screen */
   QPtrList<PMVector> m_controlPointsPosition;
   /** Position of the last right mouse click */
   PMVector m_contextClickPosition;
   /**
    * Transformation of the control points
    *
    * Allways m_viewTransformation * m_objectsTransformation.
    */
   PMMatrix m_controlPointsTransformation;
   /** Inverse of m_controlPointsTransformation */
   PMMatrix m_inversePointsTransformation;
   /** True if m_inversePointsTransformation is valid */
   bool m_bInverseValid;
   /** Normal vector of the view */
   PMVector m_normal;
   /** Transformation of the view (scale and translation) */
   PMMatrix m_viewTransformation;
   /** Transformation of the active object */
   PMMatrix m_objectsTransformation;
   /** The camera */
   PMCamera* m_pCamera;
   PMObject* m_pActiveObject;
   /** true if the opengl projection is up to date */
   bool m_projectionUpToDate;
   int m_visibilityLevel;
};


#endif
