/*
     Things to do/add/fix

    1) Add the sun
    2) Fix rotation
    3) Add fly through
    4) Add the ability to zoom
	5) Draw only what is necessary (to increase frame rate)
	   ... i.e. if planet is not shown in view then don't 
	       draw it. Also, if a planet is far away from eye  
		   then the textures do not have to have as much 
		   detail when drawn.
    .
	.
	.
     - Add satellites ...
	 - Be able to take a snapshot
 */


/* ************************************************************************
   *                                                                      *
   *  solar_sys.c                                                         *
   *                                                                      *
   *   Author:  Mariusz Zaczek                                            *
   *   Date:    06/13/2000                                                *
   *   version: 2.0                                                       *
   *                                                                      *
   *                                                                      *
   *                                                                      *    
   * (c) Copyright 2000 Mariusz Zaczek                                    *   
   * ALL RIGHTS RESERVED                                                  *
   *                                                                      *
   * Permission to use, copy, modify, and distribute this software for any*
   * purpose and without fee is hereby granted, provided that the above   *
   * copyright notice appear in all copies and that both the copyright    *
   * notice and this permission notice appear in supporting documentation,* 
   * and that the name of Mariusz Zaczek. not be used in advertising      *
   * or publicity pertaining to distribution of the software without      *
   * specific, written prior permission.                                  *
   *                                                                      * 
   *  THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"   *
   *  AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,    * 
   *  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR    *
   *  FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL MARIUSZ        *
   *  ZACZEK BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,              *
   *  SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY       *
   *  KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,      *
   *  LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF   *
   *  THIRD PARTIES, WHETHER OR NOT MARIUSZ ZACZEK HAS BEEN               *
   *  ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON      *
   *  ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE   *
   *  POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.                    *
   *                                                                      *
   *  US Government Users Restricted Rights                               *
   *  Use, duplication, or disclosure by the Government is subject to     *
   *  restrictions set forth in FAR 52.227.19(c)(2) or subparagraph       *
   *  (c)(1)(ii) of the Rights in Technical Data and Computer Software    *  
   *  clause at DFARS 252.227-7013 and/or in similar or successor         *
   *  clauses in the FAR or the DOD or NASA FAR Supplement.               *
   *  Unpublished-- rights reserved under the copyright laws of the       *
   *  United States.                                                      *
   *                                                                      *
   ************************************************************************ */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <GL/glut.h>
#include <GL/glaux.h>  /* Can't be used in Unix I think */


/* ************************************************************************ */
/* *******( Defines )****************************************************** */
/* ************************************************************************ */
#ifndef PI
#define PI 3.14159625358979323846      /* PI                                */
#endif

#define BMP_FILES   /* or RAW_FILES */

#define MAX_NO_TEXTURES 15             /* Maximum # of textures             */
#define SCALE_FACTOR    1000           /* Scaling factor                    */
#define SCALE_3x_FACTOR 3*SCALE_FACTOR 
#define NUM_PLANETS     10             /* Number of planets (with sun)      */
#define POS_SCALE       1.0            /* Scaling factor for positions      */
#define NUMSTARS        3000
#define WIN_WIDTH       768
#define WIN_HEIGHT      512

#define SCREEN_MENU_ENTIRE       -2
#define SCREEN_MENU_INNER        -1
#define SCREEN_MENU_SUN           0
#define SCREEN_MENU_MERCURY       1
#define SCREEN_MENU_VENUS         2
#define SCREEN_MENU_EARTH         3
#define SCREEN_MENU_MARS          4
#define SCREEN_MENU_JUPITER       5
#define SCREEN_MENU_SATURN        6
#define SCREEN_MENU_URANUS        7
#define SCREEN_MENU_NEPTUNE       8
#define SCREEN_MENU_PLUTO         9

#define SCREEN_MENU_TEXTURES      1
#define SCREEN_MENU_STARS         2
#define SCREEN_MENU_ORBITS        3

#define SCREEN_MENU_ZOOM          1
#define SCREEN_MENU_ROTATE        2
#define SCREEN_MENU_FLY           3

#define SCREEN_MENU_ANIMATE       1
#define SCREEN_MENU_REVERSE       2
#define SCREEN_MENU_STOP          3
#define SCREEN_MENU_RESET         4
#define SCREEN_MENU_FWD           5
#define SCREEN_MENU_REV           6

#define MENU_EXIT                 1


/* ************************************************************************ */
/* *******( structs )****************************************************** */
/* ************************************************************************ */

/* ************************************************************************
   *                                                                      *
   *      PlanetData - struct containing info on a planet                 *
   *                                                                      *
   ************************************************************************ */
typedef struct
{
  char     *name,             /* Name of planet                             */
           *filename;         /* Name of .raw file                          */
  GLint    tex_width,         /* Width of texture                           */
           tex_height;        /* Height of texture                          */
  GLdouble N1,                /* (N) Longitude of ascend. node (part 1)     */
           N2,                /*        "        "       "           2      */
           i1,                /* (i) Inclination of ecliptic (part 1 of eq) */
           i2,                /*        "        "       "         2        */
           w1,                /* (w) argument of perihelion (part 1 of eq)  */
           w2,                /*        "        "       "        2         */
           a1,                /* (a) semi-major axis in AU (part 1 of eq)   */
           a2,                /*        "        "       "       2          */ 
           e1,                /* (e) eccentricity (part 1 of eq)            */
           e2,                /*        "        "      2                   */
           M1,                /* (M) Mean anomaly (part 1 of eq)            */
           M2,                /*        "        "      2                   */
           radius,            /* Planet's radius in km                      */
           dia_scaled,        /* Scaled diameter ...                        */
           tilt,              /* Body Tilt in degrees                       */
           flat_x,            /* x axis flattening                          */
           flat_y,            /* y  "       "                               */
           flat_z,            /* z  "       "                               */
           rot_rate,          /* rotation rate (degrees/day)                */
           circ_sun_dist,     /* Distance to sun of "circular" orbit AU     */
		   orbit_rate;        /* Orbital period (days)... used for circular */
  GLubyte  R,G,B;             /* RGB color triplet for untextured cases     */
  GLint    num_sats,          /* Number of satellites (sun has planets)     */
           rings;             /* 0 = no rings; 1 = has rings                */
} PlanetData;


/* ************************************************************************
   *                                                                      *
   *      Points - point struct with x and y and z coordinates.           *
   *                                                                      *
   ************************************************************************ */
typedef struct {
  GLdouble x,y,z;
} Point3D;
typedef Point3D Vector;


typedef struct {
  GLubyte R,G,B;
} Color;


/* ************************************************************************
   *                                                                      *
   *      Date - struct containing day,month,year and hour                *
   *                                                                      *
   ************************************************************************ */
typedef struct {
  GLint day, month, year;
  GLdouble hour;
} Date;


/* ************************************************************************
   *                                                                      *
   *      stardata - star struct                                          *
   *                                                                      *
   ************************************************************************ */
typedef struct {
        char Name[20];
        GLdouble posx,posy,posz;
        GLdouble magnitude,color[3];
        char type;
} stardata;


/*  TEMP  */
typedef struct
{
  GLdouble  radius,  /* Radial distance of solar sail */
            phi,     /* Angle of solar sail current position */
            theta;   /* Normal to the solar sail. */
} TrajectoryPoint;



/* ************************************************************************ */
/* *******( Global Variables )********************************************* */
/* ************************************************************************ */
GLuint        texture_id[MAX_NO_TEXTURES],
              sub_width = WIN_WIDTH,
              sub_height = WIN_HEIGHT,
              clouds;              

AUX_RGBImageRec *textures[MAX_NO_TEXTURES];

GLvoid        *font_style = GLUT_BITMAP_TIMES_ROMAN_10;

/* -1=all, -2=inner, -3=outer, >0 = specific planet */
#define       ENTIRE     -1
#define       INNER      -2
#define       OUTER      -3

GLint         numplanets = 9, /* Number of planets                                */
              solarsys = ENTIRE, /* -1=all, -2=inner, -3=outer, >0 = specific planet */
              old_x,
              old_y;          /* Old y value of mouse in "command" window         */

GLdouble      spin_x = 0.0,
              spin_y = 0.0,
              world_position  = -75.0; /* default "world" position...above solar system */

GLfloat       ambient_light[] = {0.4f,  0.4f,  0.4f,  1.0f};
//GLfloat       ambient_light[] = {1.0f,  1.0f,  1.0f,  1.0f};
GLfloat       source_light[]  = {0.9f,  0.8f,  0.8f,  1.0f};
//GLfloat       source_light[]  = {1.0f,  1.0f,  1.0f,  1.0f};
//GLfloat       light_pos[]     = {1.0f, -1.0f,  1.0f,  1.0f};
GLfloat       light_pos[] = {0.0f, 0.0f, 0.0f, 1.0f};


GLdouble        TransMatrix[16] =  {  1.0,  0.0,  0.0,  0.0,
                                      0.0,  1.0,  0.0,  0.0,
    				    		 	  0.0,  0.0,  1.0,  0.0,
						    		  0.0,  0.0,  0.0,  1.0  };

GLdouble       GlobalMatrix[16] =  {  1.0,  0.0,  0.0,  0.0,
                                      0.0,  1.0,  0.0,  0.0,
					    		 	  0.0,  0.0,  1.0,  0.0,
						     		  0.0,  0.0,  0.0,  1.0  };

GLdouble      projection[16], 
              modelview[16];
              
Point3D       planet_locs[NUM_PLANETS],  /* Locations of each planet           */
              lookat = { 0.0, 0.0, 0.0 };

Date          cur_date = { 1, 1, 2000, 0.0 };  /* Jan 1, 2000   0:00 am        */

