#include "cs148.h"

// Screen size
int screenWidth = 640;
int screenHeight = 480;

// How far off the ground is our car?
float car_y = 3.0;

// What color should our shadow be?
float shadow_color[4] = {0,0,0,0.4};

// The position of the light... we need this to render
// the light and to render the shadow
GLfloat light_position[] =  {0.0, 4.0, 0.0, 1.0};

// Mouse viewing rotation points
const double PI = 3.1415926;
const double DEG_TO_RAD = PI / 180.0;
int last_mouse_valid = 0;
int mouse_lastx, mouse_lasty;
double mouse_theta = 90.0;

// Which button is being dragged right now?
int dragging = -1;

// Mouse rotation
const double ROTATE_RATE = 0.75;
const double ZOOM_RATE = 0.05;

// Where is our camera?
float ex=0, ey=7.0, ez=8.0;

// Draw cylinder along z axis centered at origin
void drawCylinder(double radius, double height) {

  GLUquadricObj *quad;
  glPushMatrix();
  quad = gluNewQuadric();
  glTranslated(0., 0., -height * .5);
  gluQuadricDrawStyle(quad, GLU_FILL);
  gluQuadricOrientation(quad, GLU_OUTSIDE);
  gluQuadricNormals(quad, GLU_FLAT);
  gluCylinder(quad, radius, radius, height, 20, 10);
  glRotated(180., 1., 0., 0.);
  gluDisk(quad, 0., radius, 20, 10);
  glTranslated(0., 0., -height);
  glRotated(180., 1., 0., 0.);
  gluDisk(quad, 0., radius, 20, 10);
  gluDeleteQuadric(quad);
  glPopMatrix();

}

// Draws our nice little car made of cylinders
void drawCar(bool change_colors=true) {

  glTranslatef(0,car_y,0);

  // Draw car
  if (change_colors) glColor3f(.8, .8, .8);
  drawCylinder(.5, 2.);
  if (change_colors) glColor3f(.3, .3, .3);
  glPushMatrix();
  glRotated(90., 0., 1., 0.);
  glTranslated(.65, -.2, .5);
  drawCylinder(.4, .2);
  glTranslated(0., 0., -1.);
  drawCylinder(.4, .2);
  glTranslated(-1.3, 0., 0.);
  drawCylinder(.4, .2);
  glTranslated(0., 0., 1.);
  drawCylinder(.4, .2);
  glPopMatrix();

  glTranslatef(0,-car_y,0);
  

  
}


void drawShadow() {

  
  // This will be our shadow-projection matrix
  float shadow_matrix[16];

  int i;
  for (i=0;i<=15;i++) shadow_matrix[i]=0.0;
  shadow_matrix[0] = shadow_matrix[5] = shadow_matrix[10] = 1.0;
  shadow_matrix[7] = -1.0/light_position[1];
  
  glDisable(GL_CULL_FACE);
  
  // We'll enable blending, to allow a transparent gray shadow
  glEnable(GL_BLEND);
  glColor4fv(shadow_color);
    
  glPushMatrix();

    // We lift the resulting shape off the y=0 plane just a little, so it
    // always displays on top of the floor
    glTranslatef(0,0.01,0);

    // Squish everything onto the plane y = 0
    glScalef(1,0,1);

    // Move the origin back where it belongs
    glTranslatef(light_position[0],light_position[1],light_position[2]);

    // Project our object onto the plane
    glMultMatrixf(shadow_matrix); 

    // Move the origin to the light source position
    glTranslatef(-light_position[0],-light_position[1],-light_position[2]); 

    // Draw the car without generating colors
    drawCar(false);
  glPopMatrix();

  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  glDisable(GL_BLEND);

}


