/***************************************************************************
                          scene.c  -  scene specific code,
                             -------------------
    begin                : Mon Apr 14 2003
    copyright            : (C) 2003 by Nick Winters
    email                : n_winters99 yahoo.com
    notes                : Look at function sceneMain for the scene main loop
 ***************************************************************************/
#include "includes.h"
/* to be implemented, the actual gameplay...
*/
// some stuff
static void drawScene(void);
static void sceneUnload(void);

struct dynamicObject {
  vec3 location;
  vec3 initLoc;
  vec3 velocity;
  float radius;
  float mass;
  GLint dispList;
  int inPlay;
};
typedef   struct dynamicObject    dynamicObject;

///////////// scene-specific variables /////////////

static int numPins = 0;
static dynamicObject *pins; // dynamically allocated array of pins
static dynamicObject ball;

static int remainingPins;

static collisionFace *firstCollisionFace;

static float bgRed=0.5f, bgGreen=0.5f, bgBlue=0.5f;
static float txtRed=0.5f, txtGreen=0.5f, txtBlue=0.5f;

static GLint worldDispList;
static float worldLowerBound = -100.0;

static vec3 cameraLocation;
static vec3 cameraInitLoc;
static vec3 cameraVelocity; // more used for where the camera points
static float cameraSpeed = 20; // how quickly does the camera point at things?

static float deltaTime; // screen refresh delta time
static float gameDeltaTime; // zero if paused

//enum yesNo isPaused = yes;
static int isPaused = 1;
static float pausedTimer = 0.0;
/*
  pausedTimer is kind of interesting...
  it must be greater than zero to pause
  it must be greater than some number to unpause
  (see the if statements in sceneMain() for these numbers)
  it is set to a negative number when unpaused (though will not stay negative)
  to zero when paused,
  and incremented by deltaTime when paused

  this accomplishes:
  holding the pause toggle will not flip the pause state every frame
  */

static int limitFrames = 0; // to be removed?


// some scene-specific vars that don't _need_ to be global to the file,
// but it makes debugging easier
static float mouseMotionTime;     // the time it took for the user to throw the ball
static float throwTimeRemaining;  // the time remaining in THIS throw of the ball

static int totalXChange; // will be cast to something else later..., as a short there will be fewer casts
static int totalYChange;

static unsigned short mousex, mousey; // no useful purpose, but used for debug

static float ysensitivity = 0.02f; // in the future, possibly setable by function
static float xsensitivity = 0.02f;

enum e_gameState gameState;

static int showGLPointer = 0;
////////////////// begin functions //////////////////
void showMyGLPointer(int newState) {
	// change the showGLPointer value to newState
	showGLPointer = newState;
}

// free scene resources
static void sceneUnload() {
  int i;
  printf("unloading scene...\n");

  if (firstCollisionFace) freeCollisionFaces(firstCollisionFace);
  firstCollisionFace = NULL;

  if (glIsList(ball.dispList)) glDeleteLists(ball.dispList, 1);
  
  if (pins) {
    for (i =0; i<numPins; i++){
      if (glIsList(pins[i].dispList)) glDeleteLists(pins[i].dispList, 1);
    }
  }
	free(pins);
  pins = NULL;

  if (glIsList(worldDispList)) glDeleteLists(worldDispList, 1);
}

//////////////////////////// scene initalization //////////////////////////// 

