//-*-c++-*-
/**
 Author: David Auber
 Email : auber@labri.fr
 Last modification : 20/08/2001
 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 Tulip_GLGRAPH_H
#define Tulip_GLGRAPH_H

#ifndef NDEBUG
#define TRACE_EXEC()	
//	cerr << __PRETTY_FUNCTION__ << endl;
#else
#define TRACE_EXEC()
#endif

#if (__GNUC__ < 3)
#include <hash_map>
#else
#include <ext/hash_map>
#endif
#include <string>

#include <GL/gl.h>
#include <tulip/PluginContext.h>
#include <tulip/Observable.h>
#include <tulip/Coord.h>
#include <tulip/Color.h>
#include <tulip/SuperGraph.h>
#include <tulip/Types.h>
#include <tulip/TemplateFactory.h>
#include <tulip/Reflect.h>

#include <tulip/tulipconf.h>
#include "tulip/Camera.h"
#include "tulip/GlGraphStrategy.h"
#include "tulip/Rectangle.h"

class LayoutProxy;
class MetricProxy;
class StringProxy;
class IntProxy;
class SelectionProxy;
class SizesProxy;
class ColorsProxy;
class MetaGraphProxy;
class PropertyProxyContainer;

class Glyph;
class GlyphFactory;
class GlyphContext;
class ListRectangleInt2D;

typedef STL_EXT_NS::hash_map<int, std::string, STL_EXT_NS::hash<int> > GlyphTableString;
typedef STL_EXT_NS::hash_map<int, Glyph *, STL_EXT_NS::hash<int> > GlyphTableGlyph;


struct RectangleInt2D : public tlp::geo::Rectangle<unsigned int> {
  RectangleInt2D(unsigned int x,unsigned int y,unsigned int x1,unsigned int y1) {
    (*this)[0][0]=x;
    (*this)[0][1]=y;
    (*this)[1][0]=x1;
    (*this)[1][1]=y1;
  }
  RectangleInt2D(const RectangleInt2D &r):tlp::geo::Rectangle<unsigned int>(r){}
};

struct ListRectangleInt2D {
  std::vector<RectangleInt2D> data;
  void reset() {
    data.clear();
  }
  bool addRectangle(const RectangleInt2D &rec) {
    bool intersect=false;
    std::vector<RectangleInt2D>::const_iterator it;
    for (it=data.begin();it!=data.end();++it) {
      if (rec.intersect(*it)) {
	intersect=true;
	break;
      }
    }
    if (!intersect) data.push_back(rec);
    return intersect;
  }
};

/**
 * This widget enables an incremental 3D drawing of a Tulip SuperGraph.
 *  It also includes 3D navigation capabilities and selection methods.
 *  All the displaying is done with OpenGl.
 */
class GlGraph {
private:
  GlGraphStrategy *strategy;

  //we will never have to get a copy of that GlGraph.
  //GlGraph objects should be only passed by reference or pointer
  GlGraph(const GlGraph &);
  
public:
  GlGraph(GlGraphStrategy &strategy);
  GlGraph(GlGraphStrategy &strategy, const GlGraph &);
  ~GlGraph();

  //============================================
  DataSet getParameters() const;
  void setParameters(const DataSet);
  
  /**
   *  Initialize scale factors according to layout bounding box
   *  Initialize the camera position.
   */
  void init();
  
  /**
   * sets the glyph table
   * \param v hash of integer to symbolic glyph name (a string)
   \sa GlyphTableString
   */
  void setGlyphTable(const GlyphTableString &v);
  
  /**
   * sets the glyph table
   * \param v hash of integer to Glyph object
   * \sa GlyphTableGlyph
   */
  void setGlyphTable(const GlyphTableGlyph &v);
  GlyphTableString getGlyphTable() const;
  
  /// Rebuild the OpenGl Display lists
  void updateList();
  
  /**
   * Initialize OpenGl renderer parameters
   * GL_LINE_SMOOTH,GL_BLEND,GL_CULL_FACE,GL_DEPTH_TEST
   */
  void initializeGL();
  
  /**
   *  Set ligths, clipping plane, camera position, 
   *  and start the incremental rendering of the graph.
   *  if incremental rendering is enable, else draw the
   *  entire graph.
   */
  void paintGL();
  /**
   * Change the dimension of the drawing (screen size), and enable redrawing.
   * 
   */
  void resizeGL(const int width, const int height);
  
