#include "cs148.h"

GLuint square_dlist;

// This class defines a square the moves around in our world
class moving_square {
public:

  // The length of one side of the square
  float edge;
  // The position of the center of the square
	float x, y;
  // The velocity of the square, in units per second
  float xvel, yvel;
  // The color of the square
  float r,g,b;
};

// The current window size, updated when we resize the window
int window_width = 640;
int window_height = 480;

// How many squares do we have
#define NSQUARES 20

// When we pick random velocities and sizes for the squares, what
// range should they be in?
#define MIN_VELOCITY 10.0
#define MAX_VELOCITY 100.0
#define MIN_EDGE_SIZE 15.0
#define MAX_EDGE_SIZE 40.0

// The list of squares that we'll animate
moving_square my_squares[NSQUARES];

// This is -1 when we're not dragging a square around.  When we
// are, it's the index of that square.
int dragging_square = -1;

// Initialization function - called when we start up
void myinit (void) {
   
  // Define a display list for a square with edge size 1 unit,
  // centered at the origin.
  square_dlist = glGenLists(1);
  glNewList(square_dlist, GL_COMPILE);
    glBegin (GL_QUADS);
      glVertex2f(-0.5, -0.5);
      glVertex2f( 0.5, -0.5);
      glVertex2f( 0.5,  0.5);
      glVertex2f(-0.5,  0.5);
    glEnd ();
  glEndList();

  // Give random size, velocity, and color to each square
  for(int i=0; i<NSQUARES; i++) {

    // Random size
    my_squares[i].edge =
      CS148::RandFloat() * (MAX_EDGE_SIZE - MIN_EDGE_SIZE) + MIN_EDGE_SIZE;
    
    // Random velocity
    my_squares[i].xvel =
      CS148::RandFloat() * (MAX_VELOCITY - MIN_VELOCITY) + MIN_VELOCITY;
    my_squares[i].yvel =
      CS148::RandFloat() * (MAX_VELOCITY - MIN_VELOCITY) + MIN_VELOCITY;
    
    // Random color
    my_squares[i].r = CS148::RandFloat();
    my_squares[i].g = CS148::RandFloat();
    my_squares[i].b = CS148::RandFloat();

  }

}


// Called when it's time to draw the screen
void display(void) {

  // Clear the framebuffer  
  glClear (GL_COLOR_BUFFER_BIT);

  // For each of our animated squares...
  for (int i = 0; i < NSQUARES; i++) {

    // There are only two lines in this program I'm going to tell you to ignore...
    glPushMatrix();

      // Tell OpenGL to draw with this square's color
      glColor3f(my_squares[i].r,my_squares[i].g,my_squares[i].b);
      // Tell OpenGL to translate all upcoming vertices by this square's position
      glTranslatef(my_squares[i].x,my_squares[i].y,0);
      // Tell OpenGL to scale all upcoming vertices by this square's size
      glScalef(my_squares[i].edge,my_squares[i].edge,0);
      // Draw a square using our display list
      glCallList(square_dlist);

    // This is the other one...
    glPopMatrix();

  }

  // Copy all this out to the display
  glutSwapBuffers();
}


// The last time our idle() function got a chance to run
double last_idle_time = 0.0;

// Our idle() function, called when GLUT isn't busy with something
// else
void idle(void) {

  // The very first time through, don't do anything except record
  // the time
  if (last_idle_time == 0.0) {
    last_idle_time = CS148::getTime();
    return;
  }

  // How much time has elapsed since we last did our animation?
  double curtime = CS148::getTime();
  double elapsed = curtime - last_idle_time;
  last_idle_time = curtime;
  
  // For each animated square
  for (int i = 0; i < NSQUARES; i++) {

    // If we're dragging this square with the mouse, leave him alone
    if (i == dragging_square) continue;

    // Update the square's x position
    my_squares[i].x += my_squares[i].xvel * elapsed;
    if (my_squares[i].x > window_width) my_squares[i].x = 0;
    if (my_squares[i].x < 0) my_squares[i].x = window_width;

    // Update the square's y position
    my_squares[i].y += my_squares[i].yvel * elapsed;
    if (my_squares[i].y > window_height) my_squares[i].y = 0;
    if (my_squares[i].y < 0) my_squares[i].y = window_height;

  }

  // Ask glut to redraw our window
  glutPostRedisplay();

}


// Called when the user clicks or releases a mouse button
void myMouseDown(int button, int state, int x, int y) {

  // We only care about the left button
  if (button != GLUT_LEFT) return;

  // If the user released the button, we're not dragging any squares
  if (state != GLUT_DOWN) {
    dragging_square = -1;
    return;
  }

  // Convert y to our coordinate system with the origin at the lower-left
  y = window_height - y;

  // For each animated square
  for (int i = 0; i < NSQUARES; i++) {

    // Did the user click inside this square?
    if (
      (x > my_squares[i].x - my_squares[i].edge / 2.0) &&
      (x < my_squares[i].x + my_squares[i].edge / 2.0) &&
      (y > my_squares[i].y - my_squares[i].edge / 2.0) &&
      (y < my_squares[i].y + my_squares[i].edge / 2.0)
    ) {
      // If he did, I'm dragging this square now...
      dragging_square = i;
      return;
    }
  }

  // The user didn't click inside any squares, so I'm not dragging any squares
  dragging_square = -1;
  return;
 
}


// Called when the user drags the mouse with the button held down
void myMouseMove(int mousex, int mousey) {

  // If I'm dragging one of my squares...
  if (dragging_square) {
    // Move it to wherever the user put it
    my_squares[dragging_square].x = mousex;
    my_squares[dragging_square].y = window_height - mousey;
    // Ask glut to redraw
    glutPostRedisplay();
  }

}


// Called when the user resizes the window
void myReshape(int w, int h) {
    
  // Update our global variables that store the window size
  window_width = w;
  window_height = h;

  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h);
  glMatrixMode(GL_MODELVIEW);
  
}


int main(int argc, char ** argv) {
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutInitWindowSize (window_width, window_height); 
   glutInitWindowPosition (0, 0);
   glutCreateWindow (argv[0]);
   glClearColor(1,1,1,1);
   myinit ();
   glutIdleFunc(idle);
   glutMouseFunc(myMouseDown);
   glutMotionFunc(myMouseMove);
   glutDisplayFunc(display); 
   glutReshapeFunc(myReshape);
   glutMainLoop();
   return 0;

}

