#include <graphmisc.h>
#include <OpenGLtemplated.h>


void camera::lookatxz
(
  commandline & cmd,
  doublec camerax_, 
  doublec cameraz_ 
)
{
  lookat(cmd,camerax_,0.0,cameraz_);
}


void camera::lookat
(
  commandline & cmd,
  doublec camerax_, 
  doublec cameray_, 
  doublec cameraz_ 
)
{
  double camerax(camerax_);
  double cameray(cameray_);
  double cameraz(cameraz_);
  
  cmd.mapvar(camerax,"camerax");
  cmd.mapvar(cameray,"cameray");
  cmd.mapvar(cameraz,"cameraz");

  glMatrixMode(GL_MODELVIEW);

  glLoadIdentity();
  gluLookAt
  (
    camerax, cameray, cameraz,  // Eye  - creates a x: [-2,2] y: [-2,2] screen.
    camerax, cameray, 0.0,  // Center
    0.0, 1.0, 0.0   // Up
  );
  glutPostRedisplay();
}

point3<double> const myRotate::zaxis(0.0,0.0,1.0);


void wirerectangle
(
  floatc x, 
  floatc y, 
  floatc z
)
{
  myglPushMatrix temp;
  glScalef (x,y,z);
  glutWireCube (1.0);
}

myaxes::myaxes(doublec _length)
  : length(_length)
{
  xaxiscolor=point3<float>(1.0,0.0,0.0);
  yaxiscolor=point3<float>(0.0,1.0,0.0);
  zaxiscolor=point3<float>(0.0,0.0,1.0);
}

void myaxes::draw()
{
  GOBJDEBUGCODE
  myLightingTurnOff temp;

  glBegin(GL_LINES);
   
  glColor3T<GLfloat>()(xaxiscolor);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(length,0.0,0.0);

  glColor3T<GLfloat>()(yaxiscolor);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,length,0.0);
  
  glColor3T<GLfloat>()(zaxiscolor);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.0,length);

  glEnd();
}

