
#include <commandline.h>
#include <gobj.h>
#include <graphmisc.h>
#include <partitionequ.h>
#include <partitionstest.h>
#include <point.h>
#include <random.h>
#include <tetrahedronpartition.h>
#include <trianglepartition.h>
#include <typedefs.h>
#include <zpr.h>


partitionstest* partitionstest::global=0;


template<>
double zero<double>::val = 1E-15;


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

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

  assert(global!=0);

  glColor3f(1.0,0.0,0.0);
  glBegin(GL_POINTS);
  uint sz=global->red.size();
  for (uint i=0; i<sz; ++i)
    glVertex2f(global->vec[global->red[i]].x,global->vec[global->red[i]].y);
  glEnd();

  glColor3f(0.0,0.0,1.0);
  glBegin(GL_POINTS);
  sz=global->blue.size();
  for (uint i=0; i<sz; ++i)
    glVertex2f(global->vec[global->blue[i]].x,global->vec[global->blue[i]].y);
  glEnd();

  glerrordisplay();
  
  glutSwapBuffers();
}

void partitionstest::reshape(GLsizei w1, GLsizei h1)
{
  glViewport(0,0,w1,h1);

  assert(global!=0);

  double rx = (double)(global->width) / (double)w1;
  double ry = (double)(global->height) / (double)h1;

  for (uint i=0; i<global->vec.size(); ++i)
  {
    global->vec[i].x *= rx;
    global->vec[i].y *= ry;
  }

  global->width = w1; 
  global->height = h1;
}

partitionstest::partitionstest(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

  width=600;
  height=600;
  glutInitWindowSize(width,height);
  glutCreateWindow("");

  glutDisplayFunc(display01);
  glutMouseFunc(mousefunc01);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard01);

  glClearColor(1.0,1.0,1.0,0.0);

  glColor3d(0.0,0.0,0.0);
  glPointSize(3.0);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0.0,1.0,0.0,1.0);

  set();
}

void partitionstest::addpoint(intc x, intc y)
{
  pt2 pos;
  pos.x = x/(double)width;
  pos.y = 1.0-y/(double)height;

  uintc k=vec.size();
  vec.push_back( pos );

  assert(ps!=0);

  if (ps->isInside(pos))
    red.push_back(k);
  else
    blue.push_back(k);

  glutPostRedisplay();
}

void partitionstest::addstar
( 
  intc k, 
  intc x,
  intc y 
)
{
  addpoint(x,y);
  addpoint(x+k,y);
  addpoint(x-k,y);
  addpoint(x,y+k);
  addpoint(x+k,y+k);
  addpoint(x-k,y+k);
  addpoint(x,y-k);
  addpoint(x+k,y-k);
  addpoint(x-k,y-k);
}

void partitionstest::addstarbig
( 
  intc x,
  intc y 
)
{
  // Incrementing in Fibonacci pattern, no particular reason.
  // eg 6 = 3 + F[2], 11 = 6 + F[3], 
  //   19 = 11 + F[4], 32 = 19 + F[5], ...
  addstar(1,x,y);
  addstar(3,x,y);
  addstar(6,x,y);
  addstar(11,x,y);
  addstar(19,x,y);
  addstar(32,x,y);
  addstar(53,x,y);
}




void partitionstest::mousefunc01
(
  int button, 
  int state, 
  int x, 
  int y
)
{
  assert(global!=0);

  if (button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
  {
    global->addstarbig(x,y);
  }
}

void partitionstest::test01()
{
  pt2 a(0.0,0.0);
  pt2 b(0.5,1.0);
  ps = new halfspaceD2<pt2,double>(a,b);

  glutPostRedisplay();
  glutMainLoop();
}


void partitionstest::test02()
{
  pt2 a(0.3,0.3);
  pt2 b(0.4,0.8);
  pt2 c(0.7,0.2);
  ps = new trianglepartition< pt2, double >(a,c,b);

  glutPostRedisplay();
  glutMainLoop();
}

void partitionstest::test03()
{
  halfspaceD2< pt2, double > L[4];

  L[0].set( pt2(0.4,0.8), pt2(0.3,0.3) );
  L[1].set( pt2(0.3,0.3), pt2(0.7,0.2) );
  L[2].set( pt2(0.7,0.2), pt2(0.4,0.5) );
  L[3].set( pt2(0.4,0.5), pt2(0.4,0.8) );

  PeqCapture(ps,Peq(L[0])*Peq(L[1])*(Peq(L[2])+Peq(L[3])));

  glutPostRedisplay();
  glutMainLoop();
}

void partitionstest::test04()
{
  halfspaceD2< pt2, double > L[4];

  pt2 A(0.4,0.8);
  pt2 B(0.4,0.2);
  pt2 C(0.9,0.2);
  pt2 D(0.9,0.8);

  L[0].set(A,B);
  L[1].set(B,C);
  L[2].set(C,D);
  L[3].set(D,A);

  PeqCapture(ps,!(Peq(L[0])*Peq(L[1])*Peq(L[2])*Peq(L[3])));

  glutPostRedisplay();
  glutMainLoop();
}

class circlepartition : public partitionspace<partitionstest::pt2>
{
public:

  partitionstest::pt2 center;
  double radius;

  circlepartition( partitionstest::pt2 const & _center, doublec _radius )
    : center(_center), radius(_radius) {}

  boolc isInside( partitionstest::pt2 const & x ) const
  {
    partitionstest::pt2 x2 = x-center;
    return x2.x*x2.x+x2.y*x2.y<radius*radius;
  }
};
    

partitionstest::partitionstest()
{
}

void partitionstest::display02()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  gobj::global->draw();

  glerrordisplay();
  
  glutSwapBuffers();
}