void sceneReshape(int w, int h) {
  /* should do what is necescary on the creation of a window

   * or on it's resize
   */
  glViewport (0, 0, (GLsizei) w, (GLsizei) h);

  // set up the perspective
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  // degrees, aspect (w/h), near, far
  gluPerspective(60.0f,(GLfloat)w/(GLfloat)h,0.09f,100.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //drawScene();
}
enum condition sceneLoad(char *fName) {
  // this should load the scene in fname
  // and initalize opengl
  // it will likely only be called once
  int status, numValues, i;
  char key[15], aFileName[MAX_FILENAME_LENGTH];

  FILE *f;
  
  f = fopen(fName, "r");
  
  if (!f) {
    printf("Unable to open file: %s for scene loading\n", fName);
	return failure;
  } else {
    // load the file
    
    // background color
    numValues = fscanf(f, "%s [%f, %f, %f]", key, &bgRed, &bgGreen, &bgBlue);
    if (numValues < 4) numValues = 0;  // there was an error
    ////printf("%d / 4\n", numValues);

    // text color
    numValues = fscanf(f, "%s [%f, %f, %f]", key, &txtRed, &txtGreen, &txtBlue);
    if (numValues < 4) numValues = 0;  // there was an error
    ////printf("%d / 4\n", numValues);

    if (numValues) numValues = fscanf(f, "%s %f", key, &worldLowerBound);
    if (numValues < 2) numValues = 0;
    ////printf("%d / 2\n", numValues);
    
    // world mesh
    if (numValues) numValues = fscanf(f, "%s %"MAX_FILENAME_LENGTH_AS_STRING"s", key, aFileName);
    ////printf("%d / 2\n", numValues);
    if (numValues < 2) {
      numValues = 0;  // there was an error
    } else {
      if (aFileName) {
        worldDispList = glGenLists(1);
        glNewList(worldDispList, GL_COMPILE);
          //status = loadNdDraw("/home/nicholas/Plane.trx");
          //status = loadNdDraw("/home/nicholas/OddColoredShapeWarped.trx");
          status = loadNdDraw(aFileName);
        glEndList();
        //printf("lodNdDraw returned status was %d\n", status);
        if (status == 0) numValues = 0; // bad things have happened
      }
    }
    
    // collision mesh
    if (numValues) numValues = fscanf(f, "%s %"MAX_FILENAME_LENGTH_AS_STRING"s", key, aFileName); 
    if (numValues < 2) {
      numValues = 0;  // there was an error
    } else {
      ////printf("%d / 2\n", numValues);
      if (aFileName) {
        firstCollisionFace = loadCollisionFaces(aFileName);
        if (firstCollisionFace) {
          //printf("success loading collision mesh\n");
          ;
        } else {
          numValues = 0;
        }
      }
    }


    // camera location
    if (numValues) numValues = fscanf(f, "%s [%f, %f, %f]", key, &cameraLocation.x, &cameraLocation.y, &cameraLocation.z); 
    ////printf("%d / 4\n", numValues);
    if (numValues < 4) numValues = 0;  // there was an error

    // gravity?

    // number of pins
    if (numValues) numValues = fscanf(f, "%s %d", key, &numPins);
    ////printf("%d / 2\n", numValues);
    if (numValues < 2) numValues = 0;  // there was an error
    ////printf("%d pins\n", numPins);
    remainingPins = numPins;
    
    /////////////////////////////////////////////////////////////

    // ball location

    if (numValues) numValues = fscanf(f, "%s [%f, %f, %f]", key, &ball.initLoc.x, &ball.initLoc.y, &ball.initLoc.z);
    ////printf("%d / 4\n", numValues);
    if (numValues < 4) {
      numValues = 0;  // there was an error
    } else {
      // will be changed in the future...
      ball.location.x = ball.initLoc.x;
      ball.location.y = ball.initLoc.y;
      ball.location.z = ball.initLoc.z;
    }

    ball.velocity.x = 0;
    ball.velocity.y = 0;
    ball.velocity.z = 0;
    ball.inPlay = 1; // will this be used?

    // ball radius
    if (numValues) numValues = fscanf(f, "%s %f", key, &ball.radius);
    ////printf("%d / 2\n", numValues);
    if (numValues < 2) numValues = 0;  // there was an error

    // ball mass
    if (numValues) numValues = fscanf(f, "%s %f", key, &ball.mass);
    ////printf("%d / 2\n", numValues);
    if (numValues < 2) numValues = 0;  // there was an error

    // ball mesh
    if (numValues) numValues = fscanf(f, "%s %"MAX_FILENAME_LENGTH_AS_STRING"s", key, aFileName);
    ////printf("%d / 2\n", numValues);
    if (numValues < 2) numValues = 0;  // there was an error
    else {
      ball.dispList = glGenLists(1);
      glNewList(ball.dispList, GL_COMPILE);
      status = loadNdDraw(aFileName);

      glEndList();
      //printf("loading ball mesh returned status %d\n", status);
      if (status == 0) numValues = 0; // bad things have happened
    }
        
    
    // up to this point numValues being set to zero means failure
    if (numValues == 0) {
	  printf("error in scene file somewhere prior to pin specific information\n");

	  return failure;
		sceneUnload();
	}
    else if (numPins>0) {
      i = 0;
      // allocate memory...
			pins = (dynamicObject*)malloc(numPins * sizeof(struct dynamicObject));
      while (i<numPins) {
				
				// pin location
        if (numValues) numValues = fscanf(f, "%s [%f, %f, %f]", key, &pins[i].initLoc.x, &pins[i].initLoc.y, &pins[i].initLoc.z);
        ////printf("%d / 4		%s\n", numValues, key);
        if (numValues < 4) {
          break; // possibly correct end, checked after while loop
        } else {
          pins[i].location.x = pins[i].initLoc.x;
          pins[i].location.y = pins[i].initLoc.y;
          pins[i].location.z = pins[i].initLoc.z;
        }

        pins[i].velocity.x = 0;
        pins[i].velocity.y = 0;
        pins[i].velocity.z = 0;
        pins[i].inPlay = 1;
				
        // pin radius
        if (numValues) numValues = fscanf(f, "%s %f", key, &pins[i].radius);
        ////printf("%d / 2		%s\n", numValues, key);
        if (numValues < 2) numValues = 0;  // there was an error
				
				// pin mass
        if (numValues) numValues = fscanf(f, "%s %f", key, &pins[i].mass);
        ////printf("%d / 2		%s\n", numValues, key);
        if (numValues < 2) numValues = 0;  // there was an error
				
        // pin mesh

        if (numValues) numValues = fscanf(f, "%s %s", key, aFileName);
        ////printf("%d / 2		%s\n", numValues, key);
        if (numValues < 2) numValues = 0;  // there was an error
        else {
          pins[i].dispList = glGenLists(1);
          glNewList(pins[i].dispList, GL_COMPILE);
          status = loadNdDraw(aFileName);
          glEndList();
          //printf("loading mesh \"%s\" returned status %d\n", aFileName, status);
          if (status == failure) numValues = 0; // bad things have happened
        }
        i++;
				if (numValues == 0) break;
      }
      if (i < numPins) {
        printf(
          "file didn't contain enough correctly formatted information about pins\n");
				sceneUnload();
				return failure;
      }
    }
    
  }
  
  fclose(f);

  printf("done loading scene from file %s\n", fName);
  
  glClearColor(bgRed, bgGreen, bgBlue, 1.0f);  

  glClearDepth(1.0);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);

  glShadeModel(GL_SMOOTH);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  cameraInitLoc.x = cameraLocation.x;
  cameraInitLoc.y = cameraLocation.y;
  cameraInitLoc.z = cameraLocation.z;
  /*
  cameraVelocity.x = 0.0;
  cameraVelocity.y = 1.0;
  cameraVelocity.z = 0.0;*/
  cameraVelocity = vec_between_points(cameraInitLoc, ball.location);
  vec_normalize(&cameraVelocity);

  SDL_GL_SwapBuffers();
  return success;
}

