#include <vector>
#include <fstream>
using namespace std;

#include <commandline.h>
#include <generaterandompoints.h>
#include <graphmisc.h>
#include <gobj.h>
#include <mathlib.h>
#include <menusystem.h>
#include <point.h>
#include <pointsdisplay.h>
#include <print.h>
#include <printvector.h>
#include <quickhull3D.h>
#include <quickhull3Dtest.h>
#include <random.h>
#include <stringserialization.h>
#include <zpr.h>

quickhull3Dtest* quickhull3Dtest::member=0;

void quickhull3Dtest::keyboard01
(
  unsigned char key, 
  int x, 
  int y
)
{
  assert(member!=0);
  assert(member->displayaxes!=0);
  assert(member->quickhull!=0);

  switch (key)
  {
    case 27: exit(0); break;
    case 'a': member->displayaxes->toggle(); break;

    case 'p': member->pointsdisplay = ! member->pointsdisplay; break;
    case 'P': member->pointsnumber= ! member->pointsnumber;
      member->pointscontainer.nuke();
      pointsdisplay3D< pt3 >(
        member->pointscontainer,
        member->pts,
        member->pointsdisplay,
        member->pointsnumber);
      break;

    case 'h': member->menuhelp = ! member->menuhelp; break;

    case 'R':
    {
      quickhull3D< pt3, double > & qh(*(member->quickhull));
      qh.reset(); 
    }
    dynamicupdate();
    break;

    case 'r': 
    {
      quickhull3D< pt3, double > & qh(*(member->quickhull));
      for ( ; !qh; ++qh);
    }
    dynamicupdate();
    break;

    case 's':
    {
      quickhull3D< pt3, double > & qh(*(member->quickhull));
      if (!qh)
        ++qh;
    }
    dynamicupdate();
    break;

    case 'x': 
    {
      quickhull3D< pt3, double > & qh(*(member->quickhull));
      for ( qh.reset(); !qh; ++qh);
    }
    dynamicupdate();
    break;
     
  }

  glutPostRedisplay();
}

void quickhull3Dtest::display01()
{ 
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  gobj::global->draw();


  glerrordisplay();
  
  glutSwapBuffers();
}

quickhull3Dtest::quickhull3Dtest()
  : xGraphics(true), pointscontainer(true), dynamic(true), 
    pointsdisplay(true),
    pointsnumber(false), menuhelp(true), menuswitch(0), quickhull(0)
{
}


