/* racegl.c   race track, crude car */

#include <GL/glut.h>
#include <stdio.h>
#include <math.h>

static GLfloat track[100][6][3];
static GLfloat car[3] = {5.0, 0.0, 0.0};
static GLfloat lane = 0.6;
static GLfloat theta[3] = {-65.0, 10.0, 30.0};
static int n = 1;
static int m = 6;
static int width=600;
static int height=600;
static double Pi = 3.14159265358979323846;
static double r = 5.0;   /* turn radius */
static double lw = 0.3; /* lane width  */
static double hs = 10.0; /* half straightaway */
static double incline = 0.1; /* turn incline */
static int wire = 0;
static double move = 0.005;

void nexti(int i, int j, float dx, float dy, float dz)
{
  track[i][j][0] = track[i-1][j][0] + dx;
  track[i][j][1] = track[i-1][j][1] + dy;
  track[i][j][2] = track[i-1][j][2] + dz;
} /* end nexti */

void nextj(int i, int j, float dx, float dy, float dz)
{
  track[i][j][0] = track[i][j-1][0] + dx;
  track[i][j][1] = track[i][j-1][1] + dy;
  track[i][j][2] = track[i][j-1][2] + dz;
} /* end nextj */

void build_track()
{
  int i, j, k;
  double ang, dang=Pi/24.0;
  int nang = 23; /* minus 1 */
  float dx, dy, dz, rt;
  float  x, y, z;
  
  i=0;
  j=0;
  track[i][j][0] = -r; /* x */
  track[i][j][1] = -hs; /* y */
  track[i][j][2] =  0.0; /* z  left side botton */
  for(j=1; j<m; j++) nextj(i,j,-lw, 0.0, incline);
  i++;
  for(j=0; j<m; j++) nexti(i,j, 0.0,  hs, -incline);
  i++;
  for(j=0; j<m; j++) nexti(i,j, 0.0,  hs, incline);
  i++;
  ang = dang;
  for(k=0; k<nang; k++)
  {
    rt = r;
    z = 0.0;
    for(j=0; j<m; j++)
    {
      x = -rt*cos(ang);
      y = rt*sin(ang)+hs;
      track[i][j][0] = x;
      track[i][j][1] = y;
      track[i][j][2] = z; /* top turn */
      /* printf("track[%d][%d] x=%6.3f, y=%6.3f, z=%6.3f \n",
             i, j, track[i][j][0], track[i][j][1], track[i][j][2]); */
      z = z + incline;
      rt = rt+lw;
    }
    i++;
    ang = ang+dang;
  }
  j = 0;
  track[i][j][0] = r; /* x */
  track[i][j][1] = hs; /* y */
  track[i][j][2] = 0.0; /* z  right side top */
  printf("track[%d][%d] x=%6.3f, y=%6.3f, z=%6.3f \n",
         i, j, track[i][j][0], track[i][j][1], track[i][j][2]);
  for(j=1; j<m; j++) nextj(i,j, lw, 0.0, incline);
  i++;
  for(j=0; j<m; j++) nexti(i,j, 0.0,  -hs, -incline);
  i++;
  for(j=0; j<m; j++) nexti(i,j, 0.0,  -hs, incline);
  i++;
  ang = dang;
  for(k=0; k<=nang; k++) /* must close */
  {
    rt = r;
    z = 0.0;
    for(j=0; j<m; j++)
    {
      x = rt*cos(ang);
      y = -(rt*sin(ang)+hs);
      track[i][j][0] = x;
      track[i][j][1] = y;
      track[i][j][2] = z; /* top turn */
      /* printf("track[%d][%d] x=%6.3f, y=%6.3f, z=%6.3f \n",
             i, j, track[i][j][0], track[i][j][1], track[i][j][2]); */
      z = z + incline;
      rt = rt+lw;
    }
    i++;
    ang = ang+dang;
  }
  j = 0;
  n = i;  
} /* end build_track */

