
#include <GL/glut.h>

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

#include <d4tessdraw.h>
#include <tetrahedrondraw.h>



typedef point3<double> pt3;
typedef point3<double> const pt3c;
typedef point4<double> pt4;
typedef point4<double> const pt4c;

typedef unsigned int uint;

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

  gobjContainer & g(*gobjContainer::global);

  uint i = g.v.size();

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

  uint k = g.v.size();

  for (uint j=i; j<k; ++j)
    gdynamic.push_back( g.v[j] );

  g.v.erase(g.v.begin()+i,g.v.end());

  assert(g.v.size()==i);
}


d4tessdraw::d4tessdraw
(
  d4tess & _tess
)
  : tess(_tess), gdynamic(true)
{
  gobjContainer::global->push_back(&gdynamic);

  graphicsDeffered.push_back(new writepointsobj(tess));
  graphicsDeffered.push_back(new writesimplicesobj(tess));
  graphicsDeffered.push_back(new writewindingobj(tess));
  graphicsDeffered.push_back(new writegridobj(tess));
  graphicsDeffered.push_back(new writebaseobj(tess));
  graphicsDeffered.push_back(new writesurfaceobj(tess));


  graphicsImmediate.push_back(new writecpobj(tess));

  meshupdate();
}

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

  graphicsImmediate.draw();

  glPopAttrib();
  glPopAttrib();
}

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

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

  x.push_back( new gobjglDisable(GL_LIGHTING) );

  x.push_back( new gobjglColor3ub(col) );
    
  displaypoints<pt4> dp(x,tess.pt);

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


void writecpobj::draw() const
{

  pt4 P0,P1,P2,P3;
  tess.getpoints(P0,P1,P2,P3);

  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);

    glBegin(GL_TRIANGLES);

    // Inner Triangle

    glColor3ub(128,0,128);
    glVertex3f(P1.x,P1.y,P1.z);
    glVertex3f(P0.x,P0.y,P0.z);
    glColor3ub(0,255,0);
    glVertex3f(P2.x,P2.y,P2.z);


    // Outer Triangle

    glColor3ub(0,255,0);
    glVertex3f(P2.x,P2.y,P2.z);
    glColor3ub(255,0,0);
    glVertex3f(P0.x,P0.y,P0.z);
    glVertex3f(P1.x,P1.y,P1.z);

    glEnd();

    glColor3ub(255,215,0);

    double const radius=0.02;

    glPushMatrix();
    glTranslatef(P0.x,P0.y,P0.z);
    glutSolidSphere(radius,10,10);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(P1.x,P1.y,P1.z);
    glutSolidSphere(radius,10,10);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(P2.x,P2.y,P2.z);
    glutSolidSphere(radius,10,10);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(P3.x,P3.y,P3.z);
    glutSolidSphere(radius,10,10);
    glPopMatrix();

// TODO - transparent sides for better visualization of cp

// I am unable to get this working. 
//
//  What I wanted was transpent sides of the cp other than the base. 
//  The color of the inner triangle should come through.

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

//    float const blendratio = 0.05;
    



    
//    glBegin(GL_TRIANGLES);

//    glVertex4f(P0.x,P0.y,P0.z,blendratio);
//    glVertex4f(P2.x,P2.y,P2.z,blendratio);
//    glVertex4f(P3.x,P3.y,P3.z,blendratio);

/*
    glVertex3f(P0.x,P0.y,P0.z);
    glVertex3f(P2.x,P2.y,P2.z);
    glVertex3f(P3.x,P3.y,P3.z);

    glVertex3f(P2.x,P2.y,P2.z);
    glVertex3f(P1.x,P1.y,P1.z);
    glVertex3f(P3.x,P3.y,P3.z);

    glVertex3f(P0.x,P0.y,P0.z);
    glVertex3f(P3.x,P3.y,P3.z);
    glVertex3f(P1.x,P1.y,P1.z);
*/


/*
    glVertex4f(P0.x,P0.y,P0.z,blendratio);
    glVertex4f(P1.x,P1.y,P1.z,blendratio);
    glVertex4f(P2.x,P2.y,P2.z,blendratio);

    glVertex4f(P0.x,P2.y,P2.z,blendratio);
    glVertex4f(P3.x,P3.y,P3.z,blendratio);
    glVertex4f(P0.x,P0.y,P0.z,blendratio);

    glVertex4f(P1.x,P1.y,P1.z,blendratio);
    glVertex4f(P0.x,P0.y,P0.z,blendratio);
    glVertex4f(P3.x,P3.y,P3.z,blendratio);

    glVertex4f(P2.x,P2.y,P2.z,blendratio);
    glVertex4f(P1.x,P1.y,P1.z,blendratio);
    glVertex4f(P3.x,P3.y,P3.z,blendratio);
*/

//    glEnd();


    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);



  glPopAttrib();
  glPopAttrib();

}

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

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

  x.push_back( new gobjglEnable(GL_LIGHTING) );

  x.push_back( new gobjglColor3ub(surfacecolor) );

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

    if (t.isnull())
      continue;

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

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

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

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

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


  x.push_back( new gobjglDisable(GL_LIGHTING) );


  x.push_back( new gobjglColor3ub(gridcolor) );

  vector<pt4> const & pt(tess.pt);
  vector<d4tri> const & vi(tess.vi);

  x.push_back( new gobjglBegin(GL_LINES) );

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

    if (t.isnull())
      continue;

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

    x.push_back( new gobjglVertex3f(P0.x,P0.y,P0.z) );
    x.push_back( new gobjglVertex3f(P1.x,P1.y,P1.z) );
    x.push_back( new gobjglVertex3f(P0.x,P0.y,P0.z) );
    x.push_back( new gobjglVertex3f(P2.x,P2.y,P2.z) );
    x.push_back( new gobjglVertex3f(P0.x,P0.y,P0.z) );
    x.push_back( new gobjglVertex3f(P3.x,P3.y,P3.z) );
    x.push_back( new gobjglVertex3f(P1.x,P1.y,P1.z) );
    x.push_back( new gobjglVertex3f(P2.x,P2.y,P2.z) );
    x.push_back( new gobjglVertex3f(P1.x,P1.y,P1.z) );
    x.push_back( new gobjglVertex3f(P3.x,P3.y,P3.z) );
    x.push_back( new gobjglVertex3f(P2.x,P2.y,P2.z) );
    x.push_back( new gobjglVertex3f(P3.x,P3.y,P3.z) );

  }

  x.push_back( new gobjglEnd() );

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




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

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

  x.push_back( new gobjglDisable(GL_LIGHTING) );

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

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

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

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

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

  displaypoints<pt4> dp(x,v,false);

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


void writewindingobj::draw() const
{
  uintc imax = tess.vi.size();
  for (uint i=1; i<imax; ++i)
  {
    d4tri const & t(tess.vi[i]);

    if (t.isnull())
      continue;

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

    tetrahedrondraw<double> td(P0,P1,P2,P3);
    td.displaywinding();
  }
}

void writebaseobj::draw() const
{
  //glPushAttrib(GL_CURRENT_BIT);
  //glPushAttrib(GL_LIGHTING_BIT);

  //glBegin(GL_LINES);

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

    if (t.isnull())
      continue;

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

    tetrahedrondraw<double> td(P0,P1,P2,P3);
    td.displaybase();
  }

  //glEnd();

  //glPopAttrib();
  //glPopAttrib();
}