void quickhull3Dtest::dynamicupdate()
{
  assert(member!=0);

  member->dynamic.nuke();
  member->dynamic.globalpush();

  gobjpush(new gobjglPushAttrib(GL_LIGHTING));
  gobjpush(new gobjglPushAttrib(GL_CURRENT_BIT));
  gobjpush(new gobjglEnable(GL_LIGHTING));

  gobjpush( new gobjglEnable(GL_BLEND));
  gobjpush(new gobjglBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
  gobjpush(new gobjglColor4f(191.0/255.0,0.0,0.0,0.3));

  // Draw the hull points found so far.

  gobjQuadric * q2 = new gobjQuadric();
  q2->radius = 0.10;
  q2->slices=25;
  q2->loops=7;
  gobjpush(q2);

  quickhull3D< pt3, double > & qh(*member->quickhull);

  for (uint i=0; i<qh.boundary.size(); ++i)
    { gobjpush( new gobjMySphereDraw(qh.pts[qh.boundary[i]],q2) ); }

  // Draw each half-space container and its points. 

  random11<double> r;

  gobjQuadric * q3 = new gobjQuadric();
  q3->radius = 0.02;
  q3->slices=25;
  q3->loops=7;
  gobjpush(q3);

  if (qh.cs.size()>0)
  {
    uint current = qh.cs.size()-1;

    gobjpush(new gobjglColor3f(1.0,0.0,0.0));
    gobjpush(new gobjglDisable(GL_LIGHTING));

    gobjpush(new gobjglBegin(GL_LINES));

    assert( qh.cs[current]!=0 );
    quickhull3D<pt3,double>::HS & hs(qh.cs[current]->halfspace);

    gobjpush(new gobjglVertex3f(hs.p0.x,hs.p0.y,hs.p0.z));
    gobjpush(new gobjglVertex3f(hs.p1.x,hs.p1.y,hs.p1.z));
    gobjpush(new gobjglVertex3f(hs.p1.x,hs.p1.y,hs.p1.z));
    gobjpush(new gobjglVertex3f(hs.p2.x,hs.p2.y,hs.p2.z));
    gobjpush(new gobjglVertex3f(hs.p2.x,hs.p2.y,hs.p2.z));
    gobjpush(new gobjglVertex3f(hs.p0.x,hs.p0.y,hs.p0.z));

    pt3 q0
    ((hs.p0.x+hs.p1.x+hs.p2.x)/3.0,
      (hs.p0.y+hs.p1.y+hs.p2.y)/3.0,
      (hs.p0.z+hs.p1.z+hs.p2.z)/3.0
    );
    pt3 q1(q0.x+hs.normal.x,q0.y+hs.normal.y,q0.z+hs.normal.z);

    gobjpush(new gobjglVertex3f(q0.x,q0.y,q0.z));
    gobjpush(new gobjglVertex3f(q1.x,q1.y,q1.z));

    gobjpush(new gobjglEnd());

    gobjpush(new gobjglEnable(GL_LIGHTING));
  }

  for (uint i=0; i<qh.cs.size(); ++i)
  {
    gobjpush(new gobjglColor4d(r(),r(),r(),r()));

    assert( qh.cs[i]!=0 );
    quickhull3D<pt3,double>::HS & hs(qh.cs[i]->halfspace);

    gobjpush(new gobjglBegin(GL_TRIANGLES));
    gobjpush(new gobjglVertex3f(hs.p0.x,hs.p0.y,hs.p0.z));
    gobjpush(new gobjglVertex3f(hs.p1.x,hs.p1.y,hs.p1.z));
    gobjpush(new gobjglVertex3f(hs.p2.x,hs.p2.y,hs.p2.z));
    gobjpush(new gobjglEnd());
    
    list<uint> & L(qh.cs[i]->index);
    for (list<uint>::iterator k=L.begin();
      k!=L.end(); ++k)
    {
      gobjpush( new gobjMySphereDraw(qh.pts[*k],q3) ); 
    }
  }

  gobjpush(new gobjglPopAttrib());
  gobjpush(new gobjglPopAttrib());

  member->dynamic.globalpop();
}



void quickhull3Dtest::test01(int argc, char** argv)
{
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(800,600);
  glutCreateWindow("");
  glutDisplayFunc(display01);
  glutKeyboardFunc(keyboard01);

  OpenGLinitialisation();

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glEnable(GL_NORMALIZE);

  xGraphics.set();

  member = this;
  displayaxes = new gobjSwitch<>( new myaxes(1.0), true);
  gobjpush(displayaxes);

  commandline cmd(argc,argv);
  uint numPoints(10);
  cmd.mapvar(numPoints,"n");

  typedef point3<double> pt3;
  //vector< pt3 > v;
  generateRandomPointsInSphere< pt3, double, random11<double> >(pts,numPoints+1);

  //cout << SHOW(pts.size()) << endl;

  gobjpush(new gobjglColor3f(0.0,1.0,0.0));
  gobjpush(new gobjglDisable(GL_LIGHTING));

  pointsdisplay3D< pt3 >( member->pointscontainer, pts, pointsdisplay, pointsnumber );
  gobjpush(new gobjSwitch<bool &>(&member->pointscontainer,pointsdisplay,false));

  menusystem * menu = 
    new menusystem(0,0,true,point2<GLint>(60,30),10);
  menu->fontcolor = point4<float>(218.0/255.0,165.0/255.0,32.0/255.0,0.75);

  menuswitch = new gobjSwitch<bool &>(menu,menuhelp);

  menu->addfont12("Hull calculation in 3D",2);
  menu->addfont10("p   Toggle the points.",1);
  menu->addfont10("P   Toggle numbering the points.",1);
  menu->addfont10("a   Toggle the axes.",2);

  menu->addfont10("x   reset and run",1);
  menu->addfont10("R   reset",1);
  menu->addfont10("r   run",1);
  menu->addfont10("s   step",2);

  menu->addfont10("h   toggle this help menu",1);
  menu->addfont10("ESC  quit");

  gobjpush(menuswitch);

  gobjpush(new gobjSwitch<>(&dynamic,true,false));
  
  glutPostRedisplay();

  quickhull = new quickhull3D< pt3, double >(pts);

  //quickhull3D< pt3, double > qh(pts);

  //for ( qh.reset(); !qh; ++qh);
  

  //cout << print(qh.boundary) << endl;


/*
  quickhull3D< pt3, double > qh(v);

  //cout << SHOW(print(qh.boundary)) << endl;

  gobjpush(new gobjglDisable(GL_LIGHTING));
  gobjpush(new gobjglColor3ub(255,0,0));
  gobjpush(new gobjglBegin(GL_POINTS));
  for (uint i=0; i<qh.boundary.size(); ++i)
    { gobjpush(new gobjglVertex3d(v[qh.boundary[i]])); }
  gobjpush(new gobjglEnd());

  vector<uint> v2;
  integersetdiff(v2,qh.boundary,numPoints);
  gobjpush(new gobjglColor3ub(0,255,0));
  gobjpush(new gobjglBegin(GL_POINTS));
  for (uint i=0; i<v2.size(); ++i)
    { gobjpush(new gobjglVertex3d(v[v2[i]])); }
  gobjpush(new gobjglEnd());
*/


  //gobj::global->displaylist(1);


  zpr zz;
  glutMainLoop();
}


/*

void quickhull3Dtest::test02(int argc, char** argv)
{
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(800,600);
  glutCreateWindow("");
  glutDisplayFunc(display01);
  glutKeyboardFunc(keyboard01);

  OpenGLinitialisation();

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glEnable(GL_NORMALIZE);

  xGraphics.set();

  commandline cmd(argc,argv);

  uint numPoints(500);
  cmd.mapvar(numPoints,"numPoints");

  bool labelpoints(false);
  cmd.mapvar(labelpoints,"labelpoints");
  bool drawpoints(true);
  cmd.mapvar(drawpoints,"drawpoints");

  typedef point3<double> pt3;
  vector< pt3 > v;
  generateRandomPointsInSphere< pt3, double, random11<double> >(v,numPoints);
  
  gobjpush( new gobjglDisable(GL_LIGHTING) );
  gobjpush( new gobjglColor3f(0.0,1.0,0.0) );
  pointsdisplay3D<pt3> dp(*gobj::global,v,drawpoints,labelpoints);

//cout << "Pre quickhull3D" << endl;
  quickhull3D< pt3, double > qh(v);
//cout << "Post quickhull3D" << endl;

  vector< pt3 > v2;
  for (uint i=0; i<qh.boundary.size(); ++i)
    v2.push_back( v[qh.boundary[i]] );
  gobjpush( new gobjglColor3f(1.0,0.0,0.0) );
  pointsdisplay3D<pt3> dp2(*gobj::global,v2,true,false);
  dp2.gq->radius=0.02;


  //gobj::global->displaylist(1);
  zpr zz;
  glutMainLoop();
}
*/