GLdouble      cur_julian = 2451545.0,    /* Julian equivalent of cur_date      */
              date_update = 0.01,        /* The time increment for animation   */
              eyeX = 10.0,                /* x-coord of eye position in 3D      */
			  eyeY = 0.0,                /* y    "      "      "      "        */
			  eyeZ = 0.0,                /* z    "      "      "      "        */
			  centerX = 0.0,             /* x-coord at what eye is looking     */
			  centerY = 0.0,             /* y    "      "      "      "        */  
			  centerZ = 0.0,             /* z    "      "      "      "        */
			  upX = 0.0,                 /* x-value of up direction vector     */
              upY = 0.0,                 /* y    "      "      "      "        */
			  upZ = 1.0,                 /* z    "      "      "      "        */
			  sec = 0.0,                 /* Elapsed time                       */
			  frames_per_sec = 0.0,      /* Frames per second ...              */
			  star_size = 1.0,           /* Size of stars in background        */
			  intro_angle = 0.0,         /* Angle of intro screen planet rot.  */
			  intro_clouds = 0.0;        /* Angle of clouds ...                */
             
GLint         Stars,                     /* Integers representing strs list    */
			  parameter_selected = 0,    /* Which input parameters to draw     */
              frames = 0,                /* Frame counter                      */
              mouse_x,                   /* x coordinate of mouse point        */
			  mouse_y,                   /* y     "     "      "       "       */
			  selected_planet = -1,      /* Which planet I'm looking at        */
              num_plates = 20,           /* Referring to the detail of textures*/
              fullscreen = 0,            /* Set to full screen mode 1=yes      */
			  smallFullscreen = 0,       /* Smaller resolution full screen mode*/
			  scrnsvr_counter = 0,       /* Screen Saver counter               */
              scrnsvr_planet = 0;        /* Current Planet in screen saver     */

GLboolean     ANIMATE = 1,               /* 0 = no animation; 1 = animation on */
              show_textures = 1,         /* 0 = no object textures; 1 = on tex.*/
              show_screen_orbits = 1,    /* 0 = don't draw circular orbits     */
              show_screen_stars = 1,     /* 0 = don't draw stars, else draw    */
			  PRINT_HELP = 0,            /* Display help info on screen        */
              PRINT_INFO = 1,            /* Display info on screen, toggle i   */
			  DRAW_AXES = 1,             /* Draw axes on screen, toggle a      */
              FLY_THROUGH = 1,           /* Set if you want to fly through 3d  */
              CIRCULAR = 0,              /* Draw positions based on circ orbits*/
              INTRO = 1,                 /* If 1, display intro screen         */
	          FREE_MODE = 0,             /* If 1, run in normal mode           */
	          FILE_INPUT = 0,            /* If 1, then execute per input file  */
			  SCREEN_SAVER = 0,          /* Screensaver mode ... rotating plnts*/
			  DRAW_CLOUDS = 0;           /* Draw clouds on screen  not used yet*/

GLdouble      forward = 0.0,
              reverse = 0.0,
			  left    = 0.0,
              right   = 0.0,
			  up      = 0.0,
			  down    = 0.0;

Vector        direction = { 0.0, 0.0, 0.0 };

/* Planet structures ... containing planet data */
PlanetData    planet[NUM_PLANETS];

/* Name of file which contains the planet data */
#include "solarsys.data"

/* Initial array of degree values of each planets rotation about it's axis */
GLdouble      rot_degrees[NUM_PLANETS] = 
					{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

/* Initial position of planets when using circular orbits about sun */
GLdouble      rev_degrees[NUM_PLANETS] = 
					{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

/* Star structure ... containing position, intensity, name, etc */
stardata      stars[NUMSTARS];

/* TEMP Number of trajectory points */
TrajectoryPoint pts[100];
GLint num_points = 0;
char title[100];

/* *** Define some colors ******************** */
Color       RED = { 255,   0,   0 },
          GREEN = {   0, 255,   0 },
           BLUE = {   0,   0, 255 },   
         YELLOW = { 255, 255,   0 },
          WHITE = { 255, 255, 255 },
        MAGENTA = { 255,   0, 255 },
 		   CYAN = {   0, 255, 255 };

/* ************************************************************************ */
/* *******( function declarations)***************************************** */
/* ************************************************************************ */
GLvoid redisplay_all( GLvoid );
GLvoid draw_stars( GLvoid );
GLvoid Camera( GLvoid );


/* ************************************************************************ */
/* *******( functions )**************************************************** */
/* ************************************************************************ */



GLvoid draw_trajectory( GLvoid )
{
  GLint i = 0;

  glColor3ub( RED.R, RED.G, RED.B );
  for ( i=0 ; i<num_points ; i++ )
  {
    glPushMatrix();
      glTranslatef(pts[i].radius*cos(pts[i].phi),
                   pts[i].radius*sin(pts[i].phi),0.0);
  
      glRotatef(90.0+180.0*pts[i].theta/PI,0.0,0.0,1.0);
      
      glScalef(0.05f, 0.0025f, 0.05f);      
        glutSolidCube(1.0);
    glPopMatrix();
  }

  glColor3ub( WHITE.R, WHITE.G, WHITE.B );
}



GLvoid FileInput( GLvoid )
{
  FILE *f=NULL;
//  char line[100] = "##################";
//  char title[100];
  GLint i = 0;

  f = fopen("solarsys.inp","rt");

  if ( f == NULL ) 
  {
    printf("\nError(FileInput-1): Input file is missing...exiting\n");
    exit(-1);
  }

  /* Skip lines that start with # */
//  while ( line[0]=='#' ) 
//    fgets(line,100,f);
 
    
  fgets(title,100,f);     /* Read title */
//  fgets(line,100,f);      /* ... comments ... */
  fscanf(f,"%d",&num_points);
//  fgets(line,100,f);      /* ... comments ... */

  for ( i=0 ; i<num_points ; i++ )
  {
    fscanf(f,"%lf%lf%lf",&pts[i].radius, &pts[i].phi, &pts[i].theta);
    printf("\n %lf  %lf  %lf ",pts[i].radius,pts[i].phi,pts[i].theta);
  }
  fclose(f);
}


GLvoid Intro( GLvoid )
{
  GLUquadricObj *sphere;

  glBindTexture(GL_TEXTURE_2D, texture_id[3]);

  glPushMatrix();
    glRotatef(intro_angle, 0.0, 0.0, 1.0);

    /* Draw a sphere */
    sphere = gluNewQuadric();
    gluQuadricDrawStyle(sphere, GLU_FILL);
    gluQuadricNormals(sphere, GLU_SMOOTH);
    gluQuadricTexture(sphere, GL_TRUE);
    gluSphere(sphere, planet[3].dia_scaled*SCALE_FACTOR,
            100, 100);
    gluDeleteQuadric(sphere);
  glPopMatrix();
}


GLvoid ScreenSaver( GLvoid )
{
  GLUquadricObj *sphere;

  glBindTexture(GL_TEXTURE_2D, texture_id[scrnsvr_planet]);

  glPushMatrix();
    glRotatef(intro_angle, 0.0, 0.0, 1.0);

    /* Draw a sphere */
    sphere = gluNewQuadric();
    gluQuadricDrawStyle(sphere, GLU_FILL);
    gluQuadricNormals(sphere, GLU_SMOOTH);
    gluQuadricTexture(sphere, GL_TRUE);
    gluSphere(sphere, 
		      planet[scrnsvr_planet].dia_scaled*SCALE_FACTOR,
              100, 100);
    gluDeleteQuadric(sphere);
  glPopMatrix();
}


/* ************************************************************************
   *                                                                      *
   *      FlightDirection                                                 *
   *                                                                      *
   *   - determins the direction of flight based on mouse position        *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid FlightDirection( GLvoid )
{


}


/* ************************************************************************
   *                                                                      *
   *      Fly                                                             *
   *                                                                      *
   *   - Fly-through routine...which moves the eye position according     *
   *     to user input via the directional keys and mouse                 *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid Fly( GLvoid )
{
//  FlightDirection();

  eyeX += (forward-reverse);
  eyeY += (right-left);
  eyeZ += (up-down);

  forward = 0.0;
  reverse = 0.0;
  right = 0.0;
  left = 0.0;
  up = 0.0;
  down = 0.0;

  centerX = 0.0;
  centerY = 0.0;  
  centerZ = 0.0;
  selected_planet = -1;
}


/* ************************************************************************
   *                                                                      *
   *      gregorian_to_julian                                             *
   *                                                                      *
   *   - calculates the julian day from the Gregorian Date                *
   *                                                                      *
   *   Input Variables: Date the_date Gregorian Date                      *
   *                                                                      *
   *   Returns: GLint - julian day                                        *
   *                                                                      *
   ************************************************************************ */
GLvoid gregorian_to_julian( GLvoid )
{
  cur_julian = ( 1461 * ( cur_date.year + 4800 + ( cur_date.month - 14 ) / 
	    12 )) / 4 + ( 367 * ( cur_date.month - 2 - 12 * (( cur_date.month - 
		14 ) / 12 ))) / 12 - ( 3 * (( cur_date.year + 4900 + ( cur_date.month
		- 14 ) / 12 ) / 100 )) / 4 + cur_date.day - 32075;
}


/* ************************************************************************
   *                                                                      *
   *      julian_to_gregorian                                             *
   *                                                                      *
   *   - calculates the julian day from the Gregorian Date                *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid julian_to_gregorian( GLvoid )
{
  GLint i=0, j=0, l=0, n=0;
  
  /* Calculate the current hour 0-24 based on the values after the decimal
     point of the "cur_julian" counter */
  cur_date.hour = 24.0*(cur_julian - (GLint)cur_julian);

  l = (GLint)cur_julian + 68569;
  n = ( 4 * l ) / 146097;
  l = l - ( 146097 * n + 3 ) / 4;
  i = ( 4000 * ( l + 1 ) ) / 1461001;
  l = l - ( 1461 * i ) / 4 + 31;
  j = ( 80 * l ) / 2447;
  cur_date.day = l - ( 2447 * j ) / 80;
  l = j / 11;
  cur_date.month = j + 2 - ( 12 * l );
  cur_date.year = 100 * ( n - 49 ) + i + l;
}


/* ************************************************************************
   *                                                                      *
   *      find_time                                                       *
   *                                                                      *
   *   - calculates the time required to determine planet positions       *
   *                                                                      *
   *   Input Variables: Date                                              *
   *                                                                      * 
   *   Returns: time                                                      *
   *                                                                      *
   ************************************************************************ */
GLdouble find_time( GLvoid )
{
  GLdouble time = 0.0;

  time = 367*cur_date.year - 7*( cur_date.year + 
       (cur_date.month+9)/12 )/4 + 275*cur_date.month/9 + 
        cur_date.day - 730530;

  time += cur_date.hour/24.0;

  return time;
}


/* ************************************************************************
   *                                                                      *
   *      Length                                                          *
   *                                                                      *
   *   - Adds two vectors                                                 *
   *                                                                      *
   *   Input Variables: Point3D a - vector whose length is calculated     *
   *                                                                      *
   *   Returns: GLdouble length of vector a                               *
   *                                                                      *
   ************************************************************************ */
GLdouble Length( Point3D a )
{
  return sqrt( a.x*a.x + a.y*a.y + a.z*a.z );  
}


/* ************************************************************************
   *                                                                      *
   *      Normalize                                                       *
   *                                                                      *
   *   - normalize a vector                                               *
   *                                                                      *
   *   Input Variables: Point3D a - vector to be normalized               *
   *                                                                      *
   *   Returns: Point3D a - normalized vector                             *
   *                                                                      *
   ************************************************************************ */
Point3D Normalize( Point3D a )
{
  GLdouble len;

  len = Length(a);
  a.x /= len;
  a.y /= len;
  a.z /= len;

  return a;
}

/* ************************************************************************
   *                                                                      *
   *      Add                                                             *
   *                                                                      *
   *   - Adds two vectors                                                 *
   *                                                                      *
   *   Input Variables: Point3D a & b - vectors to be added               *
   *                                                                      *
   *   Returns: Point3D - sum of a & b                                    *
   *                                                                      *
   ************************************************************************ */
Point3D Add( Point3D a, Point3D b )
{
  Point3D c;

  c.x = a.x + b.x;
  c.y = a.y + b.y; 
  c.z = a.z + b.z;

  return c;
}


/* ************************************************************************
   *                                                                      *
   *      Subtract                                                        *
   *                                                                      *
   *   - Subtracts vector a from b                                        *
   *                                                                      *
   *   Input Variables: Point3D a & b - vectors to be subtracted          *
   *                                                                      *
   *   Returns: Point3D = b-a                                             *
   *                                                                      *
   ************************************************************************ */
Point3D Subtract( Point3D a, Point3D b )
{
  Point3D c;

  c.x = b.x - a.x;
  c.y = b.y - a.y; 
  c.z = b.z - a.z;

  return c;
}


/* ************************************************************************
   *                                                                      *
   *      Scale                                                           *
   *                                                                      *
   *   - Takes a constant and multiplies it times the vector...           *
   *     rescales the vector.                                             *
   *                                                                      *
   *   Input Variables: Point3D a                                         *
   *                                                                      *
   *   Returns: Point3D a - new rescaled vector                           *
   *                                                                      *
   ************************************************************************ */
Point3D Scale( Point3D a, GLdouble multi )
{
  a.x *= multi;
  a.y *= multi;
  a.z *= multi;

  return a;
}

/* ************************************************************************
   *                                                                      *
   *      Negate                                                          *
   *                                                                      *
   *   - Negates a vector                                                 *
   *                                                                      *
   *   Input Variables: Point3D a - vector to be negated                  *
   *                                                                      *
   *   Returns: Point3D - negated vector                                  *
   *                                                                      *
   ************************************************************************ */
Point3D Negate( Point3D a )
{
  a.x *= -1.0;
  a.y *= -1.0;
  a.z *= -1.0;

  return a;
}


/* ************************************************************************
   *                                                                      *
   *      lookat_what                                                     *
   *                                                                      *
   *   - Sets the position of viewing eye                                 *
   *                                                                      *
   *   Input Variables: GLint item - what to look at                      *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid lookat_what( GLint item )
{
  Point3D vec;
  
  if ( item == -1 )
  {
    /* Do nothing special if in FLY_THROUGH mode */
  }
  else if ( item == 0 )
  {
	eyeX = 7.0;
	eyeY = 7.0;
	eyeZ = 7.0;

    centerX = 0.0;
	centerY = 0.0;
	centerZ = 0.0;
  }
  else if ( item > 0 )
  {
	////glRotatef(spin_x, 1.0, 0.0, 0.0);
    ////glRotatef(spin_y, 0.0, 0.0, 1.0);

    vec = Normalize( Subtract( planet_locs[0], planet_locs[item] ) );
    vec = Scale( vec, 1.0 );

    centerX = planet_locs[item].x;
    centerY = planet_locs[item].y;
    centerZ = planet_locs[item].z;

	eyeX = centerX + vec.x + planet[item].dia_scaled*SCALE_FACTOR;
	eyeY = centerY + vec.y + planet[item].dia_scaled*SCALE_FACTOR;
	eyeZ = centerZ + vec.z + planet[item].dia_scaled*SCALE_FACTOR;
  }
  else
    printf("\nError(lookat_what-1): Not able to look at planet %d",item);
}


