#ifndef TETRAHEDRONDISPLAY_H
#define TETRAHEDRONDISPLAY_H

#include <gobj.h>
#include <mathlib.h>
#include <tetrahedron.h>
#include <triangle.h>
#include <triangledisplay.h>


/*!
\brief Display the tetrahedron. 

The client can provide additional graphics
 by directly writing to gx when draw() is 
 called.

*/
template< typename TET >
class tetrahedrondisplay : public gobj
{
public:

  /** Unprocessed graphics. */
  gobjContainer & gx;
  /** The tetrahedron. */
  TET const & tr;

  /** Display the tetrahedron edges. */
  bool mesh;
  /** Display the tetrahedron's centroid. */
  bool centroid;

  bool test;

  /** Display the centers for each triangle face. */
  bool trianglecenterpoints;

  bool trianglecenters;

  /** The graphics output and tetrahedron. */
  tetrahedrondisplay
  ( 
    gobjContainer & _gx, 
    TET const & _tr
  ) : gx(_gx), tr(_tr), mesh(1), centroid(1), test(1),
      trianglecenterpoints(0), trianglecenters(0) {}

  /** Turn all options on. */
  void turnon();
  
  /** Write the graphics as unprocessed commands to gx. */
  void draw();
};


/*!
\brief Display the tetrahedron's edges. 
*/
template< typename TET >
class tetrahedrondisplaymesh : public gobj
{
  /** Unprocessed graphics. */
  gobjContainer & gx;
  /** The tetrahedron. */
  TET const & tr;
public:

  /** The graphics output and tetrahedron. */
  tetrahedrondisplaymesh
  ( 
    gobjContainer & _gx, 
    TET const & _tr
  ) : gx(_gx), tr(_tr) {}
  
  /** Write the graphics as gobj objects. */
  void draw();
};

/*!
\brief Display the average of the four points
       by constructing lines through the mid points.
*/
template< typename TET >
class tetrahedrondisplaycentroid : public gobj
{
  /** Unprocessed graphics. */
  gobjContainer & gx;
  /** The tetrahedron. */
  TET const & tr;
public:

  /** The graphics output and tetrahedron. */
  tetrahedrondisplaycentroid
  ( 
    gobjContainer & _gx, 
    TET const & _tr
  ) : gx(_gx), tr(_tr) {}
  
  /** Write the graphics as gobj objects. */
  void draw();
};

/*!
\brief Display test. 
*/
template< typename TET >
class tetrahedrondisplaytest : public gobj
{
  /** Unprocessed graphics. */
  gobjContainer & gx;
  /** The tetrahedron. */
  TET const & tr;
public:

  /** The graphics output and tetrahedron. */
  tetrahedrondisplaytest
  ( 
    gobjContainer & _gx, 
    TET const & _tr
  ) : gx(_gx), tr(_tr) {}
  
  /** Write the graphics as gobj objects. */
  void draw();
};

/*!
\brief Display test. 
*/
template< typename TET, typename TD >
class tetrahedrontriangledisplay : public gobj
{
  /** Unprocessed graphics. */
  gobjContainer & gx;
  /** The tetrahedron. */
  TET const & tr;
public:

  typedef triangle3D
  <
    typename TET::PTtype,
    typename TET::PDtype 
  > triType;

  /** The client can configure the triangle displays. */
  TD * tdi[4];
  /** Each display has an associated triangle. */
  triType ti[4];

  /** Example 
      tetrahedrontriangledisplaytdimacro(tetd, turnon() ); */
  #define tetrahedrontriangledisplaytdimacro(tet,xarg) \
  for (uint i=0; i<4; ++i ) tet.tdi[i]->xarg

  /** The graphics output and tetrahedron. */
  tetrahedrontriangledisplay
  ( 
    gobjContainer & _gx, 
    TET const & _tr
  ) : gx(_gx), tr(_tr) 
  {
    for (uint i=0; i<4; ++i)
    {
      tr.trianglei(ti[i],i);
      tdi[i] = new TD(gx,ti[i]);
    }
  }
  
  /** Memory cleanup. */
  ~tetrahedrontriangledisplay()
  {
    for (uint i=0; i<4; ++i)
      delete tdi[i];
  }

  /** Write the graphics as gobj objects. */
  void draw();
};

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

