/***********************************
 * assignment 3 shell              *
 * CS 488 - Spring 2003            *
 * Written 10/03 by Andrew Johnson *
 ***********************************/
 
#include <stdlib.h>
#include <stdio.h>

//most systems include GL/glut.h others just glut.h
#include <GL/glut.h>

#define UPDATE_RATE 50
#define DISPLAY_RATE 50


// here are some global variables (nasty)
/////////////////////////////////////////

float angle = 0.0;

GLubyte * sunTex;
GLubyte * earthTex;

GLUquadricObj*  sph;

/*************************************************************
/* output code by Mark Kilgard */

void output(float x, float y, float z, char *string)
{
  int len, i;

  glRasterPos3f(x, y, z);
  len = (int) strlen(string);
  for (i = 0; i < len; i++) {
    glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, string[i]);
  }
}

/*************************************************************/

GLubyte * loadTexture (char *fileName, int width, int height, int depth)
{
  GLubyte *raw_bitmap, *reversed_bitmap;
  FILE *texFile;

  int byteSize, textureSize;
  byteSize = sizeof(GLubyte);
  textureSize = width * height * depth * byteSize;

  texFile = fopen(fileName, "rb");

  if (texFile == NULL)
    {
      printf ("Texture %s not found\n", fileName);
      exit(1);
    }

  raw_bitmap = (GLubyte *) malloc (textureSize);
  reversed_bitmap = (GLubyte *) malloc (textureSize);

  if ((raw_bitmap == NULL) || (reversed_bitmap == NULL))
    {
      printf("Cannot allocate memory for texture %s\n", fileName);
      fclose(texFile);
      exit(1);
    }

  fread (raw_bitmap , width * height * depth, 1 , texFile );
  fclose (texFile);

  /* need to reverse the texture horizontally here */
  /************************************************/

  int i, j, k;

  for (i=0; i<height;i++)
    for (j=0; j<width*depth;j+=depth)
      for (k=0;k<depth;k++)
	reversed_bitmap[i*width*depth + (j+k)] =
	                  raw_bitmap[(i+1)*width*depth-(j+depth)+k];


  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

  free (raw_bitmap);

  return (reversed_bitmap);
}

/*************************************************************
 *
 * keyboard
 * 
 * deal with the keyboard input to the program.  
 * 
 *************************************************************/

void keyboard(unsigned char c, int x, int y)
    {
      // escape key exits the program

      if (c == 27)
	exit(0);
    }

/*************************************************************/

void reshape ( int w, int h )
{
  glViewport(0, 0, (GLint) w, (GLint) h);

  glMatrixMode ( GL_PROJECTION);
  glLoadIdentity( );
  gluPerspective ( 45, (GLdouble)w / (GLdouble)h, 1.0, 200.0 );

  glMatrixMode ( GL_MODELVIEW );
}

 
/*************************************************************
 *
 * update
 * 
 * update everything that needs to be updated in the scene - this
 * function should be called via a timer to ensure that updates 
 * happen at a regular rate, independent of the graphics update
 * rate
 *
 *************************************************************/

void update(int value)
    {
      angle += 0.5;

      if (angle < 0)
	angle = 360;

      if (angle > 360)
	angle = 0;

    glutTimerFunc(UPDATE_RATE, update, 1);
    } 

/*************************************************************/

void drawPlanet(GLubyte * tex, int components, float radius) 
{
    glPushMatrix();
	    glRotatef(90.0, 1.0, 0.0, 0.0);

	    glColor4f (1.0, 1.0, 1.0, 1.0);

	    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 256, 0,
		 GL_RGB, GL_UNSIGNED_BYTE, tex);

	    gluSphere(sph, radius, components, components);
    glPopMatrix();
}
   
/*************************************************************
 *
 * display
 * 
 * this function is called each time the screen is to be drawn
 *************************************************************/

