// Common procedures for all GL operations performed
// on an XForm window
//
// Eric Amram, Oct 1997
//
// Some part of this file comes from Sudeep's and Satyan's codes
// God bless them
//
// Watch the conventions, the include files to customize
//    and the (virtual) display class to derive from (that's all)
//

// **********************************************************************
// ********* CONVENTIONS on names in fdesign ******************
// ************************************************************
//
// GL canvases MUST have names GLCanvas[0], GLCanvas[1], ...
//
// **********************************************************************

//
// USE : 1. create instance of class XForms<give FD_NAME from your form>
//             Any_name( fct create_form_?NAME?, 
//                       num of GL canvases, 
//                       array pointers on derived DisplayGLCanvas class)
//          CAUTION : This array must survive in memory meanwhile 
//                    (use new allocation if necessary)
//       2. Call once FirstXformsInit()  [ in main() ]
//
//       3. Initialize all your buttons the way you want
//
//       4. call Any_name.initFormsInteraction(); [init form and 
//                                                 events handling]
//
//       5. call Any_name.mainLoopInteraction(); [main loop]
//
//       6. go out and have fun since it's the only thing you need to do
//
// Practically, you derive as many classes as you need different display
// Then you define an array of pointers     class DisplayGLCanvas *test[4];
// and you instantiate the pointers: test[i] = new Display_i_th_class(); 
//
// Note : A button without callback function makes the form exit 
//

#ifndef _GL_CANVAS_H__
#define _GL_CANVAS_H__

#include <GL/gl.h>
#include "forms.h"

// math library
#include "gprims/vector.H"



// **********************************************************************
// ***************** INCLUDES ********************
// ***********************************************
// ***    Please add all fdesign-created .h    ***
// *** AFTER this launchGLforms.h in main prog ***
// ***********************************************

// **********************************************************************
// *** (Virtual) Base class for displaying ***
// *** inside GL canvases and picking.     ***
// *** Each canvas has its own display     ***
// *** & pick function derived from this   ***
// *******************************************

typedef unsigned char uchar;
class GLCan;

// object handle type definition for user selection
typedef void*      HANDLE ;
typedef HANDLE*    PHANDLE ;


// Functions to interface between C xforms and C++ classes
int     TempHandleEvent  ( FL_OBJECT*, Window, int, int, XEvent *, void * );


typedef struct MOUSEINFO_tag {
  int   curX;         // current mouse X Position
  int   curY;         // current mouse X Position
  int   prevX;        // previous mouse X Position
  int   prevY;        // previous mouse X Position
  int   mouseButton;   // ID of button which was clicked
  int   doubleClick;   // flag to signify button doubleclick
  int   buttonMotion;  // flag for button motion
  // event which generated this callback { ButtonPress, ButtonRelease }
  int   event;         
} MOUSEINFO, *PMOUSEINFO;



// Bounds in 2D GL coord for image rendering
#define LGF_TEX_1         -1.0
#define LGF_TEX_2         1.0
#define LGF_SIZE_DOT      8e-3
#define LGF_PICK_BORDER   5
#define LGF_DEFAULT_IMG   "/home/amram/Images/aaratip.rgb"
#define LGF_STATE_CHANGED 32
#define LGF_STATE_NOPICK  16
#define LGF_STATE_SHIFT   10



// =======================================================
// ---------- Special display class for 2D Input ---------
// =======================================================
static const GLfloat LGF_tex_rect[4][2] 
= { {LGF_TEX_1, LGF_TEX_1},  {LGF_TEX_2, LGF_TEX_1},    // C D listed {ABCD}
    {LGF_TEX_1, LGF_TEX_2},  {LGF_TEX_2, LGF_TEX_2} };  // A B



// MODE_PICK2D allows continuous picking : it triggers on button press
// instead of button release and triggers as long as LEFT button is on
// Send (-10000, -10000) to notice button is released

#define GLCAN_INTERACT_MODE_NONE            0
#define GLCAN_INTERACT_MODE_PICK2D          1   
#define GLCAN_INTERACT_MODE_2D              2
#define GLCAN_INTERACT_MODE_3D              3
#define GLCAN_INTERACT_MODE_EYEMOUSE_3D     4

//
// Grouping canvases for simultaneous updates
//
#define GLCAN_MAX_GROUP_MEMBERS             10


class DisplayGLCanvas {
  friend class GLCan;
public:
  DisplayGLCanvas                      ( void ); 
  DisplayGLCanvas                      ( DisplayGLCanvas * ); 

  // should be overloaded by user to determine world-space axial bounding box

  virtual void          UserBounds     ( Point3&, Point3& ) {};
  virtual void          UserDraw       ( void ) {};
  virtual PHANDLE       UserHandle     ( void );     
  virtual int           UserAction     ( const Point3&, const Point3&, 
					 PMOUSEINFO, PHANDLE );
  
  // Init with default lightings, matrices, etc.
  virtual void          UserGLInit     ( void );


  // GL lists can be generated for DisplayGLCanvas()
  // A modify flag allows to demand recompilation
  inline void           SetGLModify    ( void ) { modifyGLlist=1; }
  // Notify modification
  inline void           UnsetModify    ( void ) { modifyGLlist=0; }
  // 0 if no modif  
  inline char           getGLModify    ( void ) { return modifyGLlist; }  

public:
  void                  resetView      ( void );


private:
  // camera/eye to mouse point ray
  int    rayAction           ( PMOUSEINFO, PHANDLE );             
  // int    rayUpdate           ( PMOUSEINFO, PHANDLE );
  void   addCanvasToGroup    ( DisplayGLCanvas *);
  
public:  
  char interactionMode; // One of the GLCAN_INTERACT_MODE_
  // Add your own data for display with derivation
  // XXX 
  int    _dirty;  // true iff a redraw is required

protected:
  // to maintain GL lists - allocated by initGL
  // GLuint     displaylist; 
  // GLdouble   modelview[16], projmatr[16];
  // GLint      viewport[4];
  // GLdouble   Pbase[7];
  // pikx, piky in [0..1][0..1]
  //double     pikx, piky;  
  // 2D (GL) bounding box for conversion to range [0..1]^2
  //GLfloat    bound[4];


