/* frogger.c  game                                          */

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

static GLfloat theta[3], theta0[3] = {115.0, 0.0, 60.0};
static GLdouble m[16]; /* for unproject mouse */
static GLint axis = 2;
/* initial viewer location */
static GLdouble viewer[]= {0.0, 0.0, 10.0}; 
static int winWidth = 600;
static int winHeight = 600;
static GLfloat speed = 1.0;
static GLUquadricObj *log1, *log2, *log3, *log4, *log5;
static GLUquadricObj *end1, *end2, *frog1;
static GLfloat loglen = 4.0;
static GLfloat logr   = 1.0;
static GLfloat log1x, log1x0 = -14.0;
static GLfloat log1y, log1y0 =  7.0;
static GLfloat log2x, log2x0 = -11.0;
static GLfloat log2y, log2y0 =  3.5;
static GLfloat log3x, log3x0 = -9.0;
static GLfloat log3y, log3y0 =  0.0;
static GLfloat log4x, log4x0 = -8.0;
static GLfloat log4y, log4y0 = -3.5;
static GLfloat log5x, log5x0 = -7.0;
static GLfloat log5y, log5y0 = -7.0;
static GLfloat frogx, frogx0 =  0.0;
static GLfloat frogy, frogy0 =  11.0;
static GLfloat frogz, frogz0 = -0.75;
static int frogpos = 0;

static void init()
{
  glClearColor(1.0, 1.0, 1.0, 1.0);
  log1 = gluNewQuadric();
  log2 = gluNewQuadric();
  log3 = gluNewQuadric();
  log4 = gluNewQuadric();
  log5 = gluNewQuadric();
  end1 = gluNewQuadric();
  end2 = gluNewQuadric();
  frog1 = gluNewQuadric();
  frogx = frogx0;
  frogy = frogy0;
  frogz = frogz0;
  theta[0] = theta0[0];
  theta[1] = theta0[1];
  theta[2] = theta0[2];
  log1x = log1x0;
  log1y = log1y0;
  log2x = log2x0;
  log2y = log2y0;
  log3x = log3x0;
  log3y = log3y0;
  log4x = log4x0;
  log4y = log4y0;
  log5x = log5x0;
  log5y = log5y0;
} /* end init */