  /**
   * Draw a part of the graph, the number of elements displayed
   * is automatically computed by analysing the time used to draw
   * the previous slice of data.
   */
  void drawGraph();

  /**
   * Repaint the entire graph.
   */
  void redraw();
  
  /**
   * copy current values into given parameters (names are corresponding)
   */
  void getWinParameters(int *winX, int *winY, int *winW, int *winH, GLint *vp[4]);

  //==============================================================================
  // Data centralization in order to optimize memory using
  //==============================================================================
  /** activate Texture with name filename.
   * \param filename the image to be textured
   * \return true on success, false otherwise (format not supported, file not found,
             not enough memory, ...)
   */
  bool activateTexture(const std::string &filename);
  /*
   * Desactivate texturing
   */
  void desactivateTexture();

  //==============================================================================
  // Rendering parameters
  //==============================================================================

  /** return a Camera object which desribes the 
      camera used for he rendering
  */
  Camera getCamera() const;

  /** set the camera parameter for the rendering
   */
  void setCamera(const Camera &cam);

  /** return the background color
   * \sa setBackgroundColor
   */
  Color getBackgroundColor() const;
  /** set the background color, RGB colorspace.
   * \sa getBackgroundColor
   */
  void setBackgroundColor(const int r,const int g, const int b);
 
  /** activate or deactivate displaying of arrows along the graph's edges.
   * \sa isViewArrow
   */
  void setViewArrow(const bool state);
  /** return true if arrows drawing is activated else false.
   * \sa setViewArrow
   */
  bool isViewArrow()const;
  
  /** activate or deactivate displaying of labels on nodes and edges.
   *  \sa isViewLabel
   */
  void setViewLabel(const bool state);
  /** return true if label drawing is on else false.
   *  \sa setViewLabel
   */
  bool isViewLabel() const;
  
  /** activate or deactivate interpolation of nodes colors along edge
   *  \sa isEdgeColorInterpolate
   */
  void setEdgeColorInterpolate(const bool state);
  /** return true if color interpolation is activated
   * \sa setEdgeColorInterpolate
   */
  bool isEdgeColorInterpolate() const;
  
  /** return the type of fonts used in the renderer(0=3D,1=bitmap,2=texture)
   *  \sa setFontsType
   */
  unsigned int fontsType() const;
  /** set fonts type used in the renderer (0=3D,1=bitmap,2=texture)
   * \sa fontsType
   */
  void setFontsType(unsigned int type);
  
  /** if true activate the Orthogonal projection, else perspective is used.
   * \sa isViewOrtho
   */
  void setViewOrtho(const bool state);
  /** return true if orthogonal projection is used.
   *  \sa isViewOrtho
   */
  bool isViewOrtho() const;
  
  /** if true glgraph use the incremental rendering else display all elements in one pass
   *  \sa isIncrementalRendering
   */
  void setIncrementalRendering(const bool state);

  /** return the incremental rendering state
   *  \sa setIncrementalRendering
   */
  bool isIncrementalRendering() const;

  ///
  void setViewKey(const bool state);
  ///
  bool isViewKey() const;
  
  /** activate Strahler drawing mode. The graph is then drawn in Strahler order
   *  \sa isViewStrahler
   */
  void setViewStrahler(const bool state);
  /** return Strahler mode state
   *  \sa isViewStrahler
   */
  bool isViewStrahler() const;
  
  /** use 3D displaying of edges instead of 2D lines on ALL edges
   * \sa isEdged3D
   */
  void setEdge3D(const bool state);
  /** return true is 3D drawing of edges is activated
   * \sa setEdge3D
   */
  bool isEdged3D() const;

  /** get current translation applied to the scene
   * \return current tranlation of the scene
   * \sa setSceneTranslation, setTranslation
   */
  Coord getSceneTranslation() const;
  /** set translation to apply to the scene
   *  \sa getSceneTranslation, setTranslation
   */
  void setSceneTranslation(const Coord &translation);
  /** get current rotation applied to the scene
   *  \return current rotation of the scene
   *  \sa setSceneRotation, setRotation
   */  
  Coord getSceneRotation() const;
  /** set rotation to apply to the scene
   *  \sa getSceneRotation, setRotation
   */
  void setSceneRotation(const Coord &rotation);


  //=======================================================================
  // Navigation functions: GlGraphNavigate.cpp
  //=======================================================================