//////////////////////////// scene runtime //////////////////////////// 

void doPhysics () {
  // do the physics on the dynamic objects
  // NOTE: the remarked opengl calls in this section were for debugging, and
  // unremarking them will probably not have an effect any more as
  // doPhysics is not called between clearing of the buffers, and swapping of them
  // (doPhysics was once called from drawScene)
  collisionFace *thisFace;
  dynamicObject *obj, *pbj;
  vec3 reboundDirection, pointOnPlane, tempVector1, tempVector2, tempVector3;
	vec3 ov, ow, pv, pw, oNvel, pNvel;
  float numCollisions;
  float distFromPlane, radiusSquared, tempFloat;

  int i, k; // pinCounter, objectcounter respectively
  int u, v; // also used as counters
  int isCollision;
  
	obj = &ball;
  for (i = -1; i < numPins; i++) { // starts at -1 for the ball
    if (i>=0) {
			obj = &pins[i];
		}
    // is the object out of the bounds of the world?
    /* // debug, too specific
    if (obj->location.x > 100 || obj->location.x < -100 ||
        obj->location.y > 100 || obj->location.y < -100 ||
        obj->location.z > 100 || obj->location.z < -100) {
      printf("object out of bounds\n");
      obj->location.x = 0;
      obj->location.y = 0;
      obj->location.z = 1;*/
    if (obj->location.z < worldLowerBound) {
      obj->location.x = obj->initLoc.x;
      obj->location.y = obj->initLoc.y;
      obj->location.z = obj->initLoc.z;
      obj->velocity.x = 0.0;
      obj->velocity.y = 0.0;
      obj->velocity.z = 0.0;
      obj->inPlay = 0;
      if (i>=0) {
        remainingPins--;
      }
    }      
    // for sphere to sphere collisions, code follows
		pbj = &ball;
		for (k = i+1; k<numPins; k++) {
			if (k!=i) {
				if (k>=0) pbj = &pins[k];
        if (pbj->inPlay == 0 || obj->inPlay == 0) continue;
				// are the spheres obj and pbj intersecting?
				tempVector1 = vec_between_points(obj->location, pbj->location);
				tempFloat = vec_mag(tempVector1);
				//if (tempFloat < obj->radius + pbj->radius) {
				if (obj->radius + pbj->radius > tempFloat) {
          /* how this will work
            the total momentum between obj and pbj will be conserved...
            I will need to find the momentum of each object along the vector between them
              this will be velocity*mass where velocity may be negative
            the solution would have the same
            ...?
          */
          /*
          // spheres obj and pbj intesect...
					glPushMatrix();
					glLoadIdentity();  
					glMatrixMode(GL_PROJECTION);
					glPushMatrix();
					glLoadIdentity();
					glOrtho(0, 1, 0, 1, 1, -1);
					glDisable(GL_DEPTH_TEST);
					glColor3f(1.0, 0.0, 1.0);
					glRasterPos2f(0.5, 0.0);
					textString("COLLISION");
					glEnable(GL_DEPTH_TEST);
					glPopMatrix();
					glMatrixMode(GL_MODELVIEW);
					glPopMatrix();
					
					//glEnable(GL_BLEND);
					glDisable(GL_DEPTH_TEST);
					//glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); //multiply with current colors?
          //glBlendFunc(GL_DST_COLOR, GL_ZERO); //multiply with current colors?
					//glBlendFunc(GL_ONE, GL_SRC_COLOR);
					glPushMatrix();
					glTranslated(obj->location.x, obj->location.y, obj->location.z);
					glCallList(obj->dispList);
					glPopMatrix();
					glPushMatrix();
					glTranslated(pbj->location.x, pbj->location.y, pbj->location.z);
					glCallList(pbj->dispList);
					glPopMatrix();
					glEnable(GL_DEPTH_TEST);
					//glDisable(GL_BLEND); // */

					// there was a collision, change the velocity of accordingly
          // there is a newtons law, the force they exert on eachother is equal and opposite...
					// the method used was from a tutoial by Dimitrios Christopoulos
					// I used the version at http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=30
					// ... to an extent
					// I only used the equations, not the code (I have objects of different mass which the code didn't allow)

					// and fixing a bug in the algrorythm is the if statement further down
          
					vec_normalize(&tempVector1); // can change, will reduce one sqrt, oh my


          ov = vec_scalar_multiple(tempVector1, vec_dot(tempVector1, obj->velocity)); // velocity along between vector
          ow = vec_subtract(obj->velocity, ov); // velocity perpendicular to between vector
          //same for object pbj
          pv = vec_scalar_multiple(tempVector1, vec_dot(tempVector1, pbj->velocity)); // velocity along between vector
          pw = vec_subtract(pbj->velocity, pv); // velocity perpendicular to between vector

          // are the velocities such that obj and pbj will not collide more if left alone?
					// (are they moving away from each other...)
					/* this is how it works:
							tempVector is from ov to pv
							vector:			ov			pv			ov-pv
									no collision because the objeccts are moving away from each other
													<-			->			<--
													->			-->			<--
												 <--			->			<---
									is collision because they are moving towards eachother, or the one
									that is behind is faster
												 -->			->			--->
													<-		 <--			--->
													->			<-			-->
							notice how in the situations with collisions ov-pb is pointing to the right, 
							and in the situations without ov-pv is pointing to the left.  
							since tempVector is from ov to pv, I can use a dot product of it, and ov-pv
							to tell if the objects are colliding */
					if (vec_dot(tempVector1, vec_subtract(ov, pv)) > 0.0) {
            // I guess this could be tempVector 1 again
            // it is the factor of the new velocities that is the
            // same for both ov and pv
            // the diference between them will be added next
            tempVector2 = vec_add(
              vec_scalar_multiple(ov, obj->mass),

              vec_scalar_multiple(pv, pbj->mass));
						//tempFloat = 1 / (obj->mass + pbj->mass);
            tempFloat = REBOUND / (obj->mass + pbj->mass);

            // both of these vectors are temporary too
            // this could be much more efficient ...
            oNvel = vec_subtract(tempVector2,
              vec_scalar_multiple(vec_subtract(ov, pv),pbj->mass));
            pNvel = vec_subtract(tempVector2,
              vec_scalar_multiple(vec_subtract(pv, ov),obj->mass));

            obj->velocity = vec_add(ow, vec_scalar_multiple(oNvel, tempFloat));
            pbj->velocity = vec_add(pw, vec_scalar_multiple(pNvel, tempFloat));
          }
				}
			}
		}
	}

  // for sphere to geometry collisions, code follows
	obj = &ball;

  i = -2;
  for (i = -1; i < numPins; i++) { // starts at -1 for the ball
    if (i>=0) {
			obj = &pins[i];
		}
    if (obj->inPlay == 0) continue;
		radiusSquared = obj->radius * obj->radius;
    reboundDirection.x = 0.0;
    reboundDirection.y = 0.0;
    reboundDirection.z = 0.0;
    numCollisions = 0.0;
		thisFace  = firstCollisionFace;
    while (thisFace) {
      isCollision = 0;
      // is obj within radius of the [space] plane containing thisFace?
      distFromPlane = thisFace->offset - vec_dot(obj->location, thisFace->no);
      if (fabs(distFromPlane) < obj->radius) {
				/* && vec_dot(vec_between_points(obj->location, vec_scalar_multiple(thisFace->no, thisFace->offset)),	obj->velocity) > 0.0*/
        // distFromPlane can be negative indicating that the object
        // is opposite the normal of the plane

        /*  is the face inside the triangle of collision face
              collision, continue with next face
            is the face within radius of verticies
              collision, do next face
            is the face within radius of the edges
              collision, continue with next face
          */

        
        //----------------------------------------------------------------//
        
        // the following code section (which is sorta repeated 3 times)
        // checks to see if the object is within it's radius of a vertex on thisFace
        if (!isCollision) {
          if (fabs(vec_mag_squared(vec_between_points(thisFace->v[0], obj->location))) < radiusSquared)
            isCollision = 1;
        }
        if (!isCollision) {
          if (fabs(vec_mag_squared(vec_between_points(thisFace->v[1], obj->location))) < radiusSquared)
            isCollision = 1;
        }
        if (!isCollision) {
          if (fabs(vec_mag_squared(vec_between_points(thisFace->v[2], obj->location))) < radiusSquared)
            isCollision = 1;
        } 

        //----------------------------------------------------------------//
        

        /* the following section checks to see if the nearest point from
         * obj->location is inside of thisFace, if so there is a collision
         */
        
        pointOnPlane = vec_add(obj->location, vec_scalar_multiple(thisFace->no, distFromPlane));

        /*
          each tempVector is a normal of the plane formed by
          pointOnPlane and one of the edges on the triangle.
          they will all be parallel if pointOnPlane is inside of
          the triangle thisFace

        */  
        tempVector1 = vec_cross(

          vec_between_points(pointOnPlane,    thisFace->v[0]),
          vec_between_points(thisFace->v[0],  thisFace->v[1]));
        tempVector2 = vec_cross(
          vec_between_points(pointOnPlane,    thisFace->v[1]),
          vec_between_points(thisFace->v[1],  thisFace->v[2]));
        tempVector3 = vec_cross(
          vec_between_points(pointOnPlane,    thisFace->v[2]),
          vec_between_points(thisFace->v[2],  thisFace->v[0]));

        // is pointOnPlane inside of the triangle in thisFace
        // (are the tempVectors not pointing in opposite directions)
        if (
             vec_dot(tempVector1, tempVector2) > 0.0 &&
             vec_dot(tempVector2, tempVector3) > 0.0 &&
             vec_dot(tempVector3, tempVector1) > 0.0) {
          isCollision = 1;

          /*
          glColor3d(0.0, 1.0, 0.0);
          glPointSize(4.0);
          glBegin(GL_POINTS);
            glVertex3d(pointOnPlane.x, pointOnPlane.y, pointOnPlane.z);
          glEnd(); // */
        } 

        //----------------------------------------------------------------//

        /* this section tests to see if the distance from obj->location to
         * an edge (limited in length) on thisFace is less than the radius

         * of obj
         */

        for (u =0, v=1; u<3; u++, v++) {
          if (v == 3) v = 0;
          //printf("%d %d  ", u, v);
          if (!isCollision) {
            tempVector1 = vec_between_points(thisFace->v[u], (*obj).location);
            tempVector2 = vec_between_points(thisFace->v[u], thisFace->v[v]);
            tempVector3 = vec_between_points((*obj).location, thisFace->v[v]);
            // is obj->location between the planes formed by the perpendiculars
            // of this edge, at the end of the line segments?
            // yes, this is obfuscated
            if (vec_dot(tempVector1, tempVector2) > 0.0 && vec_dot(tempVector2, tempVector3) > 0.0) { //&& vec_dot(tempVector2, tempVector3) < 0.0 
              vec_normalize(&tempVector2);
              // how far is obj->location from this edge squared
              if (vec_mag_squared(vec_cross(tempVector1, tempVector2)) < radiusSquared) {
                isCollision = 1;
              }
            }
          }
        }
        //printf("\n");


        //----------------------------------------------------------------//

        // wheee! now we get to act this collision
        if (isCollision) {
          /*
          glColor4d(0.0, 0.0, 1.0, 0.75);
          glDisable(GL_CULL_FACE);
          glEnable(GL_BLEND);
          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
          glDisable(GL_DEPTH_TEST);
          glBegin(GL_TRIANGLES);
            glVertex3d(thisFace->v[0].x, thisFace->v[2].y, thisFace->v[0].z);
						glVertex3d(thisFace->v[1].x, thisFace->v[1].y, thisFace->v[1].z);

						glVertex3d(thisFace->v[2].x, thisFace->v[0].y, thisFace->v[2].z);
          glEnd();
          glEnable(GL_DEPTH_TEST);
          glDisable(GL_BLEND);
          glEnable(GL_CULL_FACE); // */

          // work starts
          // needs to be reversed if on other side of face
          if (distFromPlane >= 0.0) {
            reboundDirection.x -= thisFace->no.x;
            reboundDirection.y -= thisFace->no.y;
            reboundDirection.z -= thisFace->no.z;
            numCollisions += 1.0;
          } else {
            //printf("in opposite direction?\n");
            reboundDirection.x += thisFace->no.x;

            reboundDirection.y += thisFace->no.y;
            reboundDirection.z += thisFace->no.z;
            numCollisions += 1.0;
          }
        }
      }
      thisFace = thisFace->nextFace;
    }

    // new velocity (increase acceleration)
    obj->velocity.z -= GRAVITY * gameDeltaTime;
    
    if (numCollisions > 0.01) {
      // there was one or more collision faces generated,
      // alter velocity approaitely
      
      if (vec_mag_squared(reboundDirection) != 0.0) {
        vec_normalize(&reboundDirection);
      }
      // reboundDirection should now be a unit vector
      
      // set the velocity in reboundDirection to positive

      // how fast away from face (in reboundDirection)
      tempFloat = vec_dot(obj->velocity, reboundDirection);
      if (tempFloat < 0.0) {
        // velocity is towards face, reverse the towards face part
        obj->velocity = vec_add(obj->velocity, vec_scalar_multiple(reboundDirection, -2.0f * tempFloat * REBOUND));
      }
    } else {
    // no gravity for collision frames
    // in the future make different?

    
    }
    
    // move object direction of velocity
    obj->location.x += gameDeltaTime * obj->velocity.x;
    obj->location.y += gameDeltaTime * obj->velocity.y;
    obj->location.z += gameDeltaTime * obj->velocity.z;

    //printf("%l\r",vec_mag(obj->velocity));
  }
  
}

