
#include <cassert>
#include <cstdlib>

#include <GL/glut.h>
#include <GL/gl.h>

using namespace std;

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

#include <d3tessdraw.h>

#define SHOW(x) #x << '=' << (x)

typedef unsigned int uint;
typedef unsigned int const uintc;
typedef point3<double> pt3;
typedef point2<double> pt2;


void writesurfaceobj::draw()
{
  surfacecolor.draw();
    
  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    simplexD2linked t(tess.vi[i]);

    if (t.isnull())
      continue;

    pt3c & P0(tess.pt[t.pi[0]]); 
    pt3c & P1(tess.pt[t.pi[1]]); 
    pt3c & P2(tess.pt[t.pi[2]]); 

    surface.eval(P0,P1,P2);
  }
}


void writepointsobj::draw()
{
  gobjContainer & x = * gobjContainer::global;

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

  x.push( new gobjglDisable(GL_LIGHTING) );

  x.push( new gobjglColor3ub(col) );
    
  //displaypoints<pt3> dp(x,tess.pt);
  pointsdisplay2D<pt3> dp(x,tess.pt);

  x.push( new gobjglPopAttrib() );
  x.push( new gobjglPopAttrib() );
}



void writewindingobj::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  glBegin(GL_LINES);

  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    simplexD2linked t(tess.vi[i]);

    if (t.isnull())
      continue;

    pt3c & P0(tess.pt[t.pi[0]]); 
    pt3c & P1(tess.pt[t.pi[1]]); 

    glColor3ub(255,0,0);
    glVertex2f(P0.x,P0.y);
    glColor3ub(0,255,0);
    glVertex2f(P1.x,P1.y);   

  }

  glEnd();

  glPopAttrib();
  glPopAttrib();
}



void writegridobj::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  gridcolor.draw();

  vector<pt3> const & pt(tess.pt);
  vector<simplexD2linked> const & vi(tess.vi);

  glBegin(GL_LINES);

  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    simplexD2linked const & t(vi[i]);

    if (t.isnull())
      continue;

    pt3c & P0(pt[t.pi[0]]); 
    pt3c & P1(pt[t.pi[1]]); 
    pt3c & P2(pt[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);

  }

  glEnd();

  glPopAttrib();
  glPopAttrib();
}


void writesimplicesobj::draw()
{
  gobjContainer & x = * gobjContainer::global;

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

  x.push( new gobjglDisable(GL_LIGHTING) );

  x.push( new gobjglColor3f(1.0,0.0,0.0) );
    
  uintc n = tess.vi.size();
  vector< pt3 > v;
  uint k;
  pt3 w;

  // The first tet is void.
  v.push_back(w);  

  for (uint i=1; i<n; ++i)
  {
    simplexD2linked const & t(tess.vi[i]);
    w = pt3();

    for (k=0; k<3; ++k)
      w += tess.pt[ t.pi[k] ];

    w *= 0.3333333333333;
    v.push_back(w);
  }

  //displaypoints<pt3> dp(x,v,false);
  pointsdisplay2D<pt3> dp(x,v,false);

  x.push( new gobjglPopAttrib() );
  x.push( new gobjglPopAttrib() );
}



void writecpobj::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  simplexD2linked const & cp(tess.cpsimplex());
  virtualtriangle const & vs(tess.vs);

  pt3 P0(tess.pt[ cp.pi[vs.v[0]] ]);
  pt3 P1(tess.pt[ cp.pi[vs.v[1]] ]);
  pt3 P2(tess.pt[ cp.pi[vs.v[2]] ]);

  float const blendratio = 0.85;

  glBegin(GL_TRIANGLES);


/*
    glColor3ub(255,0,0);
    glVertex2f(P0.x,P0.y);
    glVertex2f(P1.x,P1.y);
    glColor3ub(0,255,0);
    glVertex2f(P2.x,P2.y);
*/

    glColor4f(1.0,0.0,0.0,blendratio);
    glVertex2f(P0.x,P0.y);
    glVertex2f(P1.x,P1.y);
    glColor4f(0.0,1.0,0.0,blendratio);
    glVertex2f(P2.x,P2.y);

  glColor4f(1.0,156.0/255.0,0.0,blendratio);