  /** set translation to apply to the scene
   * \sa getSceneTranslation, setSceneTranslation
   */
  void translateCamera(const int x, const int y, const int z);
  
  /** set rotation to apply to the scene
   *  \sa getSceneRotation, setSceneRotation
   */
  void rotateScene(const int rotx, const int roty, const int rotz);

  /** zoom on the center of the screen
   *  \param step positive: zoom in, negative: zoom out
   */
  void zoom(const int step);
  /** \brief zoom to  screen coordinates
   *  a translation is performed while zooming. The farther the point (x,y) is
   *  from the center of the screen, the bigger is the translation. The direction
   *  of the translation is reversed when switching from zoom in to zoom out.
   *  \sa zoom
   */
  void zoomXY(const int step, const int x, const int y);
 
 
  //=======================================================================
  //Selection of elements on the screen
  //=======================================================================
  /** \brief select nodes and edges in a region of the screen
   *
   *  select all nodes and edges lying in the area of the screen of given width and height,
   *  and with its upper-left corner at (x,y)
   *  \param sNode filled by the method with the nodes found in the region
   *  \param sEdge filled by the method with the edges found in the region
   */
  void doSelect(const int x, const int y, const int width, const int height, std::set<node> &sNode, std::set<edge> &sEdge);
  
  /** \brief select a node or edge at a point
   *
   *  select either a node or edge at point (x,y)
   *  \param type tells what has been found: NODE, EDGE
   *  \return true if something has been found, false otherwise
   */
  bool doSelect(const int x, const int y, Tulip::AtomType &type, node &, edge &);
  
  /** \brief select nodes in a region of the screen
   *
   *  select all nodes lying in the area of the screen of given width and height,
   *  and with its upper-left corner at (x,y)
   *  \param sNode filled by the method with the nodes found in the region
   *  \return true if at least one node has been found
   *  \sa doSelect(const int, const int, const int, const int, set<node>&, set<edge>&),
          doEdgeSelect(const int, const int, const int, const int, set<edge>&);
   */
  bool doNodeSelect(const int x, const int y, const int w, const int h, std::set<node> &sNode);
  
  /** \brief select nodes in a region of the screen
   *
   *  select all edges lying in the area of the screen of given width and height,
   *  and with its upper-left corner at (x,y)
   *  \param sEdge filled by the method with the edges found in the region
   *  \return true if at least one edge has been found
   *  \sa doSelect(const int, const int, const int, const int, set<node>&, set<edge>&),
          doNodeSelect(const int, const int, const int, const int, set<node>&);      
   */
  bool doEdgeSelect(const int x, const int y, const int w, const int h, std::set<edge> &sEdge);
  
  /** select nodes at a given point, ordered from the closest to the farthest
   *  \param selected the nodes found
   *  \return true if at least one node has been found
   */
  bool doNodeSelect(const int x, const int y, std::vector<node> &selected);
  
  /** select edges at a given point, ordered from the closest to the farthest
   *  \param selected the edges found
   *  \return true if at least one edge has been found
   */
  bool doEdgeSelect(const int x, const int y, std::vector<edge> &selected);

  //=======================================================================
  //Tools
  //=======================================================================
  //FIXME: mode outputEPS outside of GlGraph
  void outputEPS(int size, int doSort, const char *filename);
  
  void changeCoord(double& x, double& y, double& z);
  
  /** wait drawing of the graph and take a snapshot
   *  \return an array of dimension width*height*3 char (8bits per color RGB).
   *          The pointer has to be freed after (with free, not delete)
   **/
  unsigned char *getImage(int &width, int &height);

  //=======================================
  //contôle de la fenêtre openGl
  //=======================================
  void goodScale();

  //=========================================
  //Operation on graph's structure
  //=========================================
  //  void del(const GLint x, const GLint y);

  /** Change current graph being viewed
   *  \param a pointer to the graph to view
   *  \sa getSuperGraph
   */
  void setSuperGraph(SuperGraph *supergraph);
  
  /** get current graph being viewed
   *  \return a pointer to the current graph
   *  \sa setSuperGraph
   */
  SuperGraph* getSuperGraph() const;

  //  void delSelection();