/* ************************************************************************
   *                                                                      *
   *      distance_to_eye                                                 *
   *                                                                      *
   *   - Determine the distance from an object to the eye point. This     *
   *     is used to determine the level of detail of the object.          *
   *                                                                      *
   *   Input Variables:                                                   *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLdouble distance_to_eye( GLint item )
{
  Point3D a,b;

  a.x = planet_locs[item].x;
  a.y = planet_locs[item].y;
  a.z = planet_locs[item].z;

  b.x = eyeX;
  b.y = eyeY;
  b.z = eyeZ;

  return Length(Subtract(a,b));
}


/* ************************************************************************
   *                                                                      *
   *      load_texture                                                    *
   *                                                                      *
   *   - function to read the texture from a .raw file generated by       *
   *     programs such as Photoshop.                                      *
   *                                                                      *
   *   Input Variables: char *file_name - name of .raw file               *
   *                    GLint width - width of image                      *
   *                    GLint heigh - height of image                     *
   *                    GLint depth - depth                               *
   *                    GLenum color_type - enumaration of color          *
   *                    GLenum filter_type - enumaration of filter        *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid load_texture( char *file_name, GLint width, GLint height, int depth,
                     GLenum color_type, GLenum filter_type )
{
  /* Define some variables */ 
  GLubyte *raw_bitmap;
  FILE *file;
 
  /* Open .raw file if it exists */
  if ((file = fopen(file_name, "rb")) == NULL)
  {
    printf("\nError(load_texture-1): File Not Found : %s/n", file_name);
    exit (1);
  }

  /* allocate memory */
  raw_bitmap = (GLubyte *)malloc(width * height * depth * (sizeof(GLubyte)));

  if (raw_bitmap == NULL)
  {
    printf ("\nError(load_texture-2): Cannot allocate memory for texture/n");
    fclose (file);
    exit (1);
  }
  
  fread (raw_bitmap , width * height * depth, 1 , file);
  fclose (file);

  /* Set OpenGL Texture Image */
  glTexImage2D(GL_TEXTURE_2D, 0, color_type, width, height, 
               0, color_type, GL_UNSIGNED_BYTE, raw_bitmap);
	 
  /* Set Filtering type */
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_type);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_type);
   
  /* Set Texture Evironment */
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  /* Build Mipmaps */
  gluBuild2DMipmaps(GL_TEXTURE_2D, color_type, width, height, 
                    color_type, GL_UNSIGNED_BYTE, raw_bitmap);

  /* Free up the array */
  free (raw_bitmap);   	
}


/* ************************************************************************
   *                                                                      *
   *      LoadGLTextures                                                  *
   *                                                                      *
   *   - function to read the texture from a BITMAP file                  *
   *                                                                      *
   *   Input Variables: Gluint texnum - number of texture                 *
   *                    TCHAR* szFileName - name of texture file          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid LoadGLTextures(GLuint texnum, TCHAR* szFileName)
{
	// Load Textures
	textures[texnum] = auxDIBImageLoadA(szFileName);
	if (!textures[texnum])
	{
		printf("\nError(LoadGLTextures-1) Texture %s Not Found : ",
			 szFileName);
		exit(1);
	}

	// Create Linear Filtered Texture
	glGenTextures(1, &texture_id[texnum]);
	glBindTexture(GL_TEXTURE_2D, texture_id[texnum]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

	//gluBuild2DMipmaps(GL_TEXTURE_2D, 3, textures[0]->sizeX, textures[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, textures[0]->data);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, 
		textures[texnum]->sizeX, textures[texnum]->sizeY, 
		0, GL_RGB, GL_UNSIGNED_BYTE, textures[texnum]->data);

	printf("\n    Loading Texture from file: %s ",szFileName);
}


/* ************************************************************************
   *                                                                      *
   *      init                                                            *
   *                                                                      *
   *   - function which sets all the textures.                            *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      * 
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid init( GLvoid )
{
  GLint i = 0;

  /* Initialize sun to 0,0,0 location */
  planet_locs[0].x = 0.0;
  planet_locs[0].y = 0.0;
  planet_locs[0].z = 0.0;

  glEnable(GL_DEPTH_TEST);
  glFrontFace(GL_CCW);   // ????????? remove this to rotate pics

  /* Set up simple lighting model */
  glEnable(GL_LIGHTING);   

  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient_light);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, source_light);
  glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
  glEnable(GL_LIGHT0);
        
  /* Enable material properties for lighting */
  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

  glEnable(GL_TEXTURE_2D);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(NUM_PLANETS, texture_id);