//  glColor3ub(255,165,0);

  for (uint i=0; i<3; ++i)
  {
    if (cp.ni[i]==0)
      continue;

    simplexD2linked const & nb(tess.vi[cp.ni[i]]);

    pt3 const & P0(tess.pt[ nb.pi[0] ]);
    pt3 const & P1(tess.pt[ nb.pi[1] ]);
    pt3 const & P2(tess.pt[ nb.pi[2] ]);

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

  glEnd();

  glPopAttrib();
  glPopAttrib();

}


void writecpvoronoiobj::onevertex() const
{
  uint cp0;
  virtualtriangle vs0;
  tess.cppreserve(cp0,vs0);

  vector<pt3> const & pt(tess.pt);

  simplexD2linked const & w(tess.cpsimplex());

  pt2 p0;
  triangle< pt2, double > t
  (
    pt[w.pi[0]],
    pt[w.pi[1]],
    pt[w.pi[2]]
  );
  //t.midpointcirclecenter(p0);
  t.circumcenter(p0);
  
  pt2 p1;
  pt2 p2;

  bool res=true;
  p1 = p0;
  for ( ; tess.moveleft() && res; )
  {
    simplexD2linked const & w(tess.cpsimplex());

    triangle< pt2, double > t
    (
      pt[w.pi[0]],
      pt[w.pi[1]],
      pt[w.pi[2]]
    );

    //t.midpointcirclecenter(p2);
    t.circumcenter(p2);

    glVertex2d(p1.x,p1.y);
    glVertex2d(p2.x,p2.y);

    p1 = p2;

    // Is the vertex a vertex within the mesh.
    if (tess.cp==cp0)
    {
      tess.cppreserveInverse(cp0,vs0);
      return;
    }
  }

  tess.cppreserveInverse(cp0,vs0);
  tess.vs.clockwise();

  p1 = p0;
  for ( ; tess.moveright() && res; )
  {
    simplexD2linked const & w(tess.cpsimplex());

    triangle< pt2, double > t
    (
      pt[w.pi[0]],
      pt[w.pi[1]],
      pt[w.pi[2]]
    );

    //t.midpointcirclecenter(p2);
    t.circumcenter(p2);

    glVertex2d(p1.x,p1.y);
    glVertex2d(p2.x,p2.y);

    p1 = p2;

    // There should be no possibility of an infinite loop.
  }

  tess.cppreserveInverse(cp0,vs0);
}



void writecpvoronoiobj::draw()
{

  //gresPushAttribPopAttrib att1(GL_CURRENT_BIT);
  myglPushAttrib att1(GL_CURRENT_BIT);
  //gresPushAttribPopAttrib att2(GL_LIGHTING_BIT);
  myglPushAttrib att2(GL_LIGHTING_BIT);

  glColor3ub(0,255,0);

  glDisable(GL_LIGHTING);

  //gresEnableDisable enabledisable(GL_LINE_STIPPLE);
  myglCapability cap1(GL_LINE_STIPPLE);

  glLineStipple(1,0x00FF);

 
  //gresBeginEnd begend(GL_LINES);
  myglMode m1(GL_LINES);

  onevertex();
  tess.vs.clockwise();
  onevertex();
  tess.vs.clockwise();
  onevertex();
  tess.vs.clockwise();

}