  SuperGraph     *_superGraph;
  static TemplateFactory<GlyphFactory,Glyph,GlyphContext *> glyphFactory;

protected:
  void drawEdge(const Coord &startNode, const Coord &finalNode,
                const Coord &startPoint,const LineType::RealType &bends, const Coord &endPoint,
		const Color &startColor, const Color &endColor, const Size &size, int shape, const bool selected=false);
  void drawEdge(edge ite);
  unsigned int drawEdges(unsigned int , Iterator<edge> *);
  unsigned int drawEdges(unsigned int , std::list<edge>::iterator &) _DEPRECATED;
  void drawNode(node itv,unsigned int depth);
  unsigned int drawNodes(unsigned int , Iterator<node> *);
  unsigned int drawNodes(unsigned int , std::list<node>::iterator &) _DEPRECATED;
  void drawFanNode(node n) ;
  unsigned int drawFanNodes(unsigned int , Iterator<node> *);
  void drawMetaNode(node n,unsigned int depth);
  void DrawBitmapString(void *, const char *);
  void DrawStrokeString(void *font, const char *string);
  void makeArrowMatrix(GLfloat *, const Coord , const Coord , GLfloat, GLfloat, GLfloat);
  void initProxies();


  
  ///
  void initDoSelect(const GLint x, const GLint y, GLint w, GLint h);
  ///
  void endSelect();
  ///
  void makeNodeSelect(const int);
  ///
  void makeEdgeSelect(const int);
  ///
  ///void addSelection(const GLint x, const GLint y);
  ///
  ///  void initAndSelect(const GLint x, const GLint y);
  ///
  /// void selectItem(const GLint x, const GLint y);

  void initProjection(bool reset=true);
  void initModelView();
  void initLights();
  void initGlParameter();



  //  void buildColor();
  void buildOrderedList();

  GLuint makeSelectionObject();
  GLuint makeCubeWireObject();
  GLuint makeConeObject();
private:
  PropertyProxyContainer *graphProperties;
  Color  backgroundColor;
  bool _viewArrow, _viewLabel, _viewKey, _viewStrahler;
  bool _viewAutoScale, _incrementalRendering, _edgeColorInterpolate, _edge3D;
  unsigned int _viewOrtho;
  unsigned int _FontsType;

  
  int winH;
  int winW;

  STL_EXT_NS::hash_map<int, Glyph *, STL_EXT_NS::hash<int> >  glyphTable;


  STL_EXT_NS::hash_map<std::string, GLuint > texturesMap; //FIXME: make it static?
  
  //display list holding drawing of metaGraph - currently a wire cube
  GLuint	 metaGraphDL;
  //display list holding selection of nodes - currently a transparent pink sphere
  GLuint	 selectionDL;


  //FIXME: made public to allow glyphs to access those proxy
public:
  ColorsProxy    *elementColor;
  SizesProxy     *elementSize;
  IntProxy       *elementShape;
  SelectionProxy *elementSelected;
  StringProxy    *elementLabel;
  LayoutProxy    *elementLayout;
  MetaGraphProxy *elementMetaGraph;
  StringProxy	 *elementTexture;

private:
  //a factoriser
  GLint vp[4]; //->getGetIntegerv(GL_VIEWPORT)
  GLuint (*selectBuf)[4];
  GLuint objectGraph,objectCube,objectCone;

  //Scene information
  Coord sceneTranslation; // Scene translation
  Coord sceneRotation; // Rotation of the scene



  //Camera information
  Coord cameraEyes,cameraCenter,cameraUp;
  double cameraZoomFactor;
  GLfloat distCam;   //distance for camera translation

  int winX,winY; 
  std::map<double,double> colorMap;
  double colorMapMin,colorMapMax;

  //+++
  Iterator<node>* drawNodesIterator;
  Iterator<edge>* drawEdgesIterator;
  std::list<node>::iterator drawNodesIteratorStl;
  std::list<edge>::iterator drawEdgesIteratorStl;
  std::list<node> orderedNode;
  std::list<edge> orderedEdge;
  double nodeSizex,nodeSizey;

  //Incremental rendering parameters
  int maxNumberOfNodeToDraw;
  int maxNumberOfEdgeToDraw;

  //Occlusion testing
  ListRectangleInt2D occlusionTest;
  char bitmapMask[500000];
};


template<typename T> struct stlListIterator:public Iterator<T> {
  typename std::list<T>::iterator &it,itEnd;
  stlListIterator(typename std::list<T>::iterator &startIt, typename std::list<T>::iterator endIt):it(startIt),itEnd(endIt){}
  T next(){T tmp=*it;++it;return tmp;}
  bool hasNext(){return (itEnd!=it);}
};

#endif // Tulip_GLGRAPH_H
