#ifndef TESSD2DRAW02_H
#define TESSD2DRAW02_H

#include <gobj.h>
#include <graphmisc.h>
#include <pointsdisplay.h>
#include <random.h>
#include <triangle.h>

/*!
\brief Number the points in the tessellation. 

Count from 1 onwards.
*/
template< typename TESS, typename PT >
class tessD2draw02points : public gobjContainer
{
  TESS & tess;
public:

  /** Change the text color. */
  gobjglColor3ub col;

  /** Toggle the display. */
  bool & isdrawn;

  tessD2draw02points(TESS & tess_, bool & isdrawn_)
    : tess(tess_), col(255,255,0), isdrawn(isdrawn_) {}

  /** Draw to the global graphics stream. */
  void draw();
};

/*!
\brief Draw the triangle edges in one color. 
*/
template< typename TESS, typename PT, typename INDX >
class tessD2draw02mesh : public gobj
{
  TESS & tess;
public:

  /** Change the mesh color. */
  gobjglColor3f col;

  /** Blue mesh. */
  tessD2draw02mesh(TESS & tess_)
    : tess(tess_), col(0.0,0.0,1.0) {}

  /** Draw to the global graphics stream. */
  void draw();

private:
  template< typename TR, typename PTS >
  void drawtriangle(TR const & t, PTS const & pts);
};

/*!
\brief Draw the simplex index. 
*/
template< typename TESS, typename PT, typename INDX >
class tessD2draw02simplexindex : public gobj
{
  TESS & tess;
public:

  /** Change the text color. */
  gobjglColor3f col;

  /** Toggle the display. */
  bool & isdrawn;

  /** Red indexes. */
  tessD2draw02simplexindex(TESS & tess_, bool & isdrawn_)
    : tess(tess_), col(1.0,0.0,0.0), isdrawn(isdrawn_) {}

  /** Draw to the global graphics stream. */
  void draw();

private:
  template< typename TR, typename PTS >
  void processtriangle
  (
    point2<double> & q,
    TR const & t, 
    PTS const & pts
  ) const;
};

/*!
\brief Draw the triangles in random colors. 

Assumes the tessellation has anti-clockwise winding.
*/
template< typename TESS, typename PT, typename INDX >
class tessD2draw02multicolor : public gobj
{
  TESS & tess;
public:

  /** Toggle the display. */
  bool & isdrawn;

  /** The alpha component. */
  double blend;

  /** Constructor. */
  tessD2draw02multicolor(TESS & tess_, bool & isdrawn_)
    : tess(tess_), isdrawn(isdrawn_), blend(0.5) {}

  /** Draw to the global graphics stream. */
  void draw();

private:
  template< typename TR, typename PTS >
  void drawtriangle(TR const & t, PTS const & pts);

  random11<double> r;
};

/*!
\brief Circle through three points of triangle.
*/
template< typename TESS, typename PT, typename INDX >
class tessD2draw02circles: public gobj
{
  TESS & tess;
public:

  /** Toggle the display. */
  bool & isdrawn;

  /** The alpha component. */
  double blend;

  /** Constructor. */
  tessD2draw02circles(TESS & tess_, bool & isdrawn_)
    : tess(tess_), isdrawn(isdrawn_), blend(0.5) {}

  /** Draw to the global graphics stream. */
  void draw();

private:
  typedef point2<double> pt2;

  template< typename PTS, typename S >
  void get
  (   
    triangle<pt2,double> & t,
    PTS const & pts,
    S & s
  ) const
    { t.constructUnordered( pts[s.pi[0]], pts[s.pi[1]], pts[s.pi[2]] ); }

};


//---------------------------------------------------------
// Implementation


template< typename TESS, typename PT, typename INDX >
void tessD2draw02circles<TESS,PT,INDX>::draw()
{
  gobjContainer & y = * new gobjContainer(true);

  y.push( new gobjglPushAttrib(GL_CURRENT_BIT));
  y.push( new gobjglPushAttrib(GL_LIGHTING_BIT));

  y.push(new gobjglDisable(GL_LIGHTING));
  y.push( new gobjglEnable(GL_BLEND));
  y.push( new gobjglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));

  gobjMyCircle * cir = new gobjMyCircle();
  y.push(cir);

  triangle<pt2,double> t;
  pt2 p0;
  double radius;
  typedef point3<double> pt3;

  random11<double> r;

  INDX const imax = tess.vi.size();
  for (INDX i=1; i<imax; ++i)
  {
    if (tess.vi[i].isnull())
      continue;

    y.push(new gobjglColor4f(r(),r(),r(),blend));

    get(t,tess.pts,tess.vi[i]);

    t.outercircle(radius,p0);

//cout << SHOW(radius) << " " << SHOW(p0) << endl;

    y.push( new gobjMyCircleDraw(radius, pt3(p0), *cir) );
  }

  //y.push( new gobjglEnd());

  y.push( new gobjglPopAttrib());
  y.push( new gobjglPopAttrib());

//cout << SHOW(y.vg.size()) << endl;
//cout << SHOW(isdrawn) << endl;

  gobjSwitch<bool&> * s = 
    new gobjSwitch<bool&>(&y,isdrawn);

  gobjpush(s);
}