static void logs()
{
  glColor3f(0.5, 0.0, 0.5); /* brown logs */
  glPushMatrix();
    glTranslatef( log1x,  log1y, 0.0);
    glRotatef(90.0, 0.0, 1.0, 0.0);
    gluCylinder(log1, 1.0, 1.0, loglen, 12, 12);
    gluDisk(end1, 0.0, 1.0, 12, 12);
    glTranslatef(0.0, 0.0, loglen);
    gluDisk(end2, 0.0, 1.0, 12, 12);
  glPopMatrix();
  glPushMatrix();
    glTranslatef( log2x,  log2y, 0.0);
    glRotatef(90.0, 0.0, 1.0, 0.0);
    gluCylinder(log2, 1.0, 1.0, loglen, 12, 12);
    gluDisk(end1, 0.0, 1.0, 12, 12);
    glTranslatef(0.0, 0.0, loglen);
    gluDisk(end2, 0.0, 1.0, 12, 12);
  glPopMatrix();
  glPushMatrix();
    glTranslatef( log3x,  log3y, 0.0);
    glRotatef(90.0, 0.0, 1.0, 0.0);
    gluCylinder(log3, 1.0, 1.0, loglen, 12, 12);
    gluDisk(end1, 0.0, 1.0, 12, 12);
    glTranslatef(0.0, 0.0, loglen);
    gluDisk(end2, 0.0, 1.0, 12, 12);
  glPopMatrix();
  glPushMatrix();
    glTranslatef( log4x,  log4y, 0.0);
    glRotatef(90.0, 0.0, 1.0, 0.0);
    gluCylinder(log4, 1.0, 1.0, loglen, 12, 12);
    gluDisk(end1, 0.0, 1.0, 12, 12);
    glTranslatef(0.0, 0.0, loglen);
    gluDisk(end2, 0.0, 1.0, 12, 12);
  glPopMatrix();
  glPushMatrix();
    glTranslatef( log5x,  log5y, 0.0);
    glRotatef(90.0, 0.0, 1.0, 0.0);
    gluCylinder(log5, 1.0, 1.0, loglen, 12, 12);
    gluDisk(end1, 0.0, 1.0, 12, 12);
    glTranslatef(0.0, 0.0, loglen);
    gluDisk(end2, 0.0, 1.0, 12, 12);
  glPopMatrix();

  glColor3f(0.0, 0.0, 1.0); /* blue water*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0, -10.0, 0.0);
    glVertex3f(-15.0,  10.0, 0.0);
    glVertex3f( 15.0,  10.0, 0.0);
    glVertex3f( 15.0, -10.0, 0.0);
  glEnd();
  glColor3f(0.4, 0.4, 1.0); /* light blue water*/
  glBegin(GL_POLYGON);
    glVertex3f( 15.0, -10.0, 4.0);
    glVertex3f( 15.0,  10.0, 4.0);
    glVertex3f( 15.0,  10.0, 0.0);
    glVertex3f( 15.0, -10.0, 0.0);
  glEnd();
  glColor3f(0.4, 0.4, 1.0); /* upper blue water*/
  glBegin(GL_POLYGON);
    glVertex3f( -15.0, -10.0, -4.0);
    glVertex3f( -15.0,  10.0, -4.0);
    glVertex3f( -15.0,  10.0, 0.0);
    glVertex3f( -15.0, -10.0, 0.0);
  glEnd();
  glColor3f(0.0, 1.0, 0.0); /* green grass*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0,  -12.0, -0.5);
    glVertex3f(-15.0,  -10.0, -0.5);
    glVertex3f( 15.0,  -10.0, -0.5);
    glVertex3f( 15.0,  -12.0, -0.5);
  glEnd();
  glColor3f(0.0, 0.95, 0.0); /* green grass edge*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0,  -10.0, -0.5);
    glVertex3f(-15.0,  -10.0,  0.0);
    glVertex3f( 15.0,  -10.0,  0.0);
    glVertex3f( 15.0,  -10.0, -0.5);
  glEnd();
  glColor3f(0.0, 0.90, 0.0); /* lower green grass*/
  glBegin(GL_POLYGON);
    glVertex3f( 15.0, -12.0,  4.0);
    glVertex3f( 15.0, -12.0, -0.5);
    glVertex3f( 15.0, -10.0, -0.5);
    glVertex3f( 15.0, -10.0,  4.0);
  glEnd();
  glColor3f(0.0, 0.90, 0.0); /* upper green grass*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0, -12.0, -4.0);
    glVertex3f(-15.0, -12.0, -0.5);
    glVertex3f(-15.0, -10.0, -0.5);
    glVertex3f(-15.0, -10.0, -4.0);
  glEnd();
  glColor3f(1.0, 1.0, 0.0); /* yellow sand*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0,  12.0, -0.5);
    glVertex3f(-15.0,  10.0, -0.5);
    glVertex3f( 15.0,  10.0, -0.5);
    glVertex3f( 15.0,  12.0, -0.5);
  glEnd();
  glColor3f(0.95, 0.95, 0.0); /* yellow sand edge*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0, 10.0, -0.5);
    glVertex3f(-15.0, 10.0,  0.0);
    glVertex3f( 15.0, 10.0,  0.0);
    glVertex3f( 15.0, 10.0, -0.5);
  glEnd();
  glColor3f(0.90, 0.90, 0.0); /* lower yellow sand*/
  glBegin(GL_POLYGON);
    glVertex3f( 15.0, 12.0,  4.0);
    glVertex3f( 15.0, 12.0, -0.5);
    glVertex3f( 15.0, 10.0, -0.5);
    glVertex3f( 15.0, 10.0,  4.0);
  glEnd();
  glColor3f(0.90, 0.90, 0.0); /* upper yellow sand*/
  glBegin(GL_POLYGON);
    glVertex3f(-15.0, 12.0, -4.0);
    glVertex3f(-15.0, 12.0, -0.5);
    glVertex3f(-15.0, 10.0, -0.5);
    glVertex3f(-15.0, 10.0, -4.0);
  glEnd();
} /* end logs */

static void frog()
{
  glColor3f(0.0, 0.7, 0.0); /* frog*/
  glPushMatrix();
    glTranslatef( frogx,  frogy, frogz);
    gluSphere(frog1, 0.5, 12, 12);
  glPopMatrix();
} /* end frog */

static void gator()
{
} /* end gator */