void axes(doublec length)
{
  myLightingTurnOff temp;

  glBegin(GL_LINES);
   
  glColor3f(1.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(length,0.0,0.0);

  glColor3f(0.0,1.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,length,0.0);
  
  glColor3f(0.0,0.0,1.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.0,length);

  glEnd();
}

void glerrordisplay()
{
  GLenum errval = glGetError();
  if (errval != GL_NO_ERROR)
     cout << gluErrorString( errval ) << endl;
}




GLfloat OpenGLinitialisation::light_ambient[] = 
  { 0.0, 0.0, 0.0, 1.0 };
GLfloat OpenGLinitialisation::light_diffuse[] = 
  { 1.0, 1.0, 1.0, 1.0 };
GLfloat OpenGLinitialisation::light_specular[] = 
  { 1.0, 1.0, 1.0, 1.0 };
GLfloat OpenGLinitialisation::light_position[] = 
  { 1.0, 1.0, 1.0, 0.0 };

GLfloat OpenGLinitialisation::mat_ambient[] = 
  { 0.7, 0.7, 0.7, 1.0 };
GLfloat OpenGLinitialisation::mat_diffuse[] = 
  { 0.8, 0.8, 0.8, 1.0 };
GLfloat OpenGLinitialisation::mat_specular[] = 
  { 1.0, 1.0, 1.0, 1.0 };
GLfloat OpenGLinitialisation::mat_shininess[] = 
  { 100.0 };

void OpenGLinitialisation::light0Init()
{
  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
}

void OpenGLinitialisation::materialInit()
{
  glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

OpenGLinitialisation::OpenGLinitialisation()
{
  light0Init();
  materialInit();

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  //glEnable(GL_NORMALIZE);
  glEnable(GL_COLOR_MATERIAL);
}



//  <TODO>  Go through ALL my code
//  and change the gobjMySphereDraw ordering of the
//  constructors :
//
//
//  gobjMySphereDraw
//  (
//    gobjQuadric * _d,
//    floatc _x,
//    floatc _y,
//    floatc _z
//  )
// and 
//  gobjMySphereDraw
//  (
//    gobjQuadric * _d,
//    point3<double> const & x
//  )
// and ...

//  The motivation is that it is quite possible that the
//    quadric will be and should be modified.
//    In contrast the initial point while it can be modified
//    has its own variables.  The quadric does not do its own memory
//    management, but uses the pointer passed to it.

//  The memory stategy is also unclear, if I am not going to own 
//    the object why not have d as a reference?  
//
//  PS: gobjMySphereDraw does not want to own the quadratic because it 
//    gets reused.
  
void gobjMyBitmapCharacter::draw()
{
  GOBJDEBUGCODE
  glRasterPos3f(x.x,x.y,x.z);
  uintc kmax = name.size();

  for (uint k=0; k<kmax; ++k)
    glutBitmapCharacter(font, name[k]);
}

gobjMyArrow::gobjMyArrow
(
  point3<double> const & _p0,
  point3<double> const & _p1,
  doublec _headlength,
  doublec _headwidth,
  doublec _delta
)
  : p0(_p0), p1(_p1), headlength(_headlength),
    headwidth(_headwidth), delta(_delta)
{
  assert(delta>=0.0);
  assert(delta*2.0<1.0);
  // The arrow head length must be <= the arrow length.
  double len = (p1-p0).distance();
  assert(headlength <= len*(1.0-2.0*delta));
}

void gobjMyArrow::draw()
{
  GOBJDEBUGCODE
  point3<double> p01 = p1 - p0;
  double len = p01.distance();

  if (len==0.0)
    return;

  p01 *= 1.0/len;

  // p2 is the start of the arrow.
  point3<double> p2 = p0 + p01*delta;
  // p3 is the end of the arrow.
  point3<double> p3 = p0 + p01*(1.0-delta);

  point3<double> q(-p01.y,p01.x,p01.z);
  //q.normalize();
  point3<double> p4 = p3 - p01*headlength;
  point3<double> p5 = p4 + q*headwidth;
  point3<double> p6 = p4 - q*headwidth;

  glBegin(GL_LINES);
  glVertex3f(p2.x,p2.y,p2.z);
  glVertex3f(p3.x,p3.y,p3.z);
  glEnd();

  glBegin(GL_TRIANGLES);
  glVertex3f(p3.x,p3.y,p3.z);
  glVertex3f(p5.x,p5.y,p5.z);
  glVertex3f(p6.x,p6.y,p6.z);
  glVertex3f(p6.x,p6.y,p6.z);
  glVertex3f(p5.x,p5.y,p5.z);
  glVertex3f(p3.x,p3.y,p3.z);
  glEnd();
}

 
gobjMyCircle::gobjMyCircle
(
  doublec theta0, 
  doublec theta1, 
  doublec xaxislength,
  doublec yaxislength,
  uintc _N
)
  : N(_N), ptx(new float[N]),
    pty(new float[N])
{
  //Just plot the curve 
  assert(theta1>theta0);  

  float t=theta0;
  float dt=(theta1-theta0)/(N-1);

  for (uint i=0; i<N; ++i)
  {
    ptx[i] = cos(t)*xaxislength;
    pty[i] = sin(t)*yaxislength;
    t += dt;
  }
}

gobjMyCircle::gobjMyCircle
(
  doublec theta0, 
  doublec theta1, 
  doublec radius,
  uintc _N
)
  : N(_N), ptx(new float[N]),
    pty(new float[N])
{
  assert(radius>0.0);

  float t=theta0;
  float thetadiff = theta1-theta0;
  if (thetadiff<0.0)
    thetadiff += PI*2.0;
  float dt=thetadiff/(N-1);
/*
cout << SHOW(theta1-theta0) << " " << SHOW(thetadiff);
cout << " " << SHOW(dt) << endl;
*/

/*
  if (dt<0.0)
    dt += 2.0*PI;
*/

  for (uint i=0; i<N; ++i)
  {
    ptx[i] = cos(t)*radius;
    pty[i] = sin(t)*radius;
    t += dt;
  }

/*
cout << SHOW(theta1-theta0) << " " << SHOW(dt) << endl;
cout << SHOW(theta0) << " " << SHOW(theta1) << endl;
cout << SHOW(cos(theta0)) << " " << SHOW(sin(theta0)) << endl;
cout << SHOW(radius) << endl;
cout << "start: (" << ptx[0] << "," << pty[0] << ")" << endl;
cout << "end:   (" << ptx[N-1] << "," << pty[N-1] << ")" << endl;
*/

}


gobjMyCircle::gobjMyCircle
(
  uintc _N
)
  : N(_N), ptx(new float[N]),
    pty(new float[N])
{
  float t=0.0;
  float dt=2.0*PI/(N-1);

  for (uint i=0; i<N; ++i)
  {
    ptx[i] = cos(t);
    pty[i] = sin(t);
    t += dt;
  }
}


gobjMyCircle::~gobjMyCircle()
{
  delete[] ptx; 
  delete[] pty;
}

point3<double> const gobjMyCircleDraw::zaxis(0.0,0.0,1.0);

void gobjMyCircleDraw::draw()
{
  GOBJDEBUGCODE
  uintc const N(cir.N);
  float * const ptx(cir.ptx);
  float * const pty(cir.pty);

  glPushMatrix();

  glTranslatef(center.x,center.y,center.z);

  if (axis!=zaxis)
  {
    assert(axis!=point3<double>(0,0,0));

    point3<double> N;
    crossproduct::evalxyz(N,zaxis,axis);

    double angle = acos( axis.dot(zaxis) / axis.distance() );
    angle *= 180.0/PI;
    glRotated(angle,N.x,N.y,N.z);
  }

  //glBegin(GL_LINE_LOOP);
  glBegin(GL_LINE);

  if ( (scalex==1.0)&&(scaley==1.0))
  {
    for (uint i=0; i<N-1; ++i)
    {
      glVertex2f(ptx[i],pty[i]);
      glVertex2f(ptx[i+1],pty[i+1]);
    }
  }
  else
  {
    for (uint i=0; i<N-1; ++i)
    {
      glVertex2f(ptx[i]*scalex,pty[i]*scaley);
      glVertex2f(ptx[i+1]*scalex,pty[i+1]*scaley);
    }
  }
    //glVertex2f(ptx[i]*radius,pty[i]*radius);

  glEnd();

  glPopMatrix();
}


gridsquare::gridsquare
(
  GLint factor,
  GLushort pattern,
  uintc xcolumns,
  uintc ycolumns,
  doublec xmax,
  doublec ymax
)
  : gobjContainer(true)
{
  if ((xcolumns==0)||(ycolumns==0))
    return;

  double x=0.0;
  double y=0.0;

  doublec dx = xmax / xcolumns;
  doublec dy = ymax / ycolumns;

//  doublec xmax = dx * xcolumns;
//  doublec ymax = dy * ycolumns;

  push( new gobjglEnable(GL_LINE_STIPPLE));
  push( new gobjglLineStipple(factor,pattern) );

  push(new gobjglBegin(GL_LINES));

  for (uint i=0; i<=xcolumns; ++i)
  {
    x = dx * i;
    push(new gobjglVertex2f(x,0.0));
    push(new gobjglVertex2f(x,ymax));
  }

  for (uint i=0; i<=ycolumns; ++i)
  {
    y = dy * i;
    push(new gobjglVertex2f(0.0,y));
    push(new gobjglVertex2f(xmax,y));
  }

  push(new gobjglEnd());

  push(new gobjglDisable(GL_LINE_STIPPLE));
}

void graphmisc::colornormalize
(
  point3<double> & p1, 
  point3<uint> const & p2
)
{
  p1.x = (double)p2.x / 255.0;
  p1.y = (double)p2.y / 255.0;
  p1.z = (double)p2.z / 255.0;
}

void graphmisc::colornormalize
(
  point3<double> & p1, 
  commandline & cmd, 
  stringc tag 
)
{
  point3<uint> p2;
  string p2str="";
  cmd.mapvar(p2str,tag);
  if (!p2str.empty())
  {
    p2.serializeInverseBrackets(p2str);
    colornormalize(p1,p2); 
  }
}