  FL_OBJECT  *id;        // OpenGL Canvas object (GLCanvas[number])
  float       matr[16];       // transform matrix   
  GLfloat    _MPinv[16];

private:  
  char                        modifyGLlist;  
  int                        _viewChange;
  static DisplayGLCanvas   **_canvasGroups;   
  static int                 _numGroups;
};



// Practically, you derive as many classes as you need different display
// Then you define an array of pointers     class DisplayGLCanvas *test[4];
// and you instantiate the pointers:  test[i] = new Display_i_th_class(); 

// **********************************************************************
// ***END OF NOTICE******************************************************
// **********************************************************************

#define MAX(a, b)   ((a) < (b) ? (b) : (a))
#define TIMER_MSEC  30

// =======================================================
// Global vars used for lighting model: some useful const
// =======================================================

// Material | Light color
const GLfloat materlight_white[]  = {1.0, 1.0, 1.0, 1.0};
const GLfloat materlight_lgrey[]  = {0.8, 0.8, 0.8, 1.0};
const GLfloat materlight_grey[]   = {0.5, 0.5, 0.5, 1.0};
const GLfloat materlight_dgrey[]  = {0.2, 0.2, 0.2, 1.0};
const GLfloat materlight_lblack[] = {0.1, 0.1, 0.1, 1.0};
const GLfloat materlight_black[]  = {0.0, 0.0, 0.0, 1.0};
const GLfloat materlight_red[]    = {1.0, 0.0, 0.0, 1.0};
const GLfloat materlight_green[]  = {0.0, 1.0, 0.0, 1.0};
const GLfloat materlight_blue[]   = {0.0, 0.0, 1.0, 1.0};
const GLfloat materlight_mred[]   = {0.6, 0.0, 0.0, 1.0};
const GLfloat materlight_mgreen[] = {0.0, 0.6, 0.0, 1.0};
const GLfloat materlight_mblue[]  = {0.0, 0.0, 0.6, 1.0};
const GLfloat materlight_brown[]  = {0.6, 0.2, 0.0, 1.0};
const GLfloat materlight_yellow[] = {1.0, 1.0, 0.0, 1.0};
const GLfloat materlight_beige[]  = {0.3, 0.0, .35, 1.0};
const GLfloat materlight_pink[]   = {0.8, 0.4, 0.6, 1.0};
const GLfloat materlight_orange[] = {1.0, 0.5, 0.0, 1.0};
const GLfloat materlight_purple[] = {1.0, 0.0, 1.0, 1.0};
const GLfloat materlight_cyan[]   = {0.0, 1.0, 1.0, 1.0};
const GLfloat materlight_cyan2[]  = {0.3, 0.8, 0.9, 1.0};

// Light properties
const GLfloat no_mat[]            = {0, 0, 0, 1};
const GLfloat no_shininess[]      = {0};
const GLfloat light_direction[]    = {8.0, 5.0, 10.0, 0.0};

// =======================================================


// --- All data associated with a given GL canvas -> position, rotation, mouse, ...
class GLCan {

public:
  // Necessarily no args since we build arrays of this
  GLCan( void )    { };
 
  // Inits var + basicsGL
  void   initGL( DisplayGLCanvas*, const int&, const int&, FL_OBJECT* );
  // Change scale for translation
  void   changeTranslZoomScale(const float& txy=1.0, const float& zxy=1.0) 
    { trScale = txy; zoomS = zxy; }
  // Rendering
  void   render( void );
  void   posReset( void );
  

  // DO NOT USE - Handle mouse inputs in the form and process them
  void       handleEvent           ( XEvent *, const int&, const int& );

public:
  int    itmo;                  // id of the timeout;


protected:
  // Init with defaults lightings, matrices, etc.
  void initializeGLState               ( void );

  // Routines for updating view
  void updateBallRot(float, float);    // Left Button for rotation
  void updateBallZoom( void );         // Right Button for zoom
  void updateViewRot( void);           // Left+Middle for rotation in image plane
  void updateViewTranslate( void);     // Middle Button for translation
  void transform( void ) { glMultMatrixf(displC->matr); }

  // Customized display function for every canvas => no general virtual funct
  DisplayGLCanvas *displC;
  
  // keep info about position, rotation, etc.
  int idnum;            // id number in DisplayGLCanvas array 
  int numGLcan;         // total num of canvases to render
  FL_OBJECT *id;        // OpenGL Canvas object (GLCanvas[number])
  Window wind;          // Corresponding window
  int width, height;    // Canvas window size
  float aspect;         // Aspect of the window (width/height)
  // float matr[16];       // transform matrix
  
  float trScale, zoomS; // translation scale + zoom scale

  int cur_x, cur_y;     // current mouse position in this canvas
  int prev_x, prev_y;   // previous mouse position
  char MouseDown[3];    // Mouse buttons state
  char interact;        // Interaction Mode (see GLCAN_INTERACT_MODE_)
  
  long cur_usec, prev_usec;  // Time stamps at every mouse measure

  int  _doubleClick;
  int  _lastMouse;
  int  _mxD, _myD;
  int  _buttonMotion;
};
//--- end of GLCan class def


// ============== First Init - to do in main() =======================
void FirstXformsInit();
// ===================================================================


// _GL_CANVAS_H__
#endif     