template< typename TET >
void tetrahedrondisplaytest<TET>::draw()
{
  gx.push( new gobjglColor3ub(0,0,139) );

  gobjQuadric * gd = new gobjQuadric();
  gd->radius=.02;
  gx.push( gd );

  typename TET::PTtype e0;
  tr.equilaterali(e0,0);
  typename TET::PTtype e1;
  tr.equilaterali(e1,1);
  typename TET::PTtype e2;
  tr.equilaterali(e2,2);
  typename TET::PTtype e3;
  tr.equilaterali(e3,3);

  gx.push( new gobjMySphereDraw(e0,gd) );
  gx.push( new gobjMySphereDraw(e1,gd) );
  gx.push( new gobjMySphereDraw(e2,gd) );
  gx.push( new gobjMySphereDraw(e3,gd) );

  gx.push( new gobjglBegin(GL_LINES) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(e0) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(e0) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(e0) );

  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(e1) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(e1) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(e1) );

  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(e2) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(e2) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(e2) );

  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(e3) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(e3) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(e3) );


  gx.push( new gobjglEnd() );


  gx.push( new gobjglColor3ub(148,0,211) );
  gx.push( new gobjglBegin(GL_LINES) );
  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(e0) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(e1) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(e2) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(e3) );


  gx.push( new gobjglEnd() );


  typedef triangle3D
  <
    typename TET::PTtype,
    typename TET::PDtype 
  > triType;

  triType t0;

  tr.trianglei(t0,0);


//  triangledisplaypoints<triType> td(gx,t0);
/*
  td.turnon();
  td.draw();
*/

}

template< typename TET >
void tetrahedrondisplaymesh<TET>::draw()
{
  gx.push( new gobjglColor3ub(0,0,255) );

  gx.push( new gobjglBegin(GL_LINES) );
  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );

  gx.push( new gobjglEnd() );
}

template< typename TET >
void tetrahedrondisplaycentroid<TET>::draw()
{
  gx.push( new gobjglColor3ub(0,255,0) );

  gobjQuadric * gd = new gobjQuadric();
  gd->radius=.02;
  gx.push( gd );

  typename TET::PTtype c;
  tr.centroid(c);

  typename TET::PTtype c0 = (tr.pi[1]+tr.pi[2]+tr.pi[3])/3.0;
  typename TET::PTtype c1 = (tr.pi[0]+tr.pi[2]+tr.pi[3])/3.0;
  typename TET::PTtype c2 = (tr.pi[1]+tr.pi[0]+tr.pi[3])/3.0;
  typename TET::PTtype c3 = (tr.pi[1]+tr.pi[0]+tr.pi[2])/3.0;

  gx.push( new gobjMySphereDraw(c,gd) );

  gx.push( new gobjglBegin(GL_LINES) );
  gx.push( new gobjglVertex3f(tr.pi[0]) );
  gx.push( new gobjglVertex3f(c0) );
  gx.push( new gobjglVertex3f(tr.pi[1]) );
  gx.push( new gobjglVertex3f(c1) );
  gx.push( new gobjglVertex3f(tr.pi[2]) );
  gx.push( new gobjglVertex3f(c2) );
  gx.push( new gobjglVertex3f(tr.pi[3]) );
  gx.push( new gobjglVertex3f(c3) );
  gx.push( new gobjglEnd() );

}


/*
// <TODO> This is displaying a triangle face.

  typedef triangle3D
  <
    typename TET::PTtype,
    typename TET::PDtype 
  > triType;

  triType t0;
  tr.trianglei(t0,0);

  //triangledisplaypoints<triType> td(gx,t0);
  triangledisplay<triType> td(gx,t0);

  td.turnon();
  td.draw();

*/


template< typename TET >
void tetrahedrondisplay<TET>::turnon()
{
  mesh=true;
  centroid=true;
  test=true;
  trianglecenterpoints=true;
  trianglecenters=true;
}


template< typename TET >
void tetrahedrondisplay<TET>::draw()
{
  vector< gobj* > vi;

  if (mesh)
    vi.push_back(new tetrahedrondisplaymesh<TET>(gx,tr));
  if (centroid)
    vi.push_back(new tetrahedrondisplaycentroid<TET>(gx,tr));
  if (test)
    vi.push_back(new tetrahedrondisplaytest<TET>(gx,tr));
  if (trianglecenterpoints)
  {

    typedef triangledisplaypoints
      < triangle3D< typename TET::PTtype, typename TET::PDtype > > 
      tridisptype;
//    typedef tetrahedron<typename TET::PTtype, typename TET::PDtype> 
//      tettype;

    tetrahedrontriangledisplay< TET, tridisptype > tetd(gx,tr);
    tetrahedrontriangledisplaytdimacro(tetd, turnon() ); 
    tetd.draw();
  }
  if (trianglecenters)
  {
    typedef triangledisplay
      < triangle3D< typename TET::PTtype, typename TET::PDtype > > 
      tridisptype;

    tetrahedrontriangledisplay< TET, tridisptype > tetd(gx,tr);
    tetrahedrontriangledisplaytdimacro(tetd, turnon() ); 
    tetd.draw();
  } 

  for (uint i=0; i<vi.size(); ++i)
  {
    vi[i]->draw();
    delete vi[i];
  }
}

template< typename TET, typename TD >
void tetrahedrontriangledisplay<TET,TD>::draw()
{
  for (uint i=0; i<4; ++i)
  {
    assert(tdi[i]!=0);
    tdi[i]->draw();
  }
}

#endif