static void drawText(void)
{
  char  msg1[] = "'XxYyZz' for axis rotation    'F' or 'f' for speed";
  char  msg2[] = "thX=         thY=         thZ=           ";
  char  msg3[] = "mouse button for frog to jump, 'r' for reset";
  int   i, len;

  /* Draw text */
  sprintf(&msg2[5],"%4.0f", theta[0]);
  msg2[9] = ',';
  sprintf(&msg2[17],"%4.0f", theta[1]);
  msg2[21] = ',';
  sprintf(&msg2[29],"%4.0f", theta[2]);
  msg2[33] = ',';
  glEnable(GL_LINE_SMOOTH);
  /* glBlendFunc(GL_ONE, GL_ONE); */
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
    glLoadIdentity();
    glOrtho(0, winWidth, 0, winHeight, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
      glLoadIdentity();
      glColor4f(0.0, 0.0, 1.0, 1.0);
      glRasterPos2i(10, 10);
      len = strlen(msg1);
      for (i=0; i<len; i++)
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, msg1[i]);
      glRasterPos2i(10, 25);
      len = strlen(msg2);
      for (i=0; i<len; i++)
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, msg2[i]);
      glRasterPos2i(10, 40);
      len = strlen(msg3);
      for (i=0; i<len; i++)
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, msg3[i]);
      glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
} /* end drawText */


static void display(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  /* Update viewer position in modelview matrix */
  glLoadIdentity();
  /* rotate scene */
  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);
  glGetDoublev(GL_MODELVIEW_MATRIX, m); /* save transformation for mouse */
  logs();
  frog();
  gator();
  drawText();
  glFlush();
  glutSwapBuffers();
} /* end display */

static void move_logs(void)
{
  GLfloat base = 0.04;
  /* Idle callback, move logs at various speeds, random gator */
  log1x = log1x + base*speed;
  if(log1x > 13.0) {log1x = log1x - 31.0; base = base*1.5;}
  if(frogpos == 1) frogx = frogx + base*speed;
  log2x = log2x + 0.95*base*speed;
  if(log2x > 13.0) log2x = log2x - 31.0;
  if(frogpos == 2) frogx = frogx + 0.95*base*speed;
  log3x = log3x + 0.9*base*speed;
  if(log3x > 13.0) log3x = log3x - 31.0;
  if(frogpos == 3) frogx = frogx + 0.9*base*speed;
  log4x = log4x + 0.85*base*speed;
  if(log4x > 13.0) log4x = log4x - 31.0;
  if(frogpos == 4) frogx = frogx + 0.85*base*speed;
  log5x = log5x + 0.8*base*speed;
  if(log5x > 13.0) log5x = log5x - 31.0;
  if(frogpos == 5) frogx = frogx + 0.8*base*speed;

  if(frogx > 15.0) /* reset to start */
  {
    frogpos = 0;
    frogx = frogx0;
    frogy = frogy0;
    frogz = frogz0;
  }
  glutPostRedisplay();
} /* end move_logs */


static GLdouble dist(GLdouble a1x, GLdouble a1y, GLdouble a1z,
                     GLdouble a2x, GLdouble a2y, GLdouble a2z,
                     GLdouble a3x, GLdouble a3y, GLdouble a3z)
{
  GLdouble d, ax, ay, az, bx, by, bz, cx, cy, cz;
  
  ax = a2x-a1x;      ay = a2y-a1y;      az = a2z-a1z;
  bx = a1x-a3x;      by = a1y-a3y;      bz = a1z-a3z;
  cx = ay*bz-az*by;  cy = ax*bz-az*bx;  cz = ax*by-ay*bx;
  d =  sqrt(cx*cx+cy*cy+cz*cz)/sqrt(ax*ax+ay*ay+az*az);
  return d;
} /* end dist */

static GLdouble dist2(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2)
{
  return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
} /* end dist2 */

