#ifndef LANDED_VIEWER3D
#define LANDED_VIEWER3D

//3d viewer

#include <QObject>
#include <QGLWidget>
#include <QMutex>

#include <vector>
#include <iostream>

#include "viewerbase.hh"

//geometric defaults
//default viewpoint in 3d space
//this is the distance back from the origin
//it shows up as an initial negative z-shift, so bare that in mind when choosing the other init values below
//transforms are applied in the order in which they appear here
#define DEFORIG 10000
//default rotation of default viewpoint
#define INITRX 0
#define INITRY 180
#define INITRZ 0
//shifts following those rotations
//the x shift is left-right, the y up-down and the z forward-back
//they are applied not along the original axes but from the perspective of the viewer
#define INITX 0
#define INITY 0
#define INITZ 0

enum THD
  {
    THD_TRACK=0,
    THD_HIT=1,
    THD_DETECTOR=2,
  };

//a translation or rotation by the user
class Move
{
public:
  Move(void)
  {
    isRot=false;
    x=y=z=0;
  }
  bool isRot;
  float x;
  float y;
  float z;
};

//action on mouse input
enum Action
  {
    MOVE=0,
    ZOOM=1,
    PAN=2,
    ORIGIN=3,
  };


//chunk of detector geometry pulled from vrml file
class glShape : public Shape
{
public:
  glShape(QString n, int re, bool sh=true):
    Shape(n, re)
  {
    //gl stuff
    glWire=glFull=-1;
    r=g=b=0;
    //default to show shape
    show=sh;
  }

public:
  //gl handle for wire-frame rendering
  GLint glWire;
  //gl handle for surface rendering
  GLint glFull;
  //colour;
  float r,g,b;
  //show this shape or not
  bool show;
};

class ZOrder
{
public:
  ZOrder(void)
  {
    id=0;
    z=0;
  }
  ZOrder(int sid, int sz)
  {
    id=sid;
    z=sz;
  }
  bool operator<(const ZOrder& other) const
  {
    return other.z<z;
  }
  int id;
  float z;
};

typedef std::vector<Move> MoveCollection;
typedef MoveCollection::size_type MoveIndex;
typedef std::vector<glShape> glShapeCollection;
typedef glShapeCollection::size_type glShapeIndex;
typedef std::vector<ZOrder> ZOrderCollection;
typedef ZOrderCollection::size_type ZOrderIndex;

//3d image widget derived from GL capable widget
class Viewer3D : public QGLWidget, public ViewerBase
{
  Q_OBJECT

public:
  Viewer3D(QWidget*);

private:
  bool open_;
  //handles for gl constructions
  //crosshair which is seen in Move naviation moce
  GLint glCrossHair_;
  //sphwre representing centre in Origin/Centre mode
  GLint glOrigin_;
  //sphere for hit
  GLint glSphere_;
  GLint glWireSphere_;
  //sphere for selected hit
  GLint glSelSphere_;
  //dummy sphere to trick gl into behaving when no other spheres are present
  GLint glDummy_;
  //for tracks
  GLint glCylinder_;
  GLint glWireCylinder_;
  GLint glSelCylinder_;
  //view changes by the user
  //these have to be accumulated for navigation to work properly
  MoveCollection moves_;
  //size of central vertex sphere
  float origSize_;
  //its distance from the viewer
  float origPosn_;
  //current mouse co-ordinates on screen
  int mousex_, mousey_;
  //what to do with user mouse action
  Action action_;
  //detector geometry
  glShapeCollection detector_;
  //order in which detector objects should be drawn
  ZOrderCollection detOrder_;
  //what to show
  bool showFullDet_;
  bool showWireFrame_;
  //what is the mouse pointing at right now
  QString mouseInfo_;
  //a number between 0 and 1 to indicate shape opacity
  float opacity_;
  //background colour
  float bgRed_, bgGreen_, bgBlue_;
  //last image displayed
  bool capture_;
  QImage lastImage_;
  QMutex lock_;
  
private:
  //overrides for parent class functions
  //set up GL lighting and other basics
  void initializeGL(void);
  //window resized
  void resizeGL(int, int);
  //window needs re-painting
  void paintGL(void);
  //mouse action by user
  void mousePressEvent(QMouseEvent*);
  void mouseMoveEvent(QMouseEvent*);
  void mouseDoubleClickEvent(QMouseEvent*);
  //find location where right click took place
  int clickfindGL(int, int);
  //key press
  void keyPressEvent(QKeyEvent*);

private:
  //gl utilities
  //order shapes by z in current rotation/translation frame
  void zOrderGL(void);
//construct cylinder out of rectangles
  void glCylinder(bool=false);
  //construct sphere out of triangles
  void glBlob(bool=false);
  //a cross in the centre of the view
  void glCrossHair(void);
  //perform gl magic to render a shape or track
  void renderShape(glShape&);

public:
  void pointerInfo(int, int);
  //effect of mouse action by user
  Action action(void) const;
  void setAction(Action);
  //add a volume to internal array
  void addShape(const glShape&);
  //shift the viewpoint from outside this class
//   void set_rotation(GLfloat, GLfloat, GLfloat);
//   void set_position(GLfloat, GLfloat, GLfloat);
  //go to default viewpoint
  void reset(bool=true);
  //apply initial transforms
  void init(void);
  //forget current set of shape and tracks and go to default viewpoint
  void clearShapes(void);
  //what to show
  bool showFullDet(void) const;
  void setShowFullDet(bool);
  bool showWireFrame(void) const;
  void setShowWireFrame(bool);
  //move to focus on a specified point
  void seek(float, float, float, float=250.f);
  //add a move to the list of moves of the geometry
  void addMove(int, int);
  //access shape list - use with caution
  //access shape list
  glShapeIndex numShapes(void) const;
  const glShape& shape(glShapeIndex) const;
  //set shape visibility
  void setShapeVis(glShapeIndex, bool);
  //from outside, force refresh
  void refresh(void);
  //change surface opacity
  void setOpacity(float);
  //return shape opacity
  float opacity(void) const;
  //set background colour
  void setBackground(float, float, float);
  float backgroundRed(void) const;
  float backgroundGreen(void) const;
  float backgroundBlue(void) const;
  //set capturing on/off
  void setCapture(bool);
  //get last displayed image
  void lock(void);
  const QImage& lastImage(void);
  void unlock(void);
  
signals:
  //navigation mode changed
  void modeChange(Action);
  //full screen switch
  void fullScreen(void);
  //right click on a volume or track
  void select(int, THD);
  //double-click on a volume
  void hide(int);
};

#endif //ifndef LANDED_VIEWER3D