void myMouse(int button, int state, int mousex, int mousey) {

  int x = mousex;
  int y = screenHeight - mousey;

  // If this is the first time myMouse was called...
  if (last_mouse_valid == 0) {
    last_mouse_valid = 1;
    mouse_lastx = x;
    mouse_lasty = y;
  }

  // Record which button was pressed
  if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN)) {
    mouse_lastx = x;
    mouse_lasty = y;
    dragging = GLUT_LEFT_BUTTON;
  }

  else if ((button == GLUT_RIGHT_BUTTON) && (state == GLUT_DOWN)) {
    mouse_lastx = x;
    mouse_lasty = y;
    dragging = GLUT_RIGHT_BUTTON;
  }

  else dragging = -1;

}


void myMovedMouse(int mousex, int mousey) {
  
  int x = mousex;
  int y = screenHeight - mousey;

  if (dragging == GLUT_LEFT_BUTTON) {
  
    // Update rotation 
    mouse_theta -= (double)(x - mouse_lastx) * ROTATE_RATE;
    mouse_lastx = x;
    mouse_lasty = y;
  
    // Recompute look position (polar coords)
    double radius = sqrt(ez*ez+ex*ex);
    ex = radius * cos(DEG_TO_RAD * mouse_theta); 
    ez = radius * sin(DEG_TO_RAD * mouse_theta);   

  }

  else if (dragging == GLUT_RIGHT_BUTTON) {

    // Move in or out along our current look axis
    int dy = y - mouse_lasty;
    mouse_lastx = x;
    mouse_lasty = y;
    car_y += ((float)dy) / 100.0;
    
  }

  // Redraw
  glutPostRedisplay();

}


// Turn a light on
void lights(void){

  GLfloat color[] = {1,1,1,1};
  GLfloat acolor[] = {0,0,0,1};
  
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 180.0);
  glLightfv(GL_LIGHT0, GL_AMBIENT, acolor);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, color);
  glLightfv(GL_LIGHT0, GL_SPECULAR, color);

  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
  
}


// Display function -- called to redraw window
void myDisplay(void) {
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glEnable(GL_COLOR_MATERIAL);  
  
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  // Position the light in eye-space  
  lights();

  // Position the camera
  gluLookAt(ex, ey, ez, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  glShadeModel(GL_SMOOTH);
  glEnable(GL_LIGHTING);
  glEnable(GL_CULL_FACE);
  
  // Draw the floor
  glColor3f(0.0,0.5,0.5);
  glBegin(GL_QUADS);
  glVertex3f(-10, 0 , 10);
  glVertex3f( 10, 0 , 10);
  glVertex3f( 10, 0 ,-10);
  glVertex3f(-10, 0 ,-10);
  glEnd();

  drawCar();

  // Re-draws the car as a shadow on the flor  
  drawShadow();
  
  // Draw the light as a sphere
  glPushMatrix();
  glTranslatef(light_position[0],light_position[1],light_position[2]);
  glColor3f(0.8,0.8,0.3);
  glutSolidSphere(0.2,10,10);
  glPopMatrix();

  glutSwapBuffers();
}


// Reshape function -- called whenever window size changes
//   w, h = new window size
void myReshape(int w, int h) {
  screenWidth = w;
  screenHeight = h;
  glViewport(0, 0, screenWidth, screenHeight);  
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(50.0, (GLdouble)screenWidth / (GLdouble)screenHeight, 1.0, 100.);
  glMatrixMode(GL_MODELVIEW);
  glutPostRedisplay();
} 


// Initialization code
void myInit(void) {
  glLineWidth(2.);
  glEnable(GL_DEPTH_TEST);
  glClearColor(1.0, 1.0, 1.0, 0.0);
}


// Main function
int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
  glutInitWindowSize(screenWidth, screenHeight);
  glutInitWindowPosition(100, 100);
  glutCreateWindow("Patches");
  glutDisplayFunc(myDisplay);
  glutReshapeFunc(myReshape);
  glutMouseFunc(myMouse);
  glutMotionFunc(myMovedMouse);
  myInit();
  glutMainLoop();
  return 0;
}