#ifdef RAW_FILES
  printf("\n Reading Textures \n Read:...");
  fflush(stdout);

  for ( i=1 ; i<=numplanets ; i++ )
  {
    glBindTexture(GL_TEXTURE_2D, texture_id[i]);
      load_texture(planet[i].filename, planet[i].tex_width, 
                   planet[i].tex_height , 7, GL_RGB, GL_NEAREST);
      printf(" %s, ",planet[i].name );
      fflush(stdout);
  }
#endif


#ifdef  BMP_FILES
  printf("\n Reading BMP Textures \n Read:...");
  fflush(stdout);

  for ( i=0 ; i<=numplanets ; i++ )
  {
    LoadGLTextures(i,planet[i].filename);
    fflush(stdout);
  }
#endif

  glEnable(GL_CULL_FACE);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE);
  glClearColor (0.0, 0.0, 0.0, 0.0);
}


/* ************************************************************************
   *                                                                      *
   *      setfont                                                         *
   *                                                                      *
   *   - function which sets all the fonts.                               *
   *                                                                      *
   *   Input Variables: char *name - name of font                         *
   *                    GLint size - size of font                         *
   *                                                                      * 
   *   Returns: nothing                                                   * 
   *                                                                      *
   ************************************************************************ */
GLvoid setfont( char* name, GLint size )
{
  font_style = GLUT_BITMAP_HELVETICA_10;

  if (strcmp(name, "helvetica") == 0) 
  {
    if (size == 12) 
      font_style = GLUT_BITMAP_HELVETICA_12;
    else if (size == 18)
      font_style = GLUT_BITMAP_HELVETICA_18;
  } 
  else if (strcmp(name, "times roman") == 0) 
  {
    font_style = GLUT_BITMAP_TIMES_ROMAN_10;
    if (size == 24)
      font_style = GLUT_BITMAP_TIMES_ROMAN_24;
  } 
  else if (strcmp(name, "8x13") == 0)
  {
    font_style = GLUT_BITMAP_8_BY_13;
  }
  else if (strcmp(name, "9x15") == 0) 
  {
    font_style = GLUT_BITMAP_9_BY_15;
  }
}


/* ************************************************************************
   *                                                                      *
   *      drawstr                                                         *
   *                                                                      *
   *   - function which draws string in "command" window                  *
   *                                                                      *
   *   Input Variables: GLuint - x coordinate of string location          * 
   *                    GLuint - y coordinate of string location          *
   *                    char *format - format of string                   *
   *                    ...                                               *
   *                                                                      * 
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid drawstr( GLuint x, GLuint y, char* format, ...)
{
  va_list args;
  char buffer[255], *s;
    
  va_start(args, format);
  vsprintf(buffer, format, args);
  va_end(args);
    
  glRasterPos2i(x, y);
  for (s = buffer; *s; s++)
    glutBitmapCharacter(font_style, *s);
}


/* ************************************************************************
   *                                                                      *
   *      calculate_location                                              *
   *                                                                      *
   *   - calculates the location of the planet                            *
   *                                                                      *
   *   Input Variables: GLint item - planet to calc. position             *
   *                                                                      * 
   *   Returns: Point3D with location of planet                           *
   *                                                                      *
   ************************************************************************ */
Point3D calculate_location( PlanetData object, GLdouble time )
{
  Point3D loc;
  GLdouble N, i, w, a, e, M,  E=0.0, v, r, xv, yv, E0, E1;
  GLdouble temp1,temp2;

  GLdouble lonsun;

  N = object.N1 + object.N2*time;
  i = object.i1 + object.i2*time;
  w = object.w1 + object.w2*time;
  a = object.a1 + object.a2*time;
  e = object.e1 + object.e2*time;
  M = object.M1 + object.M2*time;
  
  while ( M < 0.0 )
  {
    M += 360.0;
  }
  while ( M > 360.0 )
  {
    M -= 360.0;
  }

  /* Convert to radians */
  M *= PI/180.0;

  if ( !strcmp(object.name, "Moon") )
  {
    while ( N < 0.0 )
	{
      N += 360.0;
	}
    while ( N > 360.0 )
	{
      N -= 360.0;
	}
  }
 
  /* Convert to radians */
  N *= PI/180.0;

  if ( !strcmp(object.name,"Sun") )     /* If sun */
  {
	E = M + e*sin(M) * ( 1.0 + e*cos(M) );    /* in radians */

    xv = cos(E) - e;
    yv = sqrt(1.0 - e*e) * sin(E);

    v = atan2( yv, xv );
    r = sqrt( xv*xv + yv*yv ); 

	lonsun = v + w;
	loc.x = r * cos(lonsun);
    loc.y= r * sin(lonsun);


    loc.z = 0.0;
  }
  else if ( !strcmp(object.name,"Mercury") || 
            !strcmp(object.name,"Venus")   || 
			!strcmp(object.name,"Earth")||
			!strcmp(object.name,"Mars")    || 
			!strcmp(object.name,"Jupiter") || 
			!strcmp(object.name,"Saturn")  || 
			!strcmp(object.name,"Uranus")  || 
			!strcmp(object.name,"Neptune") || 
			!strcmp(object.name,"Pluto")       )

			/* !strcmp(object.name,"Earth")   ||  */
  {
    if ( e <= 0.6 )
      E = M + e*sin(M) * ( 1.0 + e*cos(M) );     /* in radians */
	else 
    {
      E0 = E;
      E1 = E0 - ( E0 - e*sin(E0) - M )/(1 - e*cos(E0) );

      while ( fabs(E1-E0) > 0.00001 )
      {
        E0 = E1;
        E1 = E0 - ( E0 - e*sin(E0) - M )/(1 - e*cos(E0) );
      }
    }

    xv = a * ( cos(E) - e );
    yv = a * ( sqrt(1.0 - e*e) * sin(E) );

    v = atan2( yv, xv );
    r = sqrt( xv*xv + yv*yv );

    temp1 = cos(v+w);
    temp2 = sin(v+w);

	/* Convert to radians */
    i*=PI/180.0;

    loc.x = r * ( cos(N)*temp1 - sin(N)*temp2*cos(i) );
    loc.y = r * ( sin(N)*temp1 + cos(N)*temp2*cos(i) );
    loc.z = r * ( temp2*sin(i) );
  }
  else
  {
    printf("\nError(calculate_location-1): Incorrect planet ");

    loc.x = 0.0;
    loc.y = 0.0;
    loc.z = 0.0;
  }

  return loc;
}


/* ************************************************************************
   *                                                                      *
   *      draw_SaturnsRings                                               *
   *                                                                      *
   *   - Draw the rings of Saturn...simply make a annular disk and put    *
   *            the rings.raw texture on it                               *
   *                                                                      *
   *   Input Variables:  none                                             *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_SaturnsRings( GLvoid )
{
  GLUquadricObj *disk;
  
  glPushMatrix();
    
    glColor3ub(WHITE.R, WHITE.G, WHITE.B);
    
    /* If textures are to be on then use texture info else draw 
       no textured planet */
    if ( show_textures )
      glBindTexture(GL_TEXTURE_2D, texture_id[0]);
    /* Set default color of planet...used primarily when textures are not used. */
    else
      glColor3ub(planet[6].R, planet[6].G, planet[6].B);

	/* Rotate the rings into position */
    glRotatef(20.0, 1.0, 0.0, 0.0);

    /* Draw a disk to represent the rings */
    disk = gluNewQuadric();
    gluQuadricDrawStyle(disk, GLU_FILL);
    gluQuadricNormals(disk, GLU_SMOOTH);
    gluQuadricTexture(disk, GL_TRUE); 
    glDisable(GL_CULL_FACE);
    glEnable(GL_BLEND);
    gluDisk(disk, planet[6].dia_scaled*SCALE_FACTOR+0.1, 
          planet[6].dia_scaled*SCALE_FACTOR+0.7, 25, 25);
    gluDeleteQuadric(disk);
    glDisable(GL_BLEND);
    glEnable(GL_CULL_FACE);
  glPopMatrix();
}


/* ************************************************************************
   *                                                                      *
   *      draw_circle                                                     *
   *                                                                      *
   *   - Draw a circle representing a circular version of the orbit.      *
   *      ... the orbit is draw in the ecliptic plane and is only used    *
   *      for demonstration purposes.                                     *
   *                                                                      *
   *   Input Variables:  GLint item - number representing the planet      *
   *                     number; if item=0 then user has selected to      *
   *                     not draw the orbit.                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_circle( GLint item )
{
  GLdouble rad_angle;
  GLint i = 0;

  if ( item != 0 )
  {
    /* Set the color of the orbit lines */
    glColor3ub(planet[item].R, planet[item].G, planet[item].B);

    rad_angle = 2.0*PI/(80.0+item*10.0);
    /* Begin the loop for drawing lines...this loop requires
       all vertices in order that they should be connected. The
       last vertex connected will be the first vertex intialized   
       thus making a closed loop. */
    glBegin(GL_LINE_LOOP);
      glVertex2f(planet[item].circ_sun_dist,0.0);  /* First (and last) vertex */
      /* Loop through remainder of vertices
         and define their coordinates. */
      for( i=2 ; i<=(80+item*10) ; i++ )
        glVertex2f(planet[item].circ_sun_dist*cos(i*rad_angle),
                   planet[item].circ_sun_dist*sin(i*rad_angle));
    glEnd();
  }
}


/* ************************************************************************
   *                                                                      *
   *      draw_orbits                                                     *
   *                                                                      *
   *   -                                                                  *
   *                                                                      *
   *   Input Variables: GLint item - orbits to draw                       *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_orbits( GLint item )
{
  GLint i = 0;

  if ( item == -1 )          /* All planets        */
  {
    for ( i=1 ; i<=numplanets ; i++ )
      draw_circle(i);	 
  }
  else if ( item == -2 )     /* Inner planets only */
  {
    for ( i=1 ; i<=4 ; i++ )
	  draw_circle(i);
  }
  else if ( item == -3 )     /* Outer planets only */
  {
    for ( i=5 ; i<=numplanets ; i++ )
      draw_circle(i);
  }
  else if ( item >= 0  &&  item <= 9 )  /* specific planet */
  {
    draw_circle(item);
  }
  else
    printf("\nError(draw_planet-1): Incorrectly specified planets");
}