template< typename TESS, typename PT, typename INDX >
template< typename TR, typename PTS >
void tessD2draw02multicolor<TESS,PT,INDX>::drawtriangle
(
  TR const & t,
  PTS const & pts
)
{
  if (t.isnull())
    return;

  PT const & P0(pts[t.pi[0]]); 
  PT const & P1(pts[t.pi[1]]); 
  PT const & P2(pts[t.pi[2]]); 

  gobjpush(new gobjglColor4f(r(),r(),r(),blend));

  gobjpush(new gobjglVertex2f(P0.x,P0.y));
  gobjpush(new gobjglVertex2f(P1.x,P1.y));
  gobjpush(new gobjglVertex2f(P2.x,P2.y));
}

template< typename TESS, typename PT, typename INDX >
void tessD2draw02multicolor<TESS,PT,INDX>::draw()
{
  gobjContainer * preserve = gobjContainer::global;
  
  gobjContainer & y = * new gobjContainer(true);
  y.set();

  y.push( new gobjglPushAttrib(GL_CURRENT_BIT));
  y.push( new gobjglPushAttrib(GL_LIGHTING_BIT));

  y.push(new gobjglDisable(GL_LIGHTING));
  y.push( new gobjglEnable(GL_BLEND));
  y.push( new gobjglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));

  y.push( new gobjglBegin(GL_TRIANGLES));

  INDX const imax = tess.vi.size();
  for (INDX i=1; i<imax; ++i)
    drawtriangle(tess.vi[i],tess.pts);

  y.push( new gobjglEnd());

  y.push( new gobjglPopAttrib());
  y.push( new gobjglPopAttrib());

  gobjSwitch<bool&> * s = 
    new gobjSwitch<bool&>(&y,isdrawn);

  // Restore
  gobjContainer::global = preserve;
  
  gobjpush(s);
}



template< typename TESS, typename PT, typename INDX >
template< typename TR, typename PTS >
void tessD2draw02mesh<TESS,PT,INDX>::drawtriangle
(
  TR const & t,
  PTS const & pts
)
{
  if (t.isnull())
    return;

  PT const & P0(pts[t.pi[0]]); 
  PT const & P1(pts[t.pi[1]]); 
  PT const & P2(pts[t.pi[2]]); 

  glVertex2f(P0.x,P0.y);
  glVertex2f(P1.x,P1.y);
  glVertex2f(P0.x,P0.y);
  glVertex2f(P2.x,P2.y);
  glVertex2f(P1.x,P1.y);
  glVertex2f(P2.x,P2.y);
}

template< typename TESS, typename PT, typename INDX >
void tessD2draw02mesh<TESS,PT,INDX>::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  glDisable(GL_LIGHTING);

  col.draw();

  glBegin(GL_LINES);

  INDX const imax = tess.vi.size();
  for (INDX i=1; i<imax; ++i)
    drawtriangle(tess.vi[i],tess.pts);

  glEnd();

  glPopAttrib();
  glPopAttrib();
}

template< typename TESS, typename PT >
void tessD2draw02points<TESS,PT>::draw()
{
  gobjContainer & y = * new gobjContainer(true);

  y.push( new gobjglPushAttrib(GL_CURRENT_BIT) );
  y.push( new gobjglPushAttrib(GL_LIGHTING_BIT) );

  y.push( new gobjglDisable(GL_LIGHTING) );

  y.push( new gobjglColor3ub(col) );
    
  pointsdisplay2D< PT > dp(y,tess.pts);

  y.push( new gobjglPopAttrib() );
  y.push( new gobjglPopAttrib() );

  gobjSwitch<bool&> * s = 
    new gobjSwitch<bool&>(&y,isdrawn);

  gobjpush(s);
}

template< typename TESS, typename PT, typename INDX >
void tessD2draw02simplexindex<TESS,PT,INDX>::draw()
{
  gobjContainer & y = * new gobjContainer(true);

  y.push( new gobjglPushAttrib(GL_CURRENT_BIT) );
  y.push( new gobjglPushAttrib(GL_LIGHTING_BIT) );

  y.push( new gobjglDisable(GL_LIGHTING) );

  y.push( new gobjglColor3f(col) );

  vector< point2<double> > pts;
  INDX sz=tess.vi.size();
  pts.resize(sz);
  for (INDX i=1; i<sz; ++i)
    processtriangle(pts[i],tess.vi[i],tess.pts);
    
  pointsdisplay2D< PT > dp(y,pts);

  y.push( new gobjglPopAttrib() );
  y.push( new gobjglPopAttrib() );

  gobjSwitch<bool&> * s = 
    new gobjSwitch<bool&>(&y,isdrawn);

  gobjpush(s);
}

template< typename TESS, typename PT, typename INDX >
template< typename TR, typename PTS >
void tessD2draw02simplexindex<TESS,PT,INDX>::processtriangle
(
  point2<double> & q,
  TR const & t,
  PTS const & pts
) const
{
  typedef point2<double> pt2;
  if (t.isnull())
  {
    q = pt2();
    return;
  }

  PT const & P0(pts[t.pi[0]]); 
  PT const & P1(pts[t.pi[1]]); 
  PT const & P2(pts[t.pi[2]]); 

  q.x = (P0.x+P1.x+P2.x)*((double)1.0)/((double)3.0);
  q.y = (P0.y+P1.y+P2.y)*((double)1.0)/((double)3.0);
}


#endif