void display(void)
    {

    /* clear the screen to the clear colour */
    /****************************************/

      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

      /* work out the relative speed values */
      /*************************************/

      float sunRotateRate = angle;
      float earthRotateRate = angle*10;
      float earthRevolveRate = angle*2;

    /* draw everything */
    /******************/
        
    glLoadIdentity ();

    gluLookAt(0, 0, 10,   0, 0, 0,    0, 1, 0);

    // draw Sun centered at 0,0,0
    /////////////////////////////

    glPushMatrix();

	glPushMatrix();

	    glRotatef(sunRotateRate, 0.0, 1.0, 0.0 );
      	    drawPlanet(sunTex, 25, 1.0);

        glPopMatrix();


    // draw Earth revolving around Sun and rotating
    ///////////////////////////////////////////////

	glPushMatrix();

	    glRotatef(earthRevolveRate, 0.0, 1.0, 0.0 );
	    glTranslatef(3.0, 0.0, 0.0);

	    glRotatef(earthRotateRate, 0.0, 1.0, 0.0 );

	    drawPlanet(earthTex, 15, 0.5);

	glPopMatrix();


    glPopMatrix();

    //     glColor3f(1.0, 1.0, 1.0);
    //     output(-1, 2, 0, "Simple Solar System");


    /* swap buffers */
    /****************/
        
    glutSwapBuffers();
    }

/*************************************************************
 *
 * display2
 *
 * called by a timer to regularly update the  display 
 *************************************************************/

void display2 (int temp)
{
  display();

  // re-set the display timer func again
  glutTimerFunc(DISPLAY_RATE, display2, 1);

}

/*************************************************************
 *
 * dealWithParameters
 * 
 * here you could put some code to deal with the command-line
 * parameters for the program
 *************************************************************/
 
void dealWithParameters(int argc, char **argv)
    {
    }

/*************************************************************
 *
 * main
 * 
 *************************************************************/

int main(int argc, char **argv)
    {

    /* deal with any GLUT command Line options */
    /******************************************/
        
    glutInit(&argc, argv);
    
    /* deal with any command line options for this program */
    /******************************************************/
    
    dealWithParameters(argc, argv);

    /* create an output window */
    /**************************/

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

    /* set up the physical window size */
    /**********************************/

    glutInitWindowSize(500, 500);

    /* set the name of the window and try to create it */
    /**************************************************/

    glutCreateWindow("CS 488 - HW 3");

    if (glutGet(GLUT_WINDOW_COLORMAP_SIZE) != 0)
	{
	printf("FAIL: bad RGBA colormap size\n");
	exit(1);
	}

    /* load in texture maps */
    /***********************/

    sunTex = loadTexture ("sun.raw", 512, 256, 3);
    earthTex = loadTexture ("earth.raw", 512, 256, 3);

    /* set up for accepting keyboard commands */
    /*****************************************/

    glutKeyboardFunc(keyboard);

    /* set up the general graphics stuff */
    /************************************/
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45, 1.0, 1.0, 200.0);
    glClearColor(0.0, 0.0, 0.0, 1.0);

    /* set up the lighting */
    /**********************/

    GLfloat sunLight[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat light_pos[] = {0.0, 10.0, 0.0, 1.0};
    glLightfv (GL_LIGHT0, GL_POSITION, light_pos);
    glLightfv (GL_LIGHT0, GL_DIFFUSE, sunLight);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    GLfloat ambientLight[] = {0.2, 0.2, 0.2, 1.0};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_TEXTURE_2D);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    glDepthFunc(GL_LESS);
    glCullFace(GL_BACK);

    glutReshapeFunc(reshape);


    /* create a new quadric */
    /***********************/

    sph = gluNewQuadric();
    gluQuadricDrawStyle(sph, GLU_FILL);
    gluQuadricNormals(sph, GLU_SMOOTH);
    gluQuadricTexture(sph, GL_TRUE);
    gluQuadricOrientation(sph, GLU_OUTSIDE);

    /* assign the display function */
    /******************************/
    
    glutDisplayFunc(display);

    /* no general idle function this time */
    /*************************************/

    //    glutIdleFunc(display);

    /* set up a timer to regularly update the scene and display */
    /***********************************************************/

    glutTimerFunc(UPDATE_RATE, update, 1);
    glutTimerFunc(DISPLAY_RATE, display2, 1);

    /* set everything going */
    /***********************/
    
    glutMainLoop();


    // free up the memory from the quadric

    gluDeleteQuadric(sph);
  
  return 0;  
}