/* ************************************************************************
   *                                                                      *
   *      draw_planet                                                     *
   *                                                                      *
   *   - draws the planet after moving into the correct position          *
   *                                                                      *
   *   Input Variables: GLint item - planet to draw                       *
   *                                                                      * 
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_planet( GLint item, GLdouble time )
{
  GLUquadricObj *sphere;
  Point3D loc;
  GLdouble rad_angle;

  glPushMatrix();

    if ( CIRCULAR )
    {
	  if ( ANIMATE )
	  {
  	    rev_degrees[item] += planet[item].M2*date_update;

	    while ( rev_degrees[item] > 360 )
	      rev_degrees[item] -= 360.0;
      }

	  rad_angle = PI*rev_degrees[item]/180.0;

	  loc.x = planet[item].circ_sun_dist*cos(rad_angle);
	  loc.y = planet[item].circ_sun_dist*sin(rad_angle);
	  loc.z = 0.0;
	}
 	else
      loc = calculate_location(planet[item], time);

	/* update the stored value of the position of each planet */
	planet_locs[item] = loc;

	/* Move into correct position prior to drawing planet */
    glTranslatef( loc.x*POS_SCALE, 
                  loc.y*POS_SCALE, 
                  loc.z*POS_SCALE );

	//glRotatef(90.0, 1.0, 0.0, 0.0);


    glColor3ub(WHITE.R, WHITE.G, WHITE.B);

    /* Determine the level of the planet textures by determining 
	   how far is the eye from the planet object. If very far 
	   then lower the level buf if close then use a lot of detail */
	if ( distance_to_eye(item) > 3.0 )
	  num_plates = 20;     /* If far away then draw in little detail */
    else
	  num_plates = 40;     /* If close then draw in greater detail */

    glPushMatrix();

	  if ( ANIMATE )
	  {
        rot_degrees[item] += planet[item].rot_rate*date_update;

  	    while ( rot_degrees[item] > 360.0 )
          rot_degrees[item] -= 360.0;
	  }

      /* Rotate the planets according to the hour of the day */
      /* First determine the number of degrees to rotate it */
	  glRotatef( rot_degrees[item], 0.0, 0.0, 1.0);

  	  /* If textures are to be on then use texture info else draw 
	   no textured planet */
	  if ( show_textures )
        glBindTexture(GL_TEXTURE_2D, texture_id[item]);
      /* Set default color of planet...used primarily when textures are 
	     not used. */
      else
	    glColor3ub(planet[item].R, planet[item].G, planet[item].B);
    
	  /* Draw a sphere */
	  sphere = gluNewQuadric();
      gluQuadricDrawStyle(sphere, GLU_FILL);
      gluQuadricNormals(sphere, GLU_SMOOTH);
      gluQuadricTexture(sphere, GL_TRUE);
      gluSphere(sphere, planet[item].dia_scaled*SCALE_FACTOR,
		            num_plates, num_plates);
      gluDeleteQuadric(sphere);
    glPopMatrix();

	/* If the planet is SATURN then draw it's rings */
	if ( item == 6 )
      draw_SaturnsRings();

  glPopMatrix();
}


/* ************************************************************************
   *                                                                      *
   *      draw_which                                                      *
   *                                                                      *
   *   - determines which planets to draw and draws them                  *
   *                                                                      *
   *   Input Variables: GLint item - planets to draw                      *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_which( GLint item )
{
  GLint i = 0;
  GLdouble cur_time;

  if ( ANIMATE )
    /* Convert current julian date to the gregorian date to be used by
       the find_time() function */
    julian_to_gregorian();

  /* Find the current time to be used for drawing the planets */
  cur_time = find_time();


  if ( item == -1 )          /* All planets        */
  {
    for ( i=1 ; i<=numplanets ; i++ )
      draw_planet(i,cur_time);	 
  }
  else if ( item == -2 )     /* Inner planets only */
  {
    for ( i=1 ; i<=4 ; i++ )
	  draw_planet(i,cur_time);
  }
  else if ( item == -3 )     /* Outer planets only */
  {
    for ( i=5 ; i<=numplanets ; i++ )
      draw_planet(i,cur_time);
  }
  else if ( item >= 0  &&  item <= 9 )  /* specific planet */
  {
    draw_planet(item,cur_time);
  }
  else
    printf("\nError(draw_planet-1): Incorrectly specified planets");
}


/* ************************************************************************
   *                                                                      *
   *      printstring                                                     *
   *                                                                      *
   *   - displays the characters of a specified string                    *
   *                                                                      *
   *   Input Variables: void *font - font type of characters              *
   *                    char *string - the string                         *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid printstring( GLvoid *font, char *string )
{
  GLint i=0;

  while (string[i])
    glutBitmapCharacter(font,string[i++]);
}


/* ************************************************************************
   *                                                                      *
   *      print_data                                                      *
   *                                                                      *
   *   - Print some text to the screen that has current info.             *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid print_data( GLvoid )
{
  char sbuf[100];

  /* Disable textures and lighting */
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);

  /* Switch viewing method */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0,(GLfloat) (WIN_WIDTH),(GLfloat) WIN_HEIGHT,0.0,0.0,1.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  /* Set text color */
  glColor3ub( WHITE.R, WHITE.G, WHITE.B );

  if ( FREE_MODE )
  {
    /* Print text based on predefined screen positions */
    /* Write date in upper left hand corner */
    glRasterPos2i(5,13);
      sprintf(sbuf," Current Julian Day: %.1lf ",cur_julian);
      printstring( GLUT_BITMAP_HELVETICA_10,sbuf );
    glRasterPos2i(5,23);
      sprintf(sbuf," Gregorian Day     : %2d / %2d / %5d        %5.2lf",
	  	     cur_date.month, cur_date.day, cur_date.year,
		     cur_date.hour);
      printstring( GLUT_BITMAP_HELVETICA_10,sbuf );

    /* Set text color to GREEN */
    glColor3ub( GREEN.R, GREEN.G, GREEN.B );
    if ( selected_planet >= 0 )
	{
      glRasterPos2i(5,33);
        sprintf(sbuf," Viewing: %s ",planet[selected_planet].name);
        printstring( GLUT_BITMAP_HELVETICA_10,sbuf );
	  glRasterPos2i(650,15);
	    sprintf(sbuf," Observer Mode ");
	    printstring( GLUT_BITMAP_HELVETICA_10,sbuf );
	}
    else if ( selected_planet == -1 )
	{
      glRasterPos2i(650,15);
	    sprintf(sbuf," Fly-Through Mode ");
	    printstring( GLUT_BITMAP_HELVETICA_10,sbuf );
	}
  
    /* Set text color to WHITE again */
    glColor3ub( WHITE.R, WHITE.G, WHITE.B );

    /* Write Frames/sec in lower left hand corner */
    glRasterPos2i(5,500);
      sprintf(sbuf," Frames/sec = %6.2f ",frames_per_sec);
	  printstring( GLUT_BITMAP_HELVETICA_10,sbuf );

    if( PRINT_HELP )
	{
      glRasterPos2i((GLint)sub_width/2,100);
        sprintf(sbuf," Help Commands ");
        printstring( GLUT_BITMAP_HELVETICA_10,sbuf );
	}
  } 
  else if ( INTRO )
  {
    /* Print intro screen */

	glColor3ub( RED.R, RED.G, RED.B );
	glRasterPos2i(270, 220);
      sprintf(sbuf,"Solar System Simulator"); 
      printstring( GLUT_BITMAP_TIMES_ROMAN_24, sbuf );

    glColor3ub( YELLOW.R, YELLOW.G, YELLOW.B );
    glRasterPos2i(275, 250);
      sprintf(sbuf,"F1 - Intro screen (this one)");
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	
   
    glRasterPos2i(275, 275);
      sprintf(sbuf,"F2 - Free Fly Mode");
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	

    glRasterPos2i(275, 300);
      sprintf(sbuf,"F3 - Read Input From File");
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	
  
    glRasterPos2i(275, 325);
      sprintf(sbuf,"F12 - Screen Saver");
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );

    glRasterPos2i(275, 350);
      sprintf(sbuf,"[q] / [esc] - Quit the program");
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );

    glColor3ub( GREEN.R, GREEN.G, GREEN.B );
    glRasterPos2i(20, 500);
      sprintf(sbuf,"Mariusz Zaczek (zaczek@uiuc.edu)"); 
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );

    glRasterPos2i(640, 500);
      sprintf(sbuf,"Version 1.0"); 
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );
	  
  }
  else if ( FILE_INPUT && !SCREEN_SAVER )
  {
    glColor3ub( RED.R, RED.G, RED.B );
	glRasterPos2i(270, 220);
      printstring( GLUT_BITMAP_TIMES_ROMAN_24, title );
  }
  else if ( SCREEN_SAVER )
  {
    /* Display info for current planet being displayed. */
	glColor3ub( RED.R, RED.G, RED.B );
	glRasterPos2i(50, 100);
      printstring( GLUT_BITMAP_TIMES_ROMAN_24, 
		           planet[scrnsvr_planet].name );

    glColor3ub( YELLOW.R, YELLOW.G, YELLOW.B );
    glRasterPos2i(60, 125);
      sprintf(sbuf,"Number of Satellites: %d",
		           planet[scrnsvr_planet].num_sats );
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	
   
    glRasterPos2i(60, 150);
      sprintf(sbuf,"Distance to Sun: %lf",
		           planet[scrnsvr_planet].circ_sun_dist );
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	

    glRasterPos2i(60, 175);
      sprintf(sbuf,"Orbit Rate: %lf",
		           planet[scrnsvr_planet].orbit_rate );
      printstring( GLUT_BITMAP_HELVETICA_18, sbuf );	
  }


  /* Set text color to WHITE again */
  /* hmmm.,.... if this is not here then my colors are
     screwed up ... MUST FIX */
  glColor3ub( WHITE.R, WHITE.G, WHITE.B ); 


  
  /* Re-enable textures and lighting */
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_LIGHTING);
  
  glutSwapBuffers();
}


