#include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>

/* TEST PROGRAM */

#define CHECK_ERROR(str)                                           \
{                                                                  \
    GLenum error;                                                  \
    if(error = glGetError())                                       \
       printf("GL Error: %s (%s)\n", gluErrorString(error), str);  \
}

enum {SPHERE = 1, CONE};



/* Called when window needs to be redrawn */
void redraw()
{
    /* material properties for objects in scene */
    static GLfloat wall_mat[] = {1.f, 1.f, 1.f, 1.f};

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    /*
    ** Note: wall verticies are ordered so they are all front facing
    ** this lets me do back face culling to speed things up.
    */
 
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat);
    glColor3f(1.f, 1.f, 1.f);


    glBegin(GL_QUADS);

    /* floor */
    glNormal3f(0.f, 1.f, 0.f);
    glVertex3f(-100.f, -100.f,  100.f);
    glVertex3f( 100.f, -100.f,  100.f);
    glVertex3f( 100.f, -100.f, -100.f);
    glVertex3f(-100.f, -100.f, -100.f);

    /* left wall */
    glNormal3f(1.f, 0.f, 0.f);
    glVertex3f(-100.f, -100.f,  100.f);
    glVertex3f(-100.f, -100.f, -100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glVertex3f(-100.f,  100.f,  100.f);


    /* right wall */
    glNormal3f(-1.f, 0.f, 0.f);
    glVertex3f( 100.f, -100.f,  100.f);
    glVertex3f( 100.f,  100.f,  100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f( 100.f, -100.f, -100.f);


    /* ceiling */
    glColor3f(0.f, .5f, 1.f);
    glNormal3f(0.f, -1.f, 0.f);
    glVertex3f(-100.f,  100.f,  100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f( 100.f,  100.f,  100.f);


    /* back wall */
    glColor3f(.2f, .5f, 1.f);
    glNormal3f(0.f, 0.f, 1.f);
    glVertex3f(-100.f, -100.f, -100.f);
    glVertex3f( 100.f, -100.f, -100.f);
    glVertex3f( 100.f,  100.f, -100.f);
    glVertex3f(-100.f,  100.f, -100.f);
    glEnd();


    /* draw the sphere */
    glColor3f(1.f, .5f, 0.f);
    glPushMatrix();
    glTranslatef(0.f, -80.f, -80.f);
    glCallList(SPHERE);
    glPopMatrix();

    /* draw the cone */
    glColor3f(.5f, 1.f, 0.f);
    glPushMatrix();
    glTranslatef(-60.f, -100.f, -5.f);
    glCallList(CONE);
    glPopMatrix();

    CHECK_ERROR("OpenGL Error in redraw()");

    glFlush(); /* some OpenGL implementations need this if not animating */
}


void key(unsigned char key, int x, int y)
{
    if(key == '\033')
	exit(0);
}



main(int argc, char *argv[])
{
    static GLfloat lightpos[]   = {50.f, 50.f, 0.f, 1.f};

    static GLfloat sphere_mat[] = {1.f, .5f, 0.f, 1.f};
    static GLfloat cone_mat[]   = {0.f, .5f, 1.f, 1.f};
    static GLfloat spec_mat[]   = {0.8f, .8f, .8f, 1.f};
    static GLfloat specoff_mat[]   = {0.f, 0.f, 0.f, 1.f};

    GLUquadricObj *sphere, *cone, *base;
    int maxsize;

    glutInit(&argc, argv);
    glutInitWindowSize(512, 512);
    glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH);
    (void)glutCreateWindow("a lit room");
    glutDisplayFunc(redraw);
    glutKeyboardFunc(key);

    /* draw a perspective scene */
    glMatrixMode(GL_PROJECTION);
    glFrustum(-100., 100., -100., 100., 300., 600.); 
    glMatrixMode(GL_MODELVIEW);
    /* look at scene from (0, 0, 400) */
    gluLookAt(0., 0., 400., 0., 0., 0., 0., 1., 0.);

    /* turn on features */
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    /* place light 0 in the right place */
    glLightfv(GL_LIGHT0, GL_POSITION, lightpos);

    /* remove back faces to speed things up */
    glCullFace(GL_BACK);


    /* make a display list containing a sphere */
    glNewList(SPHERE, GL_COMPILE);
    sphere = gluNewQuadric();
    glPushAttrib(GL_LIGHTING_BIT);
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat);
    glMaterialfv(GL_FRONT, GL_SPECULAR, spec_mat);
    glMateriali(GL_FRONT, GL_SHININESS, 100);
    gluSphere(sphere, 20.f, 20, 20);
    gluDeleteQuadric(sphere);
#if 0
    /* Don't need this if pushing and popping attributes */
    glMateriali(GL_FRONT, GL_SHININESS, 0);
    glMaterialfv(GL_FRONT, GL_SPECULAR, specoff_mat);
#endif
    glPopAttrib();
    glEndList();

    /* create a display list containing a cone */
    glNewList(CONE, GL_COMPILE);
    cone = gluNewQuadric();
    base = gluNewQuadric();
    glRotatef(-90.f, 1.f, 0.f, 0.f);
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat);
    gluQuadricOrientation(base, GLU_INSIDE);
    gluDisk(base, 0., 20., 20, 1);
    gluCylinder(cone, 20., 0., 60., 20, 20);
    gluDeleteQuadric(cone);
    gluDeleteQuadric(base);
    glEndList();

    CHECK_ERROR("end of main");
    glutMainLoop();
}