void partitionstest::test05()
{

  halfspaceD2< pt2, double > L[4];

  pt2 A(0.4,0.8);
  pt2 B(0.4,0.2);
  pt2 C(0.9,0.2);
  pt2 D(0.9,0.8);

  L[0].set(A,B);
  L[1].set(B,C);
  L[2].set(C,D);
  L[3].set(D,A);

  circlepartition cir( pt2(0.5,0.4), 0.3 );
  PeqCapture(ps,Peq(L[0])*Peq(L[1])*Peq(L[2])*Peq(L[3])-Peq(cir));

  glutPostRedisplay();
  glutMainLoop();
}


void partitionstest::test06(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

  width=600;
  height=600;
  glutInitWindowSize(width,height);
  glutCreateWindow("");
  glutDisplayFunc(display02);
  glutKeyboardFunc(keyboard01);

  zpr zz;
  OpenGLinitialisation();

  xGraphics.set();


  unsigned int numPoints=100000;

  cout << "Testing tetrahedron" << endl;
  cout << "You can define the tetrhedron" << endl;
  cout << "eg" << endl;
  cout << "$./main a0=0.5 a1=0.0 a2=0.866 b0=-0.5 b1=0.0 b2=0.866 c0=-1.0 c1=1.0 c2=0.0 d0=0.0 d1=1.0 d2=0.0" << endl;
  cout << "Points are scattered about a unit cube at the origin." << endl;
  cout << "scale=2.5         Scale the cube." << endl;
  cout << "numPoints=" << numPoints << "   Change the number of points." << endl;
  cout << "green=false       Turn of green points." << endl;
  cout << endl;

  commandline cmd(argc,argv);
  double a0=-0.5;
  double a1=0.0;
  double a2=0.866;

  double b0=-0.5;
  double b1=0.0;
  double b2=-0.866;

  double c0=1.0;
  double c1=0.0;
  double c2=0.0;

  double d0=0.0;
  double d1=0.85556;
  double d2=0.0;

  cmd.mapvar(numPoints,"numPoints");

  cmd.mapvar(a0,"a0");
  cmd.mapvar(a1,"a1");
  cmd.mapvar(a2,"a2");
  cmd.mapvar(b0,"b0");
  cmd.mapvar(b1,"b1");
  cmd.mapvar(b2,"b2");
  cmd.mapvar(c0,"c0");
  cmd.mapvar(c1,"c1");
  cmd.mapvar(c2,"c2");
  cmd.mapvar(d0,"d0");
  cmd.mapvar(d1,"d1");
  cmd.mapvar(d2,"d2");

  bool green = false;
  cmd.mapvar(green,"green");

  double scale=2.0;
  cmd.mapvar(scale,"scale");

  pt3 p0(a0,a1,a2);
  pt3 p1(b0,b1,b2);
  pt3 p2(c0,c1,c2);
  pt3 p3(d0,d1,d2);

  tetrahedronpartition< pt3, double > tet(p0,p2,p1,p3);

  pt3 colr1(1.0,0.0,0.0);
  pt3 colr2(0.0,1.0,0.0);

  random11<double> r;

  pt3 X;

  xGraphics.push( new gobjglDisable(GL_LIGHTING) );

  xGraphics.push( new gobjglBegin(GL_POINTS) );

  for (uint i=0; i<numPoints; ++i)
  {
    X = pt3(scale*(r()-0.5),scale*(r()-0.5),scale*(r()-0.5));

    if (tet.isInside(X))
    {
      xGraphics.push( new gobjglColor3f(colr1.x,colr1.y,colr1.z) );
      xGraphics.push( new gobjglVertex3f(X.x,X.y,X.z) );
    }
    else
    {  
      if (green)
      {
        xGraphics.push( new gobjglColor3f(colr2.x,colr2.y,colr2.z) );
        xGraphics.push( new gobjglVertex3f(X.x,X.y,X.z) );
      }
    }
    
  }

  xGraphics.push( new gobjglEnd() );

  zz.update();

  glutMainLoop();
}