/* ************************************************************************
   *                                                                      *
   *      draw_axes                                                       *
   *                                                                      *
   *   - Draw temporary axes centered at 0.0,0.0,0.0.                     *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_axes( GLvoid )
{
  glColor3f(1.0,0.0,0.0);
  glBegin(GL_LINES);
    glVertex3d(0,0,0);
    glVertex3d(5,0,0);
  glEnd();

  glColor3f(0.0,1.0,0.0);
  glBegin(GL_LINES);
    glVertex3d(0,0,0);
    glVertex3d(0,5,0);
  glEnd();

  glColor3f(0.0,0.0,1.0);
  glBegin(GL_LINES);
    glVertex3d(0,0,0);
    glVertex3d(0,0,5);
  glEnd();
}

/* ************************************************************************
   *                                                                      *
   *      draw_solarsystem                                                *
   *                                                                      *
   *   - This is the main function which calls all the draw functions     *
   *     for the solarsystem. This includes the planets, stars, etc.      *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_solarsystem( GLvoid )
{
  GLUquadricObj *disk, *q;

  disk = gluNewQuadric();
  q = gluNewQuadric();

  glClear(GL_COLOR_BUFFER_BIT);
  //glDisable(GL_LIGHTING);
  glEnable(GL_LIGHTING);

  glClear(GL_COLOR_BUFFER_BIT);
  glLightfv(GL_LIGHT0,GL_POSITION,light_pos);
 
  /* Draw stars */
  if ( show_screen_stars)
    draw_stars();

  if ( show_textures )
    glEnable(GL_TEXTURE_2D);
  else
    glDisable(GL_TEXTURE_2D);

  /* Call function which determines which planets to draw and draws them */
  draw_which(solarsys);

  /* Draw orbit circles if user wants to */
  if ( show_screen_orbits )
    draw_orbits(solarsys);

  if ( DRAW_AXES )
    draw_axes();

  if ( FILE_INPUT )
    draw_trajectory();

  /* Write info on screen */
  if ( PRINT_INFO )
    print_data();

  glutSwapBuffers();
}


/* ************************************************************************
   *                                                                      *
   *      main_keyboard                                                   *
   *                                                                      *
   *   - function which senses keyboard key presses. Selected keys have   *
   *      commands associated with them.                                  *
   *                                                                      *
   *   Input Variables: key - key pressed                                 *
   *                    GLint x - x location of mouse at key press        *
   *                    GLint y - y location of mouse at key press        *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
static GLvoid main_keyboard( unsigned char key, GLint x, GLint y )
{
  switch (key) 
  { 
    case '0':
	  selected_planet = 0;
      break;
    case '1':
	  selected_planet = 1;
      break;
    case '2':
	  selected_planet = 2;
      break;
    case '3':
	  selected_planet = 3;
      break;
    case '4':
	  selected_planet = 4;
      break;
    case '5':
	  selected_planet = 5;
      break;
    case '6':
	  selected_planet = 6;
      break;
    case '7':
	  selected_planet = 7;
      break;
    case '8':
	  selected_planet = 8;
      break;
    case '9':
	  selected_planet = 9;
      break;	  
    case 'a':                   /* Animate on/off */
      ANIMATE = !ANIMATE;
	  break;
	case 'c':
	  CIRCULAR = !CIRCULAR;
      break;
	case 'f':
	  FLY_THROUGH = 1;
	  break;
    case 'F':
	  FLY_THROUGH = 0;
	  break;
    case 'h':                   /* help screen */
	  PRINT_HELP = !PRINT_HELP;
	  break;
    case 'i':
	  PRINT_INFO = !PRINT_INFO; /* Print some info to screen; frame rate,etc */
      break;
	case 'O':                   /* Toggle orbits on/off */
      show_screen_orbits = !show_screen_orbits;
	  break;
	case 'q':                   /* Quit / exit program */
	case 'Q':
    case 27:                    /* ESC key ... quit program */
  	  exit(0);
	case 'r':                   /* Toggle playing animation forward/reverse */
      date_update = -1*date_update;
	  break;
	case 'R':                   /* Reset to date: Jan. 1st, 2000 */
	  date_update = 0.01;
      cur_julian = 2451545.0;

	   eyeX = 3.0;
	   eyeY = 2.0;
	   eyeZ = 3.0;
	  break;
	case 's':                   /* Stop animation ... not really needed */
	  ANIMATE = 0;
	  break;
	case 'S':                   /* Toggle stars on/off */
      show_screen_stars = !show_screen_stars;
	  break;
	case 'T':                   /* Toggle textures on/off */
      show_textures = !show_textures;
	  break;
	case 'X':                   /* Draw red,green,blue axes lines */
      DRAW_AXES = !DRAW_AXES;
	  break;
	case '+':
	  date_update += 0.5;       /* Increase animation speed */
	  break;
    case '-':
	  date_update -= 0.5;       /* Decrease animation speed */
	  break;
	default:
	  return;   /* Avoid the glutPostRedisplay() */
  }

  glutPostRedisplay();
}


/* ************************************************************************
   *                                                                      *
   *      specialFunc                                                     *
   *                                                                      *
   *   - function which senses keyboard key presses. Selected keys have   *
   *      commands associated with them.                                  *
   *                                                                      *
   *   Input Variables: GLint key - key pressed                           *
   *                    GLint x - x location of mouse at key press        *
   *                    GLint y - y location of mouse at key press        *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
static void specialFunc( GLint key, GLint x, GLint y )
{
  switch(key) 
  {
	case GLUT_KEY_HOME:
	case GLUT_KEY_F1:
	  INTRO = 1;
	  FREE_MODE = 0;
	  SCREEN_SAVER = 0;
      Intro();
	  break;
	case GLUT_KEY_F2:
      INTRO = 0;
      FREE_MODE = 1;
	  SCREEN_SAVER = 0;
      break;
	case GLUT_KEY_F3:
	  INTRO = 0;
	  FILE_INPUT = !FILE_INPUT;
	  SCREEN_SAVER = 0;
      
	  if ( FILE_INPUT )
	  { 
		FREE_MODE = 0;
        FileInput();
      }
	  else
	    FREE_MODE = 1;

	  break;
    case GLUT_KEY_F12:
	  SCREEN_SAVER = !SCREEN_SAVER;
	  INTRO = 0;
	  FREE_MODE = 0;
	  if ( SCREEN_SAVER )
  	    ScreenSaver();
	  break;
    case GLUT_KEY_UP:        /* The up arrow key was hit. */
	  reverse = 0.1; 
	  break;
    case GLUT_KEY_DOWN:      /* The down arrow key is pushed. */
	  forward = 0.1;
      break;
	case GLUT_KEY_LEFT:      /* Left arrow key */
	  left = 0.1;
	  break;
	case GLUT_KEY_RIGHT:     /* Right arrow key */
	  right = 0.1;
	  break;
	case GLUT_KEY_PAGE_UP:   /* Page-up */
	  up = 0.1;
	  break;
	case GLUT_KEY_PAGE_DOWN: /* Page-down */
	  down = 0.1;
	  break;
    default:
	  return;   /* Avoid the glutPostRedisplay() */
  }

  glutPostRedisplay();
}


/* ************************************************************************
   *                                                                      *
   *      screen_reshape                                                  *
   *                                                                      *
   *   - "screen" reshape function when window is resized                 *
   *                                                                      *
   *   Input Variables: GLint width - new window width                    *
   *                    GLint height - new window height                  *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid main_reshape( GLint width, GLint height )
{
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(60.0, (GLfloat)width/height, 0.01, 1000.0);
  glGetDoublev(GL_PROJECTION_MATRIX, projection);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}


/* ************************************************************************
   *                                                                      *
   *      Camera                                                          *
   *                                                                      *
   *   - set the camera position                                          *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid Camera( GLvoid )
{
  GLdouble pos[4];

   /* Where are we looking at ... This will change based on the 
     menu choices....but default is the perspective view. */
  glPushMatrix();
    glLightfv(GL_LIGHT1, GL_POSITION, (GLfloat*)pos);
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glClearColor(0.0,0.0,0.0,0.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT1);
  glPopMatrix();

  if ( FREE_MODE )
  {

    lookat_what( selected_planet );

    // Most likely I have to use the spin_x,spin_y values to determine
    // position of eye when rotation is used.
 
    gluLookAt(    eyeX,    eyeY,    eyeZ,
    	       centerX, centerY, centerZ,
	  	           upX,     upY,     upZ  );
  }
  else if ( INTRO )
  {
    /* Set gluLookAt for the Intro screen */
	gluLookAt(   0.25,   0.0,   0.0,
		         0.0,     0.0,   0.0,
				 0.0,     0.0,   1.0   );
  }
  else if ( SCREEN_SAVER )
  {
    /* Set gluLookAt for the Intro screen */
	gluLookAt( planet[scrnsvr_planet].dia_scaled*SCALE_3x_FACTOR,
		                         0.0,   0.0,
		                  0.0,   0.0,   0.0,
				          0.0,   0.0,   1.0   );
  }
}