void display(void)
{
  char text[]="press mouse for menu";
  char *p;
  int i, j;
  
  /* clear window */
  glClear(GL_COLOR_BUFFER_BIT); 
  glLoadIdentity ();
  glRotatef(theta[0], 1.0, 0.0, 0.0);
  glRotatef(theta[1], 0.0, 1.0, 0.0);
  glRotatef(theta[2], 0.0, 0.0, 1.0);
  glColor3f(0.0, 0.0, 0.0);
  
  /* draw track */
  if(wire)
  {
    for(i=0; i<n; i++)
    {
      for(j=0; j<m; j++)
      {
        glBegin(GL_LINES);
		  if(j+1<m)
		  {
            glVertex3fv(track[i][j]);
            glVertex3fv(track[i][j+1]);
		  }
		  if(i+1<n)
		  {
            glVertex3fv(track[i][j]);
            glVertex3fv(track[i+1][j]);
		  }
        glEnd();
      }
    }
  }
  else
  {
    for(i=0; i<n-1; i++)
    {
      for(j=0; j<m-1; j++)
      {
        glBegin(GL_QUADS);
          glVertex3fv(track[i][j]);
          glVertex3fv(track[i][j+1]);
          glVertex3fv(track[i+1][j+1]);
          glVertex3fv(track[i+1][j]);
        glEnd();
      }
    }
  }
 
  /* Draw the tires */
  glColor3f(0.8, 0.8, 0.8);
  glPointSize(8.0);
  glBegin(GL_POINTS);
      glVertex3f(car[0]+0.1, car[1]+0.4, car[2]+0.25);
      glVertex3f(car[0]-0.1, car[1]+0.4, car[2]+0.25);
      glVertex3f(car[0]+0.1, car[1]-0.4, car[2]+0.25);
      glVertex3f(car[0]-0.1, car[1]-0.4, car[2]+0.25);
  glEnd();
  
  glFlush(); 
  glutSwapBuffers();
}

/* This routine handels mouse events */
static void mouse(int button, int state, int x, int y)
{
  glutPostRedisplay();
} /* end mouse */

static void keyboard(unsigned char key, int x, int y)
{
  switch (key)
  {
    case 'W':
      wire=1;
      break;
    case 'w':
      wire=0;
      break;
    case 'F':
      move = move*1.414;
      break;
    case 'f':
      move = move*0.707;
      break;
   }
} /* end keyboard */

static void special(int k, int x, int y)
{
  switch(k)
  {
    case GLUT_KEY_LEFT:

      break;
    case GLUT_KEY_RIGHT:

      break;
    case GLUT_KEY_DOWN:

      break;
    case GLUT_KEY_UP:
      wire = 1-wire;
      break;
  }
} /* end special */

/* This routine handles window resizes */
void reshape(int w, int h)
{
  width = w;
  height = h;
  /* Set the transformations */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-20.0, 20.0, -20.0, 20.0, -20.0, 20.0);
  glMatrixMode(GL_MODELVIEW);
  glViewport(0, 0, w, h);
} /* end reshape */

static void drive(void)
{
  double cx, cy, cz;
  double dx, dy, dz, rt;
  
  /* Idle callback, drive car */
  cx = car[0];
  cy = car[1];
  cz = car[2];
  /* determine section */
  if(cx>0.0 && cy>=-hs && cy<=hs) /* going straight, +y */
  {
    car[1] = car[1] + move;
    if(car[0]>r+(m-1)*lw) car[0] = r+(m-1)*lw;
    if(car[0]<r) car[0]=r;
  }
  if(cx<0.0 && cy>=-hs && cy<=hs) /* going straight, -y */
  {
    car[1] = car[1] - move;
    if(car[0]<-(r+(m-1)*lw)) car[0] = -(r+(m-1)*lw);
    if(car[0]>-r) car[0]=-r;
  }
  if(cy>hs) /* on top */
  {
	dx = cx;
	dy = cy-hs;
    rt = sqrt(dx*dx+dy*dy);
    dx = dx/rt;
    dy = dy/rt;
    car[0] = car[0] - move*dy;
    car[1] = car[1] + move*dx;
  }
  if(cy<-hs) /* on bottom */
  {
	  dx = cx;
	  dy = cy+hs;
	  rt = sqrt(dx*dx+dy*dy);
      dx = dx/rt;
      dy = dy/rt;
      car[0] = car[0] - move*dy;
      car[1] = car[1] + move*dx;
  }
  
  glutPostRedisplay();
} /* end drive */

void init()
{
  /* set clear color to white */
  glClearColor (1.0, 1.0, 1.0, 0.0);
  /* set fill  color to black */
  glColor3f(0.0, 0.0, 0.0);

  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrtho(-20.0, 20.0, -20.0, 20.0, -20.0, 20.0);
  glMatrixMode (GL_MODELVIEW);
  glViewport(0, 0, width, height);
  build_track();
  car[0] = car[0] + lane;
} /* end init */

int main(int argc, char* argv[])
{
  glutInit(&argc,argv);
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB );  
  glutInitWindowSize(width, height);
  glutInitWindowPosition(100,100); 
  
  glutCreateWindow(argv[0]); 
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutIdleFunc(drive);
  glutKeyboardFunc(keyboard);
  glutSpecialFunc(special);
  /* glEnable(GL_DEPTH_TEST); */
  init();
  glutMainLoop();
  return 0;
} /* end main of racegl.c */

