//
// Bouncing ball demo. 
//
//
// Original: Brian Paul
// Conversion to GLUT by Mark J. Kilgard 
//

// Modified for use in cs4731 by Mark R. Stevens (February 12, 2000)
// contact: stevensm@cs.wpi.edu with comments / concerns
// 
// Additional Modifications for windows port
//     Paul Tessier
//
// BUG FIXES: 
//     Anthony John Andrade <tony@WPI.EDU>
//
// C includes
#include <math.h>

// C++ includes
#include <iostream>

// Paul Tessier <phernost@WPI.EDU>: Windows fix 
using namespace std;

// The local includes
#include "minigl.h"
#include "miniglut.h"
#include "bounce.h"

// for simplicity
#define COS(X)   cos( (X) * 3.14159/180.0 )
#define SIN(X)   sin( (X) * 3.14159/180.0 )  
                                                
//--------------------------------------------------------------------------------
void bounce::usage() {
  int i;
  // tell the user what the keys do to the gears
  cout << "\nGears Usage:\n";
  cout << "\tKeys:\n";
  cout << "\t\tb\t\tbounce the ball\n";
  cout << "\t\tescape\t\texit\n";
  cout << "\tMouse:\n";
  cout << "\t\tNone\n";

}
//--------------------------------------------------------------------------------

//--------------------------------------------------------------------------------
void bounce::init(minigl &mgl, miniglut &mglut) {

  // set up the defaults
  mgl.mglCullFace  (minigl::MGL_BACK);
  mgl.mglEnable    (minigl::MGL_CULL_FACE);

  // set the defaults
  Zrot  = 0.0;
  Zstep = 6.0;
  Xpos  = 0.0;
  Ypos  = 1.0;
  Xvel  = 0.2;
  Yvel  = 0.0;
  Xmin  = -4.0;
  Xmax  = 4.0;
  Ymin  = -3.8;
  Ymax  = 4.0;
  G     = -0.1;                

  // The timings 
  T0 = 0;
  Frames = 0;

}
//--------------------------------------------------------------------------------

//--------------------------------------------------------------------------------
void bounce::reshape(minigl &mgl, miniglut &mglut, int width, int height)
{

  // compute the aspect
  float aspect = (float) width / (float) height;

  // set up the viewport
  mgl.mglViewport(0, 0, width, height);

  // store a new perspective matrix
  mgl.mglMatrixMode(minigl::MGL_PROJECTION);
  mgl.mglLoadIdentity();
  mgl.mglOrtho(-6.0 * aspect, 6.0 * aspect, -6.0, 6.0, -6.0, 6.0);

  // return the the modelview stack for later drawing
  mgl.mglMatrixMode(minigl::MGL_MODELVIEW);

}
//--------------------------------------------------------------------------------

//--------------------------------------------------------------------------------
void bounce::special(minigl &mgl, miniglut &mglut, int k, int x, int y) {

}
//--------------------------------------------------------------------------------

//--------------------------------------------------------------------------------
void bounce::key    (minigl &mgl, miniglut &mglut, unsigned char k, int x, int y) {
  
  // based on the key, update the rotation
  switch (k) {
  case 'b':
    break;
  case 27:  /* Escape */
    std::exit(0);
    break;
  default:
    return;
  }

  // update the attributes of the ball
  static float vel0 = -100.0;
  Zrot += Zstep;
  Xpos += Xvel;
  if (Xpos >= Xmax) {
    Xpos = Xmax;
    Xvel = -Xvel;
    Zstep = -Zstep;
  }
  if (Xpos <= Xmin) {
    Xpos = Xmin;
    Xvel = -Xvel;
    Zstep = -Zstep;
  }
  Ypos += Yvel;
  Yvel += G;
  if (Ypos < Ymin) {
    Ypos = Ymin;
    if (vel0 == -100.0)
      vel0 = fabs(Yvel);
    Yvel = vel0;
  }

  // update the screen
  mglut.mglutPostRedisplay();  
                                                                      
}
//--------------------------------------------------------------------------------

  // receive mouse events
void bounce::mouse (minigl &mgl, miniglut &mglut, int button, int state, int x, int y){

	mglut.mglutPostRedisplay( );
}

//--------------------------------------------------------------------------------
void bounce::drawBall(minigl &mgl)
{
  
  // used in computing ball attributes
  float da = 18.0, db = 18.0;
  float radius = 1.0;
  float x, y, z;

  // loop and create a row of q
  bool color = false;
  for (float a = -90.0; a + da <= 90.0; a += da) {

    mgl.mglBegin(minigl::MGL_QUAD_STRIP);
    for (float b = 0.0; b <= 360.0; b += db) {

      // set the color
      if (color) mgl.mglColor3f(1, 0, 0);
      else       mgl.mglColor3f(1, 1, 1);

      // compute the first vertex position
      // BUG FIX: Anthony John Andrade <tony@WPI.EDU>
      x = radius * COS(b) * COS(a);
      y = radius * SIN(b) * COS(a);
      z = radius * SIN(a);
      mgl.mglVertex3f(x, y, z);

      // compute the second vertex position
      x = radius * COS(b) * COS(a + da);
      y = radius * SIN(b) * COS(a + da);
      z = radius * SIN(a + da);
      mgl.mglVertex3f(x, y, z);

      // toggle the color
      color = ! color;
    }
    
    // end the current strip
    mgl.mglEnd();

  }

}
//--------------------------------------------------------------------------------

//--------------------------------------------------------------------------------
void bounce::draw(minigl &mgl, miniglut &mglut) {

  // clear the screen
  mgl.mglClear(minigl::MGL_COLOR_BUFFER_BIT);

  // set up the matrix for drawing the ball
  mgl.mglPushMatrix();
  mgl.mglTranslatef(Xpos, Ypos, 0.0);
  mgl.mglScalef(2.0, 2.0, 2.0);
  mgl.mglRotatef(8.0, 0.0, 0.0, 1.0);
  mgl.mglRotatef(90.0, 1.0, 0.0, 0.0);
  mgl.mglRotatef(Zrot, 0.0, 0.0, 1.0);

  // draw the ball
  drawBall(mgl);

  // restore the matrix
  mgl.mglPopMatrix();

  // finish rendering
  mgl.mglFinish();

  // upate the screen
  mglut.mglutSwapBuffers();

  // count frames and output the frame rate
  Frames++;
  int t = mglut.mglutGet(miniglut::MGLUT_ELAPSED_TIME);
  if (t - T0 >= 5000) {
    float seconds = (t - T0) / 1000.0;
    float fps = Frames / seconds;
    cout << "\t" << Frames << " frames in ";
    cout << seconds << " seconds = " << fps << " fps\n";
    T0 = t;
    Frames = 0;
  }

}
//--------------------------------------------------------------------------------