void writevoronoidiagramobj::draw()
{

  //  Construct a one to one mapping between a point
  //    and one of its bordering simplices.

  vector<pt3> const & pt(tess.pt);
  uint psz = pt.size();
  uint * fp = new unsigned int[psz];
  for (uint i=0; i<psz; ++i)
    fp[i] = 0;

  vector<simplexD2linked> const & vi(tess.vi);
  uint vsz = tess.vi.size();

  uint k;
  uint p1;
  for (uint i=1; i<vsz; ++i)
  {
    if (vi[i].isnull())
      continue;

    for (k=0; k<3; ++k)
    {
      p1 = vi[i].pi[k];
      if (fp[p1]!=0)
        continue;

      fp[p1] = i;
    }
  } 

  d3tesspreserve preservecp(tess);

  // Use the mapping to construct the voronoi regions by
  // rotating about the point.

  gobjContainer & x = * gobjContainer::global;

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

  x.push( new gobjglDisable(GL_LIGHTING) );

  x.push( new gobjglColor3f(0.0,1.0,0.0) );

  x.push( new gobjglEnable(GL_LINE_STIPPLE) );

  x.push( new gobjglLineStipple(1,0x00FF) );

  x.push( new gobjglBegin(GL_LINES) );

  // Draw voronoi polygons.
  for (uint i=1; i<psz; ++i)
  {
    if (fp[i]==0)
    {
      cout << "error: point " << i << " has no triangle" << endl;
      continue;
    }

    // Let the point i be in the bottom right hand corner of
    //   vs's orientation.
    tess.cp = fp[i];
    tess.vs.set( vi[ fp[i] ].piInverse(i));
    tess.vs.clockwise();
    onevertex();
  }

  x.push( new gobjglEnd() );

  x.push( new gobjglDisable(GL_LINE_STIPPLE) );

  x.push( new gobjglPopAttrib() );
  x.push( new gobjglPopAttrib() );

  delete[] fp;
}





// Does not preserve cp or orientation.
void writevoronoidiagramobj::onevertex() const
{
  uint cp0;
  virtualtriangle vs0;
  tess.cppreserve(cp0,vs0);

  vector<pt3> const & pt(tess.pt);

  simplexD2linked const & w(tess.cpsimplex());

  pt2 p0;
  triangle< pt2, double > t
  (
    pt[w.pi[0]],
    pt[w.pi[1]],
    pt[w.pi[2]]
  );
  //t.midpointcirclecenter(p0);
  t.circumcenter(p0);
  
  pt2 p1;
  pt2 p2;

  gobjContainer & x = * gobjContainer::global;

  bool res=true;
  p1 = p0;
  for ( ; tess.moveleft() && res; )
  {
    simplexD2linked const & w(tess.cpsimplex());

    triangle< pt2, double > t
    (
      pt[w.pi[0]],
      pt[w.pi[1]],
      pt[w.pi[2]]
    );

    //t.midpointcirclecenter(p2);
    t.circumcenter(p2);

    x.push( new gobjglVertex2d(p1.x,p1.y) );
    x.push( new gobjglVertex2d(p2.x,p2.y) );

    p1 = p2;

    // Is the vertex a vertex within the mesh.
    if (tess.cp==cp0)
      return;
  }

  tess.cppreserveInverse(cp0,vs0);
  tess.vs.clockwise();

  p1 = p0;
  for ( ; tess.moveright() && res; )
  {
    simplexD2linked const & w(tess.cpsimplex());

    triangle< pt2, double > t
    (
      pt[w.pi[0]],
      pt[w.pi[1]],
      pt[w.pi[2]]
    );

    //t.midpointcirclecenter(p2);
    t.circumcenter(p2);

    x.push( new gobjglVertex2d(p1.x,p1.y) );
    x.push( new gobjglVertex2d(p2.x,p2.y) );

    p1 = p2;

    // There should be no possibility of an infinite loop.
  }
}















void writecpcircleobj::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  glColor3ub(255,0,0);

  glDisable(GL_LIGHTING);

  simplexD2linked const & cp(tess.cpsimplex());

  pt3 const & P0(tess.pt[ cp.pi[0] ]);
  pt3 const & P1(tess.pt[ cp.pi[1] ]);
  pt3 const & P2(tess.pt[ cp.pi[2] ]);

  triangle< pt2, double > t(P0,P1,P2);
  pt2 p0;
  double radius;
  //t.outercircle(p0,radius);
  t.outercircle(radius,p0);
  gobjMyCircleDraw(radius, pt3(p0), cir).draw();

/*
// Attempt to look at neighbours but the situation 
//   becomes more complicated. 
  glColor3ub(255,165,0);

  for (uint i=0; i<3; ++i)
  {
    if (cp.ni[i]==0)
      continue;

    simplexD2linked const & nb(tess.vi[cp.ni[i]]);

    pt3 const & P0(tess.pt[ nb.pi[0] ]);
    pt3 const & P1(tess.pt[ nb.pi[1] ]);
    pt3 const & P2(tess.pt[ nb.pi[2] ]);

    triangle<double> t(P0,P1,P2);
    t.outercircle(p0,radius);
    gobjMyCircleDraw(radius, pt3(p0), cir).draw();
  }
*/

  glPopAttrib();
  glPopAttrib();
}

