package classwork;

import java.awt.event.KeyEvent;

import javax.media.opengl.*;
import jocode.*;
import jomodel.*;

/**
 * GLART_4_light_triangles.java
 *
 * Very basic lighting demo.  Shines a light on a three triangles arranged
 * to create a curved surface. Calculate and draw face normals for all triangles.
 * 
 * Use arrow keys to rotate model.
 * 
 * Use glShadeModel() to SMOOTH shade or FLAT shade the triangles.
 */
public class GLART_4_light_triangles extends JOApp {
    float rotation = 0;
    // Color value for the overall scene lighting
    float sceneAmbientLight[]  = { 0.18f, 0.1f, .15f, 1f };
    // Color values for light
    float lightDiffuse[]  = { 1f, 1f, .8f, 1f };
    float lightSpecular[] = { 1f, 1f, .8f, 1f };
    float lightAmbient[]  = { 0.5f, 0.5f, .4f, 1f };
    // Light position: if last value is 0, then this describes light direction.  If 1, then light position.
    float lightPosition[] = { -4f, 3f, 3f, 1f };
    // to toggle smooth/flat shading
    boolean smoothShading = true;

    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_4_light_triangles app = new GLART_4_light_triangles();
        app.run();
    }

    /**
     * Initialize OpenGL
     *
     */
    public void setup() {
        // Select the Projection Matrix (controls perspective)
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();    // Reset The Projection Matrix

        // Define perspective
        glu.gluPerspective(
            45.0f,        // Field Of View
            (float)getWidth() / (float)getHeight(), // aspect ratio
            0.1f,         // near Z clipping plane
            100.0f);      // far Z clipping plane

        // Select The Modelview Matrix (controls model orientation)
        gl.glMatrixMode(GL.GL_MODELVIEW);

        // make sure OpenGL correctly layers objects
        gl.glEnable(GL.GL_DEPTH_TEST);

        // OpenGL won't draw backward facing triangles ("back faces")
        gl.glEnable(GL.GL_CULL_FACE);

        // turn lighting on (does not create a light)
        gl.glEnable(GL.GL_LIGHTING);

        // Create a light
        // diffuse is the color of direct light from this light source
        // specular is the color of reflected light from this source
        // ambient is the color of scattered light from this source
        // position is where the light is, or it's direction
        setLight( GL.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition );

        // dim overall scene lighting
        setAmbientLight(sceneAmbientLight);

        // How to shade faces:
        //     GL_FLAT -- each face has same lighting (looks faceted)
        //     GL_SMOOTH -- lighting is blended across face
        gl.glShadeModel(GL.GL_SMOOTH);
    }

    /**
     * rotate scene if right or left arrow keys are pressed
     */
    public void keyDown(int keycode) {
        if(keycode == KeyEvent.VK_RIGHT) {
            rotation -= .5f;
        }
        else if(keycode == KeyEvent.VK_LEFT) {
        	rotation += .5f;
        }
    }

    /**
     * rotate scene if right or left arrow keys are pressed
     */
    public void keyUp(int keycode) {
        if (keycode == KeyEvent.VK_SPACE) {
        	smoothShading = ! smoothShading;
        	if (smoothShading) {
        		gl.glShadeModel(GL.GL_SMOOTH);
        	}
        	else {
        		gl.glShadeModel(GL.GL_FLAT);
        	}
        }
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	// three triangles (three verts each)
    	JOVector[] tri1 = new JOVector[3];
    	JOVector[] tri2 = new JOVector[3];
    	JOVector[] tri3 = new JOVector[3];

    	// triangle vertices center
        tri1[0] = new JOVector( 0f, .75f, 0f);  // top
        tri1[1] = new JOVector(-1f,  -1f, 0f);  // lower left
        tri1[2] = new JOVector( 1f,  -1f, 0f);  // lower right
    	// triangle vertices left
        tri2[0] = new JOVector(-1f,  -1f, 0f);  // bottom
        tri2[1] = new JOVector( 0f, .75f, 0f);  // top right
        tri2[2] = new JOVector(-2f, .75f, -.8f);  // top left
    	// triangle vertices right
        tri3[0] = new JOVector( 1f,  -1f, 0f);  // bottom
        tri3[1] = new JOVector( 2f, .75f, -.8f);  // top right
        tri3[2] = new JOVector( 0f, .75f, 0f);  // top left

		// calculate normal of triangle
		JOVector normal1 = JOVector.getNormal(tri1[0], tri1[1], tri1[2]);
		JOVector normal2 = JOVector.getNormal(tri2[0], tri2[1], tri2[2]);
		JOVector normal3 = JOVector.getNormal(tri3[0], tri3[1], tri3[2]);

		// calculate center of triangle
		JOVector center1 = JOVector.avg(tri1[0],tri1[1],tri1[2]);
		JOVector center2 = JOVector.avg(tri2[0],tri2[1],tri2[2]);
		JOVector center3 = JOVector.avg(tri3[0],tri3[1],tri3[2]);

		// Clear screen and depth buffer
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Select The Modelview Matrix (controls model orientation)
        // and reset the coordinate system to center of screen
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        // Where is the 'eye'
        glu.gluLookAt(
            0f, 0f, 5f,   // eye position
            0f, 0f, 0f,    // target to look at
            0f, 1f, 0f);   // which way is up

        // lines will be 2 pixels
        gl.glLineWidth(2);

        // Draw the triangle and normal, rotating
        gl.glPushMatrix();
        {
        	gl.glRotatef(rotation, 0,1,0);

        	drawTriangle(tri1,normal1);
			drawNormal(center1,normal1);

        	drawTriangle(tri2,normal2);
			drawNormal(center2,normal2);

        	drawTriangle(tri3,normal3);
			drawNormal(center3,normal3);
        }
        gl.glPopMatrix();
    }


    public void drawTriangle(JOVector[] tri, JOVector normal) {
	    // draw a triangle
		gl.glBegin(GL.GL_TRIANGLES);
		{
			// set the normal (affects all following verts)
	        gl.glNormal3f(normal.x, normal.y, normal.z);
			gl.glVertex3f(tri[0].x, tri[0].y, tri[0].z);
			gl.glVertex3f(tri[1].x, tri[1].y, tri[1].z);
			gl.glVertex3f(tri[2].x, tri[2].y, tri[2].z);
		}
		gl.glEnd();
    }

    /**
     * Draw a red line to indicate the normal.  Disable lighting
     * so we can use glColor().  ReEnable lighting before returning.
     */
    public void drawNormal(JOVector center, JOVector normal) {
	    // draw a red line to indicate where the normal is
		gl.glDisable(GL.GL_LIGHTING); // turn off so we can use glColor()
		gl.glColor4f(1, 0, 0, 1);
		gl.glBegin(GL.GL_LINES);
		{
			gl.glVertex3f(center.x, center.y, center.z);
			gl.glVertex3f(center.x+normal.x, center.y+normal.y,	center.z+normal.z);
		}
		gl.glEnd();
		gl.glEnable(GL.GL_LIGHTING);
    }
}