/* ************************************************************************
   *                                                                      *
   *      screen_display                                                  *
   *                                                                      *
   *   - "screen" display function for drawing solar system               *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid main_display( GLvoid )
{
  glLoadIdentity();
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  /* Define eye position and what I'm looking at */
  Camera();

  if ( FREE_MODE )
  {
    glPushMatrix();
      glRotatef(spin_x, 1.0, 0.0, 0.0);
      glRotatef(spin_y, 0.0, 0.0, 1.0);
   	  glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    
      glEnable(GL_LIGHTING);

	    if ( show_textures )
          glEnable(GL_TEXTURE_2D);
	    else
	      glDisable(GL_TEXTURE_2D);

   	    draw_solarsystem();

      glDisable(GL_LIGHTING);
    glPopMatrix();
  }
  else if ( INTRO )
  {
    /* Draw Intro screen ... */
	Intro();

	/* Draw stars */
	draw_stars();

	/* Print Menu to screen ... */
    print_data();
  }
  else if ( FILE_INPUT && !SCREEN_SAVER )
  {
    print_data();
  }
  else if ( SCREEN_SAVER )
  {
    /* Draw Screen Saver screen ... */
	ScreenSaver();

	/* Draw stars */
	draw_stars();

	print_data();
  }

  glutSwapBuffers();
}


/* ************************************************************************
   *                                                                      *
   *      screen_mouse                                                    *
   *                                                                      *
   *   - mouse control when in "screen" window                            *
   *                                                                      *
   *   Input Variables: GLint button - which button pressed               *
   *                    GLint state  - state of mouse button (UP or DOWN) *
   *                    GLint x      - x location of mouse                *
   *                    GLint y      - y location of mouse                *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_mouse(GLint button, GLint state, GLint x, GLint y )
{
  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 
  {
    old_x = x;  
    old_y = y; 
  }  
}
 

/* ************************************************************************
   *                                                                      *
   *      screen_motion                                                   *
   *                                                                      *
   *   - what should do when mouse is in motion. Mostly used to rotate    *
   *     the world around.                                                *
   *                                                                      *
   *   Input Variables: GLint x      - x location of mouse                *
   *                    GLint y      - y location of mouse                *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_motion( GLint x, GLint y )
{
  /* Store global positions of mouse on screen */
  mouse_x = x;
  mouse_y = y;

  /* Determine spin angles */
  spin_x += (old_y - y);
  old_y = y;

  if (spin_x >= 360)
      spin_x -= 360.0;

  spin_y += (old_x - x);
  old_x = x;

  if (spin_y >= 360)
      spin_y -= 360.0;

  glutPostRedisplay();
}


/* ************************************************************************
   *                                                                      *
   *      redisplay_all                                                   *
   *                                                                      *
   *   - function to redisplay all windows                                *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      * 
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid redisplay_all( GLvoid )
{
  sub_width=glutGet(GLUT_WINDOW_WIDTH);
  sub_height=glutGet(GLUT_WINDOW_HEIGHT);

  main_reshape(sub_width, sub_height);
  glutPostRedisplay();
}


/* ************************************************************************
   *                                                                      *
   *      draw_stars                                                      *
   *                                                                      *
   *   - draws the stars                                                  *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid draw_stars( GLvoid )
{ 
  glDisable(GL_TEXTURE_2D);
    glColor3f( WHITE.R, WHITE.G, WHITE.B );
    glCallList(Stars);
  glEnable(GL_TEXTURE_2D);
}


/* ************************************************************************
   *                                                                      *
   *      InitStars                                                       *
   *                                                                      *
   *   - Initialize the stars and store them in a list                    *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid InitStars( int read_file )
{ 
  static char line[100],*tmp;
  FILE *f=NULL;
  static float ascention,declination,old_mag,magnitude;
  GLint i,j;
  GLdouble dist;
  GLint modulus_count;

  dist = .5;
  modulus_count = 250;

  if (read_file) 
  {
    sprintf(line,"stars.dat",".");
	f=fopen(line,"rt");
	if (f==NULL) 
	{
      printf("\nError(InitStars-1): Star input file is missing...exiting\n");
	  exit(-1);
	}
  }

  for (i=0;i<NUMSTARS;i++) 
  {
	if ( !(i%modulus_count) )
    {
      dist += 0.1;
	  modulus_count += 250;
    }

   	if (read_file) 
	{
	  fgets(line,100,f);
	  if (feof(f)) break;
	  while (line[0]=='#') fgets(line,100,f);
	  j=0;
	  while (line[j]!=',') j++;
	  line[j]='\0';
	  tmp=line+j+1;
	  strcpy(stars[i].Name,line);
	  sscanf(tmp,"%f,%f,%f,%c\n",&ascention,&declination,
			&stars[i].magnitude,&stars[i].type);
			
	  /* Translate declination/ascention coord into x,y,z */
	  stars[i].posx=dist*100.0*cos(-ascention)*cos(declination);
	  stars[i].posy=dist*100.0*sin(declination);
	  stars[i].posz=dist*100.0*sin(-ascention)*cos(declination);
    }
  }
 
  if (read_file) fclose(f);

  Stars = glGenLists(1);
    glPointSize(star_size);
	glClearColor(0.0, 0.0, 0.0, 0.0);
    glColor3f(0.0,0.0,1.0);
    glNewList( Stars, GL_COMPILE );
      glBegin(GL_POINTS);
        for (i=0;i<NUMSTARS-100;i++) 
  	      glVertex3f(stars[i].posx,stars[i].posy,stars[i].posz);
      glEnd();  
  glEndList();

}


/* ************************************************************************
   *                                                                      *
   *      menuFuncMain                                                    *
   *                                                                      *
   *   - The main menu selections. Currently only the exit (quit)         *
   *     selection can be made                                            *
   *                                                                      *
   *   Input Variables: GLint id - which menu selection was made          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid menuFuncMain( GLint id )
{
  /* main menu commands */
  switch( id )
  {
    case MENU_EXIT:
       exit( 0 );
       break;
  }
}


/* ************************************************************************
   *                                                                      *
   *      screen_menuFuncViews                                            *
   *                                                                      *
   *   -  The user selects which planet(s) to view. The selection made    *
   *      will cause the users eye point to be focused on the selected    *
   *      planet(s) and rotation will be done relative to the selected    *
   *      planet(s). The planet(s) are drawn in the "screen" window.      *
   *                                                                      *
   *   Input Variables: GLint id - which menu selection was made          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_menuFuncViews( GLint id )
{
  switch( id )
  {
    case SCREEN_MENU_ENTIRE:
	  solarsys = ENTIRE;
	  selected_planet = 0;
	  break;
    case SCREEN_MENU_INNER:
	  solarsys = INNER;
      selected_planet = 0;
	  // SET VIEW
	  break;
	case SCREEN_MENU_SUN:
	case SCREEN_MENU_MERCURY:
	case SCREEN_MENU_VENUS:
	case SCREEN_MENU_EARTH:
	case SCREEN_MENU_MARS:
	case SCREEN_MENU_JUPITER:
	case SCREEN_MENU_SATURN:
	case SCREEN_MENU_URANUS:
	case SCREEN_MENU_NEPTUNE:
	case SCREEN_MENU_PLUTO:
	  selected_planet = id;
  	  break;
  }
}


/* ************************************************************************
   *                                                                      *
   *      screen_menuFuncDisplay                                          *
   *                                                                      *
   *   - Display function which currently toggles displaying textures,    *
   *     stars, orbits.                                                   *
   *                                                                      *
   *   Input Variables: GLint id - which menu selection was made          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_menuFuncDisplay( GLint id )
{
  /* change settings based on what we want to show */
  switch( id )
  {
	/* Toggle displaying textures */
    case SCREEN_MENU_TEXTURES:
      show_textures = !show_textures;
	  break;
	/* Toggle displaying stars */
    case SCREEN_MENU_STARS:
	  show_screen_stars = !show_screen_stars;
	  break;
	/* Toggle displaying circular orbits */
    case SCREEN_MENU_ORBITS:
	  show_screen_orbits = !show_screen_orbits;
      break;
  }

  glutPostRedisplay();
}


/* ************************************************************************
   *                                                                      *
   *      screen_menuFuncMotion                                           *
   *                                                                      *
   *   - Motion function where the user selects what actions to take;     *
   *     these include Zooming, Rotating and Flying through the solar     *
   *     system.                                                          *
   *                                                                      *
   *   Input Variables: GLint id - which menu selection was made          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_menuFuncMotion( GLint id )
{
  /* find out which motion type was selected */
  switch( id )
  {
    case SCREEN_MENU_ZOOM:
         
      break;
    case SCREEN_MENU_ROTATE:

      break;
	case SCREEN_MENU_FLY:
      FLY_THROUGH = !FLY_THROUGH;
	  break;
  }
}


/* ************************************************************************
   *                                                                      *
   *      screen_menuFuncAnimation                                        *
   *                                                                      *
   *   -  Function which allows the user to set the animation procedures. *
   *                                                                      *
   *   Input Variables: GLint id - which menu selection was made          *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid screen_menuFuncAnimation( GLint id )
{
  /* animation control menu */
  switch( id )
  {
    /* Animate solar system */
    case SCREEN_MENU_ANIMATE:
      date_update = fabs(date_update);
      ANIMATE = 1;
      break;
    /* Play animation in reverse ... decreasing dates */
    case SCREEN_MENU_REVERSE:
      date_update = -1*fabs(date_update);
      redisplay_all();
      ANIMATE = 1;
      break;
    /* Stop the animation */
    case SCREEN_MENU_STOP:
      ANIMATE = 0;
      break;
    /* Reset the speed of the animation */
    case SCREEN_MENU_RESET:
      date_update = 1.0;
      cur_julian = 2451545.0;
	break;
    /* Increase the speed of the forward animation */
	case SCREEN_MENU_FWD:
	  date_update += 1.0;
  	  redisplay_all();
	break;
    /* Increase the speed of the reverse animation */
    case SCREEN_MENU_REV:
      date_update -= 1.0;
      redisplay_all();
      break;
  }
}