/* When the left mouse button is pressed, the x,y mouse position is used */
/* to find two 3D points, one at near, one at far. The line joining these */
/* points is used to find the closest world point. */
static void mouse(int button, int state, int x, int y) 
{
   GLint viewport[4];
   GLdouble projmatrix[16]; /* modelview matrix m is global */
   GLint realy;  /*  OpenGL y coordinate position  */
   GLdouble wx0, wy0, wz0;  /*  returned world x, y, z coords at near */
   GLdouble wx1, wy1, wz1;  /*  returned world x, y, z coords at far */
   GLdouble dlog, dfrog;
   GLdouble jump = 7.0;
   int i;

   if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
   {
     glGetIntegerv (GL_VIEWPORT, viewport);
     glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
     /*  note viewport[3] is height of window in pixels  */
     realy = viewport[3] - (GLint) y - 1;
     gluUnProject((GLdouble) x, (GLdouble) realy, 0.0, 
                  m, projmatrix, viewport, &wx0, &wy0, &wz0); 
     gluUnProject((GLdouble) x, (GLdouble) realy, 1.0, 
                  m, projmatrix, viewport, &wx1, &wy1, &wz1); 
     if(frogpos == 6)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, frogx, frogy, frogz);
       if(dlog < 2.0)
       {
         frogpos = 0;
         frogx = frogx0;
         frogy = frogy0;
         frogz = frogz0;
       }
     }
     if(frogpos == 5)
     {
       frogpos = 6;
       /* frogx = frogx0; */
       frogy = -frogy0;
       frogz = frogz0;
     }
     if(frogpos == 4)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, log5x+loglen/2.0, log5y, 0.0);
       dfrog = dist2(frogx, frogy, log5x+loglen/2.0, log5y);
       printf("mouse to log %d is %g, to frog is %g \n", 1, dlog, dfrog);
       if(dlog < 2.0 && dfrog < jump)
       {
         frogx = log5x+loglen/2.0;
         frogy = log5y;
         frogz = -1.5;
         frogpos = 5;
       }
     }
     if(frogpos == 3)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, log4x+loglen/2.0, log4y, 0.0);
       dfrog = dist2(frogx, frogy, log4x+loglen/2.0, log4y);
       printf("mouse to log %d is %g, to frog is %g \n", 4, dlog, dfrog);
       if(dlog < 2.0 && dfrog < jump)
       {
         frogx = log4x+loglen/2.0;
         frogy = log4y;
         frogz = -1.5;
         frogpos = 4;
       }
     }
     if(frogpos == 2)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, log3x+loglen/2.0, log3y, 0.0);
       dfrog = dist2(frogx, frogy, log3x+loglen/2.0, log3y);
       printf("mouse to log %d is %g, to frog is %g \n", 3, dlog, dfrog);
       if(dlog < 2.0 && dfrog < jump)
       {
         frogx = log3x+loglen/2.0;
         frogy = log3y;
         frogz = -1.5;
         frogpos = 3;
       }
     }
     if(frogpos == 1)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, log2x+loglen/2.0, log2y, 0.0);
       dfrog = dist2(frogx, frogy, log2x+loglen/2.0, log2y);
       printf("mouse to log %d is %g, to frog is %g \n", 2, dlog, dfrog);
       if(dlog < 2.0 && dfrog < jump)
       {
         frogx = log2x+loglen/2.0;
         frogy = log2y;
         frogz = -1.5;
         frogpos = 2;
       }
     }
     if(frogpos == 0)
     {
       dlog = dist(wx0, wy0, wz0, wx1, wy1, wz1, log1x+loglen/2.0, log1y, 0.0);
       dfrog = dist2(frogx, frogy, log1x+loglen/2.0, log1y);
       printf("mouse to log %d is %g, to frog is %g \n", 1, dlog, dfrog);
       if(dlog < 2.0 && dfrog < jump)
       {
         frogx = log1x+loglen/2.0;
         frogy = log1y;
         frogz = -1.5;
         frogpos = 1;
       }
     }
  }
} /* end mouse */

static void keys(unsigned char key, int x, int y)
{
  /* Use x, X, y, Y, z, and Z keys to move viewer */
  if(key == 'x') theta[0]-= 5.0;
  if(key == 'X') theta[0]+= 5.0;
  if(key == 'y') theta[1]-= 5.0;
  if(key == 'Y') theta[1]+= 5.0;
  if(key == 'z') theta[2]-= 5.0;
  if(key == 'Z') theta[2]+= 5.0;
  if(key == 'l') log1x = log1x+0.5;
  if(key == 'm') log1y = log1y+0.5;
  if(key == 'f') speed = speed*0.75;
  if(key == 'F') speed = speed*1.25;
  if(key == 'r') init(); /* reset */
  glutPostRedisplay();
} /* end keys */

static void myReshape(int w, int h)
{
  winWidth = w;
  winHeight = h;
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(w <= h)
      glOrtho(-15.0, 15.0, -15.0 * (GLfloat)h / (GLfloat)w,
              15.0 * (GLfloat)h / (GLfloat)w, -24.0, 24.0);
  else
      glOrtho(-15.0 * (GLfloat)w / (GLfloat)h,
              15.0 * (GLfloat)w / (GLfloat)h, -15.0, 15.0, -24.0, 24.0);
  glMatrixMode(GL_MODELVIEW);
} /* end myReshape */

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(winWidth, winHeight);
  glutCreateWindow(argv[0]);
  glutReshapeFunc(myReshape);
  glutDisplayFunc(display);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);
  glutIdleFunc(move_logs);
  glEnable(GL_DEPTH_TEST);
  init();
  glutMainLoop();
  return 0;
} /* end main of frogger.c */


