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

#include <commandline.h>
#include <generaterandompoints.h>
#include <graphmisc.h>
#include <gobj.h>
#include <point.h>
#include <pointsdisplay.h>
#include <print.h>
#include <quickhull2D.h>
#include <quickhull2Dtest.h>
#include <random.h>
#include <stringserialization.h>
#include <zpr.h>


//template<>
//double const halfspaceD2<point2<double>,double>::zero = 1E-15;

//template<>
//float const halfspaceD2<point2<float>,float>::zero = 1E-6;

void quickhull2Dtest::keyboard01
(
  unsigned char key, 
  int x, 
  int y
)
{
  switch (key)
  {
    case 27:
      exit(0);
      break;
  }
}

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

  gobj::global->draw();


  glerrordisplay();
  
  glutSwapBuffers();
}

void quickhull2Dtest::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);

  cout << "Calculate the hull of a randomly generated set of points" << endl;
  cout << "  inside a circle. The hull points are colored red." << endl;
  cout << "Options:" << endl;
  cout << "  numPoints=4500  varies the number of points." << endl;
  
  uint numPoints(500);
  cmd.mapvar(numPoints,"numPoints");

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

  typedef point2<double> pt2;
  vector< pt2 > v;
  generateRandomPointsInCircle< pt2, double, random11<double> >(v,numPoints);
  //vectorprint::space="\n";
  //cout << v << endl;

  quickhull2D< pt2, double > qh(v);

  gobjpush( new gobjglDisable(GL_LIGHTING) );
  gobjpush( new gobjglColor3f(0.0,1.0,0.0) );
  pointsdisplay2D<pt2> dp(*gobj::global,v,drawpoints,labelpoints);

  vector< pt2 > 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) );
  pointsdisplay2D<pt2> dp2(*gobj::global,v2,true,false);
  dp2.gq->radius=0.02;

  gobjpush( new gobjglBegin(GL_LINE_LOOP) );
  for (uint i=0; i<qh.boundary.size(); ++i)
    { gobjpush( new gobjglVertex2f(v[qh.boundary[i]]) ); }
  gobjpush( new gobjglEnd() );

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


void quickhull2Dtest::test03()
{
  uintc n=6;
  vector< point2<float> > v;
  generateRandomPointsInCircle< point2<float>, float, random11<float> >(v,n);
  cout << print(v,"\n") << endl;

  // <TODO>
  //quickhull2D<point2<float>,float> q1(v);
  //cout << q1.boundary << endl;
}

void quickhull2Dtest::test04(int argc, char** argv)
{
  commandline cmd(argc,argv);
  
  uint numPoints(10);
  cmd.mapvar(numPoints,"numPoints");
  string pointsfile;
  cmd.mapvar(pointsfile,"pointsfile");
  if (pointsfile.empty())
  {
    cout << "error:  A filename was not given. eg pointsfile=data.txt" << endl;
    return;
  }
  
  vector< point2<double> > pts;
  generateRandomPointsInCircle< point2<double>, double, random11<float> >
    (pts,numPoints);

  if (vectorfile::serialize(pointsfile,pts)==false)
  {
    cout << "error:  Failed to write file" << pointsfile << "." << endl;
    return;
  }
 
  cout << "success:  Written out the points to file " << pointsfile << "." << endl;
  cout << endl;

}

void quickhull2Dtest::test05(int argc, char** argv)
{
  commandline cmd(argc,argv);

  string pointsfile;
  cmd.mapvar(pointsfile,"pointsfile");
  string hullfile;
  cmd.mapvar(hullfile,"hullfile");

  boolc pointsfileinvalid(pointsfile.empty());
  if (pointsfileinvalid)
    cout << "error:  A pointsfile was not given. eg pointsfile=data.txt" << endl;
  boolc hullfileinvalid(hullfile.empty());
  if (hullfileinvalid)
    cout << "error:  A hullfile not given. eg hullfile=index.txt" << endl;

  if (hullfileinvalid||pointsfileinvalid)
    return;

  typedef point2<double> pt2;
  vector< pt2 > pts;
  if (!vectorfile::deserialize(pts,pointsfile))
  {
    cout << "error:  Failed to read a file of 2D points from file.";
    cout << pointsfile << endl;
    return;
  }

  bool randomize(cmd.hastoken("randomize"));
  if (randomize)
  {
    cout << "Randomizing the input points." << endl;


    quickhull2Drandomized<pt2,double> qh(pts);

    if (qh.boundary.size()==0)
    {
      cout << "error:  No points on hull found by the quickhull algorithm." << endl;
      return;
    }

    if (!vectorfile::serialize(hullfile,qh.boundary))
    {
      cout << "error:  Failed to write the hull." << endl;
      return;
    }
  }
  else
  {
    quickhull2D<pt2,double> qh(pts);

    if (qh.boundary.size()==0)
    {
      cout << "error:  No points on hull found by the quickhull algorithm." << endl;
      return;
    }

    if (!vectorfile::serialize(hullfile,qh.boundary))
    {
      cout << "error:  Failed to write the hull." << endl;
      return;
    }
  }

  cout << "Successfully ran the quick hull algorithm." << endl;
  cout << endl;
}

void quickhull2Dtest::test06(int argc, char** arg)
{
  glutInit(&argc,arg);
  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,arg);

  string pointsfile;
  cmd.mapvar(pointsfile,"pointsfile");
  string hullfile;
  cmd.mapvar(hullfile,"hullfile");

  boolc pointsfileinvalid(pointsfile.empty());
  if (pointsfileinvalid)
    cout << "error:  A pointsfile was not given. eg pointsfile=data.txt" << endl;
  boolc hullfileinvalid(hullfile.empty());
  if (hullfileinvalid)
    cout << "error:  A hullfile not given. eg hullfile=index.txt" << endl;

  if (hullfileinvalid||pointsfileinvalid)
    return;

  typedef point2<double> pt2;
  vector< pt2 > pts;
  boolc pointsfilereadinvalid=
    !vectorfile::deserialize(pts,pointsfile);

  if (pointsfilereadinvalid)
    cout << "error:  Failed to read a file of 2D points from file. " << endl;

  vector<uint> boundary;
  boolc hullfilereadinvalid=
    !vectorfile::deserialize(boundary,hullfile);

cout << SHOW(boundary.size()) << endl;

  if (hullfilereadinvalid)
    cout << "error:  Failed to read hull file as indexes." << endl;

  if (pointsfilereadinvalid||hullfilereadinvalid)
    return;

  cout << "success: Read the points and hull indexes." << endl;
  cout << endl;

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

  gobjpush( new gobjglDisable(GL_LIGHTING) );
  gobjpush( new gobjglColor3f(0.0,1.0,0.0) );
  pointsdisplay2D<pt2> dp(*gobj::global,pts,drawpoints,labelpoints);

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

  glutMainLoop();

}