static void sceneIdle() {
  static int lastTime;
  int thisTime = 0;
  // to slow things down (windows XP on lab comps seems to only get 20fps)
  if(limitFrames) {
    while (thisTime < lastTime + 20) {  // 20 for 50fps, 50 for 20fps
      thisTime = SDL_GetTicks();
    }
  } else {
    thisTime = SDL_GetTicks();
  }
  deltaTime = (thisTime-lastTime)/1000.0f;
  // hack, around window movement and stuff
  if (deltaTime > 0.25) deltaTime = 0.0;
  // if I wish to slow things down...
  
	gameDeltaTime = deltaTime;
  lastTime = thisTime;
	if (isPaused == 1) {
		gameDeltaTime = 0.0;
	}
}

static void drawScene() {
  // draws the important stuff
  static char aString[400] = "";
  vec3 tempVec1;

  static int i = 0;
  // static only because I see no reason to free the memory between calls
  
  /* later change to drawScene(Scene *) */
  /* for more speed independence */


  // clear screen and depth buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // reset the matricies
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  // position the camera, and rotate it, 
  /* the args are
    camera postion              (x, y, z)
    where the camera is facing  (x, y, z)
    which way is up             (x, y, z) */
  gluLookAt(
    cameraLocation.x, cameraLocation.y, cameraLocation.z,
    /*0.0, 0.0, cameraLocation.z, // to change */
		/* ball.location.x, ball.location.y, ball.location.z, */
    cameraLocation.x + cameraVelocity.x,
    cameraLocation.y + cameraVelocity.y,
    cameraLocation.z + cameraVelocity.z,
    /* 0.0, 0.0, -3.0, // */
    0.0, 0.0, 1.0);

  // update the direction the camera points...
  tempVec1 = vec_between_points(cameraLocation, ball.location);
  vec_normalize(&tempVec1);
  vec_normalize(&cameraVelocity);
  cameraVelocity = vec_add(
    vec_scalar_multiple(tempVec1, cameraSpeed * gameDeltaTime),
    vec_scalar_multiple(cameraVelocity,(1.0f-gameDeltaTime) / cameraSpeed));

  /**** do the drawing of the objects here ****/
  //drawAxis();

  glCallList(worldDispList);
  
  // repeat for each pin as well as the ball
  glPushMatrix();
  glTranslatef(ball.location.x, ball.location.y, ball.location.z);
  glCallList(ball.dispList);
  glPopMatrix(); 

	for (i=0; i<numPins; i++) {
		// draw each pin, if it is valid
    if (pins[i].inPlay == 1) {
  		glPushMatrix();
  		glTranslatef(pins[i].location.x, pins[i].location.y, pins[i].location.z);
  		glCallList(pins[i].dispList);
  		glPopMatrix();
    }
	}
  
  /*************************************************************
   stuff to be done after object drawing commands on each frame
  *************************************************************/

  // from here on (till I say otherwise) I will put text printing
  glLoadIdentity();
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0, 1, 0, 1, 1, -1);
  // always in front, the text shall be
  glDisable(GL_DEPTH_TEST);
  glColor3f(txtRed, txtGreen, txtBlue);

	// framerate in lower left
  glRasterPos2f(0.0, 0.0); // where to put the text
	setValign(bottom);
  i = sprintf(aString, "%.2f FPS", deltaTime!=0.0?1/deltaTime:-1.0);
  textStringLength(aString, i); // */

  if (gameState != over) {
    glRasterPos2f(0.0, 1.0); // top left
  	setValign(top);
    //i = sprintf(aString, "%d of %d pins remain", remainingPins, numPins);
		i = sprintf(aString, "%d / %d", remainingPins, numPins);
    textStringLength(aString, i);
  }
  
  /*
  glRasterPos2f(0.0, 1.0);
	setValign(top);
  i = sprintf(aString, "%hu %hu", mousex, mousey);
  textStringLength(aString, i); */

  if (throwTimeRemaining >= 0.0) {
    glRasterPos2f(0.0, 1.0);
    moveCursor(0, -1);
  	setValign(top);
    i = sprintf(aString, "%.2f", throwTimeRemaining);
    textStringLength(aString, i);
  }