/* ************************************************************************
   *                                                                      *
   *      idle_func                                                       *
   *                                                                      *
   *   - when nothing is happening do animations if set                   *
   *                                                                      *
   *   Input Variables: none                                              *
   *                                                                      *
   *   Returns: nothing                                                   *
   *                                                                      *
   ************************************************************************ */
GLvoid idle_func( GLvoid )
{ 
  if ( FREE_MODE )
  {  
    if ( frames == 0 )
      sec=glutGet(GLUT_ELAPSED_TIME);

    if ( frames == 10 )
	{
      sec=(glutGet(GLUT_ELAPSED_TIME)-sec)/1000.0;
      frames_per_sec = (double)frames/sec; 
      frames = 0;
	}
    else 
      frames++;    /* Increase the frame counter */

    /* If animation is set to ON the increase the current time/date to show
       the planets' rotations and revolutions */
    if ( ANIMATE )
	{
      cur_julian += date_update;
      
      //eyeX -= 1.0;
      //eyeY -= 1.0;
	}

    if ( FLY_THROUGH )
      Fly();

  }
  else if ( INTRO || SCREEN_SAVER )
  {
    /* Do idle stuff for INTRO */
    intro_angle += 0.005*planet[4].rot_rate;
//    intro_clouds += 0.006*planet[4].rot_rate;;

	while ( intro_angle > 360 )
	  intro_angle -= 360.0;

//	while ( intro_clouds > 360 )
//	  intro_clouds -= 360.0;
  }

  if ( SCREEN_SAVER )
  {
    scrnsvr_counter++;
	 
	if ( scrnsvr_counter == 500 )
	{
	  scrnsvr_counter = 0;
      scrnsvr_planet++;

	  if ( scrnsvr_planet == 10 )
	    scrnsvr_planet = 0;
	}
  }

  redisplay_all();   /* Needed for some reason??...hmm */
}


/* ************************************************************************
   *                                                                      *
   *      main                                                            *
   *                                                                      *
   *   - main code which initializes the glut windows and calls the       *
   *     texture functions.                                               *
   *                                                                      *
   *   Input Variables:  GLint argc - number of input arguments           *
   *                     char** argv - list of arguments                  *
   *                                                                      *
   ************************************************************************ */
GLint main( GLint argc, char** argv )
{
  GLint screen_menuId, screen_sub1, screen_sub2, screen_sub3, screen_sub4;
  GLint temp;

  if ( argc > 1 )
  {
    fullscreen = atoi(argv[1]);
  }
  else
	fullscreen = 0;

  if ( argc > 2 )
  {
    temp = atoi(argv[2]);

    if ( temp == 2 )
	{	  
	  INTRO = 0;
	  FREE_MODE = 1;
	}
    else if ( temp == 3 )
	{	  
	  INTRO = 0;
	  FILE_INPUT = 1;
	}
	else if ( temp == 4 )
    {
      INTRO = 0;
      SCREEN_SAVER = 1;
	}
	else
      printf("\nError(main-1): Wrong input ... no such mode %d",temp);
  }
  glutInit(&argc, argv);

  /* Initialize display for rgb mode, depth calculations, double buffering */
  glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_STENCIL);
 
  /* Define entire glut window: size,position,etc */
  glutInitWindowSize( WIN_WIDTH, WIN_HEIGHT );
  glutInitWindowPosition(50, 50);


  /* If "fullscreen" is set then display window in fullscreen mode ... this  
     disables the mouse menu functions ... 
     NOTE: the GAME MODE is valid with the newest version of GLUT (3.7.2) */
  if (fullscreen) 
  {
//    if (smallFullscreen)
//      glutGameModeString("600x480:32@70");
//    else
//     glutGameModeString("800x600:32@70");

//    glutEnterGameMode();
    glutSetCursor(GLUT_CURSOR_NONE);    /* removes mouse from full view...
										   if commented out then mouse 
										   returns but still no pull down 
										   menu capability */
	/* choices for glutSetCursor are:
 	    GLUT_CURSOR_RIGHT_ARROW         - Arrow pointing up and to the right. 
        GLUT_CURSOR_LEFT_ARROW          - Arrow pointing up and to the left. 
        GLUT_CURSOR_INFO                - Pointing hand. 
        GLUT_CURSOR_DESTROY             - Skull & cross bones. 
        GLUT_CURSOR_HELP                - Question mark. 
        GLUT_CURSOR_CYCLE               - Arrows rotating in a circle. 
        GLUT_CURSOR_SPRAY               - Spray can. 
        GLUT_CURSOR_WAIT                - Wrist watch. 
        GLUT_CURSOR_TEXT                - Insertion point cursor for text. 
        GLUT_CURSOR_CROSSHAIR           - Simple cross-hair. 
        GLUT_CURSOR_UP_DOWN             - Bi-directional pointing up & down. 
        GLUT_CURSOR_LEFT_RIGHT          - Bi-directional pointing left & right. 
        GLUT_CURSOR_TOP_SIDE            - Arrow pointing to top side. 
        GLUT_CURSOR_BOTTOM_SIDE         - Arrow pointing to bottom side. 
        GLUT_CURSOR_LEFT_SIDE           - Arrow pointing to left side. 
        GLUT_CURSOR_RIGHT_SIDE          - Arrow pointing to right side. 
        GLUT_CURSOR_TOP_LEFT_CORNER     - Arrow pointing to top-left corner. 
        GLUT_CURSOR_TOP_RIGHT_CORNER    - Arrow pointing to top-right corner. 
        GLUT_CURSOR_BOTTOM_RIGHT_CORNER - Arrow pointing to bottom-left corner. 
        GLUT_CURSOR_BOTTOM_LEFT_CORNER  - Arrow pointing to bottom-right corner. 
        GLUT_CURSOR_FULL_CROSSHAIR      - Full-screen cross-hair cursor  
        GLUT_CURSOR_NONE                - Invisible cursor. 
        GLUT_CURSOR_INHERIT             - Use parent's cursor. 
	*/
  } 
  else
    glutCreateWindow("Solar System v2.0");


  /* Set window title and icon title */
  glutSetWindowTitle("Solar System Simulation");
  glutSetIconTitle("Solar System");

  /* Function to call when size of window changes */
    glutReshapeFunc( main_reshape );

    /* Display function */
    glutDisplayFunc( main_display );

    /* Tell OpenGL what to do when keys are pressed */
    glutKeyboardFunc( main_keyboard );
	glutSpecialFunc(specialFunc);

    /* Do something when idle */
    glutIdleFunc( idle_func );

    /* Tell OpenGL what to do when mouse is moved */
    glutMotionFunc( screen_motion );

    /* Tell OpenGL what to do when mouse is used */
    glutMouseFunc( screen_mouse );

  /* Intialize the stars */
  InitStars(1);

  /* Read in the planet textures */
  init();


  if ( !fullscreen )
  {
    /* Make menu for the 3D screen */
    screen_sub1 = glutCreateMenu( screen_menuFuncViews );
      glutAddMenuEntry("Solar System",  SCREEN_MENU_ENTIRE   );
      glutAddMenuEntry("Inner Planets", SCREEN_MENU_INNER    );
      glutAddMenuEntry("Sun",           SCREEN_MENU_SUN      );
      glutAddMenuEntry("Mercury",       SCREEN_MENU_MERCURY  );
      glutAddMenuEntry("Venus",         SCREEN_MENU_VENUS    );
      glutAddMenuEntry("Earth",         SCREEN_MENU_EARTH    );
      glutAddMenuEntry("Mars",          SCREEN_MENU_MARS     );
      glutAddMenuEntry("Jupiter",       SCREEN_MENU_JUPITER  );
      glutAddMenuEntry("Saturn",        SCREEN_MENU_SATURN   );
      glutAddMenuEntry("Uranus",        SCREEN_MENU_URANUS   );
      glutAddMenuEntry("Neptune",       SCREEN_MENU_NEPTUNE  );
      glutAddMenuEntry("Pluto",         SCREEN_MENU_PLUTO    );

    screen_sub2 = glutCreateMenu( screen_menuFuncDisplay    );
      glutAddMenuEntry("Textures",      SCREEN_MENU_TEXTURES );
      glutAddMenuEntry("Stars",         SCREEN_MENU_STARS    );
  	  glutAddMenuEntry("Orbit Lines",   SCREEN_MENU_ORBITS   );

    screen_sub3 = glutCreateMenu( screen_menuFuncMotion );
      glutAddMenuEntry("Zoom",          SCREEN_MENU_ZOOM     );
      glutAddMenuEntry("Rotate",        SCREEN_MENU_ROTATE   );
  	  glutAddMenuEntry("Fly through",   SCREEN_MENU_FLY      );

    screen_sub4 = glutCreateMenu( screen_menuFuncAnimation );
      glutAddMenuEntry("Animate(play)", SCREEN_MENU_ANIMATE  );
      glutAddMenuEntry("Play Backwards",SCREEN_MENU_REVERSE  );
  	  glutAddMenuEntry("Stop",          SCREEN_MENU_STOP     );
	  glutAddMenuEntry("Reset",         SCREEN_MENU_RESET    );
      glutAddMenuEntry("Increase Fwd",  SCREEN_MENU_FWD      );
	  glutAddMenuEntry("Increase Rev",  SCREEN_MENU_REV      );

  /* main SCREEN menu */
    screen_menuId = glutCreateMenu( menuFuncMain );
      glutAddSubMenu("Set view of...", screen_sub1 );
      glutAddSubMenu("Display.......", screen_sub2 );
      glutAddSubMenu("Motion Type...", screen_sub3 );
      glutAddSubMenu("Animation.....", screen_sub4 );
      glutAddMenuEntry("Exit",         MENU_EXIT );

  /* attach as popup to right mouse button */
    glutSetMenu( screen_menuId );
    glutAttachMenu( GLUT_RIGHT_BUTTON );
  }  /* End: if ( !fullscreen )  */

  /* Enter the main loop */
  glutMainLoop();

  return 0;
}