void writecirclesobj::draw()
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  pt2 p0;
  double radius;

  vector<pt3> const & pt(tess.pt);

  vector<simplexD2linked> const & vi(tess.vi);

  glColor3ub(255,0,0);

  uint imax = vi.size();
  for (uint i=0; i<imax; ++i)
  {
    if (vi[i].isnull())
      continue;

    // Difficult to see multicolored circles.
    //glColor3ub(rand() % 256,rand() % 256,rand() % 256);

    triangle< pt2, double > t
    (
      pt[vi[i].pi[0]],
      pt[vi[i].pi[1]],
      pt[vi[i].pi[2]]
    );
    //t.outercircle(p0,radius);
    t.outercircle(radius,p0);
    gobjMyCircleDraw(radius, pt3(p0), cir).draw();
  }

  glPopAttrib();
  glPopAttrib();
}


void writemulticolorobj::draw()
{
  gobjContainer & x = * gobjContainer::global;

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

  x.push( new gobjglDisable(GL_LIGHTING) );

  x.push( new gobjglBegin(GL_TRIANGLES) );

  float const blendratio = 0.5;

  random11<double> r;

  vector< pt3 > const & pt(tess.pt);
  vector<simplexD2linked> const & vi(tess.vi);

  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    simplexD2linked const & t(vi[i]);

    if (t.isnull())
      continue;

    x.push( new gobjglColor4f(r(),r(),r(),blendratio) );

    pt3c & P0(pt[t.pi[0]]); 
    pt3c & P1(pt[t.pi[1]]); 
    pt3c & P2(pt[t.pi[2]]); 

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

  }

  x.push( new gobjglEnd() );

  x.push( new gobjglPopAttrib() );
  x.push( new gobjglPopAttrib() );
}



/*
void d3tessdraw::writewinding() const
{
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  glBegin(GL_LINES);

  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    simplexD2linked t(tess.vi[i]);

    if (t.isnull())
      continue;

    pt3c & P0(tess.pt[t.pi[0]]); 
    pt3c & P1(tess.pt[t.pi[1]]); 

    glColor3ub(255,0,0);
    glVertex2f(P0.x,P0.y);
    glColor3ub(0,255,0);
    glVertex2f(P1.x,P1.y);   

  }

  glEnd();

  glPopAttrib();
  glPopAttrib();
}
*/






void d3tessdraw::meshupdate()
{
  gdynamic.nuke();

  assert(gobjContainer::global!=0);
  // Save the current global graphics stream.
  gobjContainer & g(*gobjContainer::global);

  // Make this the new graphics stream.
  gdynamic.set();

  // Writes all the graphics to gobjContainer::global
  graphicsDeffered.draw();  

  // Restore global graphics stream.
  g.set();
}


d3tessdraw::d3tessdraw( d3tess & tess_ )
  : tess(tess_), gdynamic(true), graphicsDeffered(true),
    graphicsImmediate(true)
{
  //gobjContainer::global->push_back(&gdynamic);

  graphicsDeffered.push(new writepointsobj(tess));
  graphicsDeffered.push(new writesimplicesobj(tess));
  graphicsDeffered.push(new writemulticolorobj(tess));
  graphicsDeffered.push(new writevoronoidiagramobj(tess));

  graphicsImmediate.push(new writewindingobj(tess));
  graphicsImmediate.push(new writegridobj(tess));
  graphicsImmediate.push(new writecpobj(tess));
  graphicsImmediate.push(new writesurfaceobj(tess));
  graphicsImmediate.push(new writecpcircleobj(tess));
  graphicsImmediate.push(new writecirclesobj(tess));
  graphicsImmediate.push(new writecpvoronoiobj(tess));

  glDisable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  meshupdate();
}


void d3tessdraw::draw()
{
  
  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

  graphicsImmediate.draw();
  gdynamic.draw();

  glPopAttrib();
  glPopAttrib();
}