/*
	// xchange and y change just below upper left
  glRasterPos2f(0.0, 1.0);
	setValign(top);
  moveCursor(0, -1);

  i = sprintf(aString, "%d %d", totalXChange, totalYChange);
  textStringLength(aString, i);  */

  glRasterPos2f(0.5, 1.0);
  moveCursor(-9, 0);
	setValign(top);
  switch (gameState) {
    case introduction:
      i = sprintf(aString, "Level Introduction");

      break;                //////////////////
    case awaitingThrow:     //////////////////
      i = sprintf(aString, "  Throw The Ball  ");
      break;                //////////////////
    case throwing:          //////////////////
      //i = sprintf(aString, "__________________");
      i = sprintf(aString, " ");
      break;                //////////////////
    case watchingThrow:     //////////////////
      //i = sprintf(aString, "..................");
      i = sprintf(aString, " ");
      break;                //////////////////
    case over:              //////////////////
      i = sprintf(aString, "     Game Over    ");
      break;
  }
  textStringLength(aString, i);

  
  if (isPaused) {
    // print PAUSED in the middle of the screen
    glRasterPos2f(0.5, 0.5);
    setValign(middle);
    moveCursor(-3, 0);
    textStringLength("PAUSED",6);
  }
  
	// the cursor has the color of the text, currently
  // but I don't think I will be using it much, or at all
	if (showGLPointer) drawPointer(mousex, mousey);
  
	/* debug
	glRasterPos2f(0.0, 1.0);
	setValign(top);
  i = sprintf(aString, "%.2f FPS", gameDeltaTime!=0.0?1/gameDeltaTime:-1.0);
  textStringLength(aString, i);
	glRasterPos2f(0.0, 0.5);
	setValign(middle);
  i = sprintf(aString, "%.2f FPS", deltaTime!=0.0?1/deltaTime:-1.0);
  textStringLength(aString, i);
	*/
    
  // begin setting things back
  glEnable(GL_DEPTH_TEST);
  glPopMatrix();

  SDL_GL_SwapBuffers();
}


