#include <GL/glut.h>

#include <zpr.h>
#include <graphmisc.h>

#include <zprtest.h>
#include <iostream>
#include <algorithm>
using namespace std;


#include <graphmisc.h>

  


// Error Checking Code.
inline void glerrordisplay()
{
  GLenum errval = glGetError();
  if (errval != GL_NO_ERROR)
    cout << gluErrorString( errval ) << endl;
}

void zprtest::display()
{
  // Drawing a coordinate system with cone pointers

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();

glerrordisplay();

  glDisable(GL_LIGHTING);
  glBegin(GL_LINES);
    glColor3f(0,0,1);
    glVertex3f(0,0,0);
    glVertex3f(0,0,1);
    glColor3f(0,1,0);
    glVertex3f(0,0,0);
    glVertex3f(0,1,0);
    glColor3f(1,0,0);
    glVertex3f(0,0,0);
    glVertex3f(1,0,0);
  glEnd();

glerrordisplay();

  glEnable(GL_LIGHTING);
  // glutSolidCone(base_radius,length,base_verticies,cone_axis_verticies)

  // The colors RGB correspont to the XYZ axes

  glPushMatrix();
  // First Translate the coordinate system
    glTranslatef(1,0,0);
    glColor3f(1,0,0);

      // Pointing along the z-axis

      // Rotation is counter clockwise in the plane with y normal
      //     This gets us pointing along the x-axis
    glRotatef(90,0,1,0);

      //glTranslatef(1,0,0);
    glutSolidCone(.2,1.0,10,5);
  glPopMatrix();

glerrordisplay();

  glPushMatrix();
    glTranslatef(0,1,0);
    glColor3f(0,1,0);
    glRotatef(270,1,0,0);
    glutSolidCone(.2,1.0,10,5);
  glPopMatrix();

  glPushMatrix();
    // Already pointing along the z-axis
    glTranslatef(0,0,1);
    glColor3f(0,0,1);
    glutSolidCone(.2,1.0,10,5);
  glPopMatrix();

glerrordisplay();

  glutSwapBuffers();

  glPopMatrix();
}

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

/*
void zpreval()
{
  // glut idle function
}
*/

void zprtest::printMatrixMode() const
{
  GLint mode;
  glGetIntegerv(GL_MATRIX_MODE, &mode);
  string s;
  switch (mode) 
  {
    case GL_MODELVIEW: s += "GL_MODELVIEW"; break;
    case GL_PROJECTION: s += "GL_PROJECTION"; break;
    case GL_TEXTURE: s+= "GL_TEXTURE"; break;
    case GL_COLOR: s+= "GL_COLOR"; break;
    default: s+="unknown";
  }
  cout << "matrix mode: " << s << endl;
}