/*******************************************************
 *                   Scene Main                        *
 * this is where the scene work is done, sort of       *
 * it contains the scene main loop, and input handling *
 *******************************************************/
enum gameReturn sceneMain(char sceneFname[], enum e_gameState startingState) {
  // load the scene
  // initalize the scene
  // load the objects
  // mainLoop...
	
	float elapsedTime = 0.0;
	int numFrames = 0;

  static SDL_Surface *scrn;
  int done = 0;
  enum condition aCondition;
  enum gameReturn exitMode = quit;

  Uint8 *keystate;
  SDLMod modstate;

  pausedTimer = 0.0;
  //gameState = introduction;
  gameState = startingState;
	isPaused = 0;

  scrn = getScreen();
  sceneReshape(scrn->w, scrn->h);
  aCondition = sceneLoad(sceneFname);
  if (aCondition != success) {
    return quit;
  }
	//printf("numpins %d\n", numPins);
	
  sceneIdle();
  while ( ! done ) {
    SDL_Event event;
    while ( SDL_PollEvent(&event) ) {
      // loop through all events on this iteration
      switch(event.type) {
        case SDL_QUIT:
          /////////////////////////////////
          // freeing of the scene memory //
          // will be done at end of loop //
          /////////////////////////////////
          done = 1;
          break;
        case SDL_MOUSEBUTTONDOWN:
          if (event.button.button == SDL_BUTTON_LEFT) {
            //printf("mouse down\n");
            //wheee!
            // I need to test here the game mode...
            // switch on the game mode?
            //do stuff
            if (gameState == awaitingThrow) {
              ball.inPlay = 1;
              gameState = throwing;
              mouseMotionTime = 0.0;
              totalXChange = 0;
              totalYChange = 0;
            }
          }
          break;
        case SDL_MOUSEBUTTONUP:
          if (!isPaused && event.button.button == SDL_BUTTON_LEFT) {
            //printf("mouse up\n");
            // do different stuff
            if (gameState == throwing) {
              if (mouseMotionTime > 0.0001) {
                gameState = watchingThrow;
                throwTimeRemaining = 10.0; // a countdown timer for how long the throw can go for
                ball.velocity.x += xsensitivity*totalXChange/mouseMotionTime;
                ball.velocity.y -= ysensitivity*totalYChange/mouseMotionTime;
                // the screen y axis is opposite that of the camera's pov of the world...

                //printf("threw ball?\n");
              } else {
                //printf("hold button longer...\n");
                gameState = awaitingThrow;
              }
            } else if (gameState == over) {
              // done, return...
              exitMode = levelCompleted;
              done = 1;
            }
          } else if (isPaused  == 0
              && throwTimeRemaining > 0.0
              && gameState == watchingThrow) {
            // I guess this is to end 'early' an in progress throw?
            /*
            throwTimeRemaining = 0.0;
            ball.location.x = ball.initLoc.x;
            ball.location.y = ball.initLoc.y;
            ball.location.z = ball.initLoc.z;
            ball.velocity.x = 0.0;
            ball.velocity.y = 0.0;
            ball.velocity.z = 0.0; */
            ball.inPlay = 0; // his will abruptly end the throw
          }
          break;
        case SDL_MOUSEMOTION:
          // mousex and mousey are not really used...
          mousex = event.motion.x;
          mousey = event.motion.y;
          if (!isPaused) {
            // only when the game is not paused should the user be able to throw
            totalXChange += event.motion.xrel;
            totalYChange += event.motion.yrel;
          }
          break;
        // mouseinput goes here
      }
    }
    keystate = SDL_GetKeyState(NULL);
    modstate = SDL_GetModState();
    // keystate syntax: if (keystate[SDLK_a])
    // modstate systax: if (modstate & KMOD_SHIFT)
    if ( keystate[SDLK_ESCAPE] || (keystate[SDLK_q] && (modstate & KMOD_CTRL))) {
      exitMode = quit;
      done = 1;
    }
		if ( pausedTimer > 0.0 && // ... see the unpause thing below
      isPaused == 0 &&
      (keystate[SDLK_p] || keystate[SDLK_PAUSE]) ) {
      isPaused = 1;
      pausedTimer = 0.0;
    }
		if ( pausedTimer > 0.25 && // quarter second after pausing, can unpause
        isPaused == 1 &&
        (keystate[SDLK_p] || keystate[SDLK_PAUSE] || keystate[SDLK_RETURN]) ) { // return can only unpuse
			isPaused = 0;
      pausedTimer = -0.25; // quarter second after unpausing, can pause again
		}
    // the key responses beyond this point are going to be removed
    if ( keystate[SDLK_F12] ) { // act like the scene has been completed
      exitMode = levelCompleted;
      done = 1;
    }
    if ( keystate[SDLK_F5] ) { // restart the scene
      done = 1;
      exitMode = restart;
    }
    /*
    if (keystate[SDLK_KP_PLUS]) { // to be removed
      ball.velocity.z += 40.0 * gameDeltaTime;
    }
    if (keystate[SDLK_UP]) {
      //cameraLocation.z += 8.0 * deltaTime;
      ball.velocity.y += 40.0*gameDeltaTime;
    }
    if (keystate[SDLK_DOWN]) {
      //cameraLocation.z -= 8.0 * deltaTime;
      ball.velocity.y -= 40.0*gameDeltaTime;
    }
    if ( keystate[SDLK_LEFT] ) {
      //cameraLocation.x += 8.0 * deltaTime;
      ball.velocity.x -= 40.0*gameDeltaTime;
    }
    if ( keystate[SDLK_RIGHT] ) {
      //cameraLocation.x -= 8.0 * deltaTime;
      ball.velocity.x += 40.0*gameDeltaTime;
    }

    if (keystate[SDLK_F1]) {
      // limit frames
      limitFrames = 1;
    }
    if (keystate[SDLK_F2]) {
      // don't limit frames
      limitFrames = 0;
    }
		// */

    #ifndef WIN32
    if ( keystate[SDLK_F4] ) {
      isPaused = 1;
      // toggle fullscreen
      toggleFullscreen();
      scrn = getScreen();      

      sceneReshape(scrn->w, scrn->h);

    }
    #endif

    sceneIdle();
    
    // this is used to keep from pausing/unpausing too many times a second
    pausedTimer = pausedTimer + deltaTime;

    // for keeping track of how long it took the user to throw the ball
    mouseMotionTime += gameDeltaTime;

    throwTimeRemaining -= gameDeltaTime;
    if ( (throwTimeRemaining <= 0.0 && gameState == watchingThrow) || ball.inPlay == 0) {
      // the time for the throw has expired, or the ball has fallen out of the world
      gameState = awaitingThrow;
      throwTimeRemaining = -1.0;
      ball.location.x = ball.initLoc.x;
      ball.location.y = ball.initLoc.y;
      ball.location.z = ball.initLoc.z;
      ball.velocity.x = 0.0;
      ball.velocity.y = 0.0;
      ball.velocity.z = 0.0;
      ball.inPlay = 1;
      // where will I check to see if the game has ended?
      // why not here?
      // I also shall reset the pins..., and count the ones that are still in play...
/*
      cameraVelocity.x = 0;
      cameraVelocity.y = 1;
      cameraVelocity.z = 0; */
      cameraVelocity = vec_between_points(cameraInitLoc, ball.location);
      vec_normalize(&cameraVelocity);

    }
    if (remainingPins <= 0 && gameState != over) {
      // level completed...
      done = 1;
      exitMode = levelCompleted;
      throwTimeRemaining = -1.0;
    }
    
    if (!isPaused &&
    ( gameState == awaitingThrow || gameState == watchingThrow) ) { 
			//g ? there was more here...
      doPhysics();
    }
    drawScene();
    /*
		elapsedTime += deltaTime;
    numFrames += 1;
    if (elapsedTime > 0.5) {
      //printf("%.2f FPS          \n",deltaTime==0 ? -1.0 : 1/deltaTime);
      printf("%.2f FPS          \n",numFrames/elapsedTime);
      elapsedTime = 0.0;
      numFrames = 0;
    } // */
  }        
  sceneUnload();
  return exitMode;
}