int zprtest::test01(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode
    (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  int wx=800; 
  int wy=600;
  glutInitWindowSize(wx,wy);
  glutCreateWindow("Coordinate Axes");

  zpr zz;

  cout << "After construction." << endl;
  zz.printInfo();

  // Callbacks

  glutKeyboardFunc(zprtest::keyboard);
  glutDisplayFunc(zprtest::display);
  //glutIdleFunc(zpreval);

  glEnable(GL_NORMALIZE);

//  glScalef(0.25,0.25,0.25);
/*

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //gluPerspective(65, (GLdouble)wx/(GLdouble)wy, .25, 10); 
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

  // COP - Center Of Projection
  // VRP - View Reference Point
  // VUP - View UP
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(
    0.0,0.0,0.0, 
    0.0,0.0,8.0, 
    0.0,1.0,0.0); 

  zz.update();
*/

  OpenGLinitialisation();

  // Never returns
  glutMainLoop();

  return 0;
}



void zprtest::test02(int argc, char** argv)
{
  cout << "Starting up OpenGL." << endl;

  glutInit(&argc, argv);
  glutInitDisplayMode
    (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  int wx=800; 
  int wy=600;
  glutInitWindowSize(wx,wy);
  glutCreateWindow("Coordinate Axes");
  zpr zz;
  
  //zpr::init(wx,wy);
  glutKeyboardFunc(zprtest::keyboard);
  glutDisplayFunc(zprtest::display);


  GLdouble pmatrix[16];

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glGetDoublev(GL_PROJECTION_MATRIX,pmatrix);
  cout << "The identity matrix should be printed." << endl;
  GLdouble * p2 = & pmatrix[0];
  zprGLmatrix(p2).print();
  cout << endl;

  GLdouble left=-1.0;
  GLdouble right=1.0;
  GLdouble bottom=-0.5;
  GLdouble top=1.5;
  GLdouble zNear=1.0;
  GLdouble zFar=4.2;
  cout << SHOW(left) << endl;
  cout << SHOW(right) << endl;
  cout << SHOW(bottom) << endl;
  cout << SHOW(top) << endl;
  cout << SHOW(zNear) << endl;
  cout << SHOW(zFar) << endl;
  glFrustum(left,right,bottom,top,zNear,zFar);

  glGetDoublev(GL_PROJECTION_MATRIX,pmatrix);
  cout << "Calculate the view frustrum with OpenGL" << endl;
  zprGLmatrix(p2).print();
  cout << endl << endl;
  zprGLmatrix(p2).printTranspose();
  cout << endl;

  zprGLmatrix mat(p2);

  GLdouble a[6];
  a[0] = mat.access(0,0);
  a[1] = mat.access(1,1);
  a[2] = mat.access(0,2);
  a[3] = mat.access(1,2);
  a[4] = mat.access(2,2);
  a[5] = mat.access(2,3);
  cout << SHOW(a[0]) << endl;
  cout << SHOW(a[1]) << endl;
  cout << SHOW(a[2]) << endl;
  cout << SHOW(a[3]) << endl;
  cout << SHOW(a[4]) << endl;
  cout << SHOW(a[5]) << endl;

  GLdouble c0 = (1.0-a[4])/(1.0+a[4]);
  cout << SHOW(c0) << endl;
  GLdouble z0 = a[5]*(c0+1.0)*-0.5/c0;
  cout << SHOW(z0) << endl;
  GLdouble z1 = -c0*z0;
  cout << SHOW(z1) << endl;
  GLdouble x0 = z0*(a[2]-1.0)/a[0];
  cout << SHOW(x0) << endl;
  GLdouble x1 = (z0*2.0+a[0]*x0)/a[0];
  cout << SHOW(x1) << endl;
  GLdouble y0 = z0*(a[3]-1.0)/a[1];
  cout << SHOW(y0) << endl;
  GLdouble y1 = (z0*2.0+a[1]*y0)/a[1];
  cout << SHOW(y1) << endl;
}


void zprtest::test03(int argc, char** argv)
{
  cout << "Starting up OpenGL." << endl;

  glutInit(&argc, argv);
  glutInitDisplayMode
    (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  int wx=800; 
  int wy=600;
  glutInitWindowSize(wx,wy);
  glutCreateWindow("Coordinate Axes");

  zpr zz; 
  //zpr::init(wx,wy);

  glutKeyboardFunc(zprtest::keyboard);
  glutDisplayFunc(zprtest::display);



  cout << "printing out zpr's state." << endl;
  zz.printInfo();
/*
  cout << "zprtest03.cpp glFrustum(left,right,bottom,top,zNear,zFar)" << endl;
  cout << SHOW(left) << endl;
  cout << SHOW(right) << endl;
  cout << SHOW(bottom) << endl;
  cout << SHOW(top) << endl;
  cout << SHOW(zNear) << endl;
  cout << SHOW(zFar) << endl;
*/

/*
  cout << endl;
  cout << "zpr's current frustrum" << endl;
  zpr::printInfo();
*/
  cout << endl;

  printMatrixMode();

  cout << "Changing OpenGL's frustrum" << endl;

  {
    myglPushMatrixMode tmp;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLdouble x0=-1.0;
    GLdouble x1=1.0;
    GLdouble y0=-0.5;
    GLdouble y1=1.5;
    GLdouble z0=1.0;
    GLdouble z1=4.2;
    glFrustum(x0,x1,y0,y1,z0,z1);
    cout << SHOW(x0) << endl;
    cout << SHOW(x1) << endl;
    cout << SHOW(y0) << endl;
    cout << SHOW(y1) << endl;
    cout << SHOW(z0) << endl;
    cout << SHOW(z1) << endl;
    cout << "glFrustum(x0,x1,y0,y1,z0,z1);" << endl;

    cout << "Reading OpenGL's projection matrix." << endl;

    zz.readProjection();
  
    printMatrixMode();
  }
  cout << endl;

  printMatrixMode();
 
  cout << "zpr's current state" << endl;
  zz.printInfo();
  cout << endl;

}




