package classwork;

import java.awt.event.KeyEvent;
import java.io.*;
import javax.media.opengl.*;
import jocode.*;
import jomodel.*;

/**
 * GLART_6_displaylist.java
 *
 * Creates a display list of the teapot model.  Display lists are
 * opengl rendering commands (glVertex, glNormal, glColor etc.) wrapped
 * up in a very efficient data structure.  In many cases display
 * lists can greatly improve rendering performance.  They freeze
 * the model though, so only can be used in cases where the geometry
 * won't change (won't work for alpha rendering, where we have to
 * sort triangles from back to front so they will draw correctly).
 *
 * See the init() function for the display list code.
 */
public class GLART_6_displayList  extends JOApp {
    private float rotation = 0;
    private JOMesh obj;

    // Material object will hold color values
    JOMaterial material = new JOMaterial();

    // texture handle (a number that refers to an allocated texture)
    int marbleTextureHandle = 0;

    // Handle for a display list
    int teapotDisplayList = -1;

    boolean useDisplayList = false;
    
    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_6_displayList app = new GLART_6_displayList();
    	displayWidth = 800;
    	displayHeight = 600;
        app.run();
    }

    /**
     * Initialize OpenGL
     *
     */
    public void setup() {
        // color of overall scene lighting
        float ambient[]       = { .2f, .2f, .2f, 1f };

        // color of light source
        float lightDiffuse[]  = { .7f, .7f, .65f, 1f }; // direct light
        float lightSpecular[] = { .7f, .7f, .65f, 1f }; // highlight
        float lightAmbient[]  = { .7f, .7f, .7f, 1f }; // scattered light

        // light position: if last value is 0, then this describes light direction.
        // If 1, then light position.
        float lightPosition[] = { -4f, 4f, 6, 0f };

        // color of material
        float mtlDiffuse[]    = { .9f, .9f, .8f, 1f };   // warm gray
        float mtlAmbient[]    = { .5f, .5f, .4f, 1f };   // darker warm gray
        float mtlSpecular[]   = { .5f, .5f, .5f, 1f };   // medium reflective
        float mtlEmissive[]   = { 0f, 0f, 0f, 1f};       // no glow
        float mtlShininess    = 10f;   // 0=no shine,  127=max shine

        // Select the Projection Matrix (controls perspective)
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();    // Reset The Projection Matrix

        // Define perspective
        glu.gluPerspective(
            55.0f,        // Field Of View
            (float)getWidth() / (float)getHeight(), // aspect ratio
            0.1f,         // near Z clipping plane
            1000.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 texturing on
        gl.glEnable(GL.GL_TEXTURE_2D);

        // 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 hightlight color
        // 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 );

        // no overall scene lighting
        setAmbientLight(ambient);

        // change the current material settings
        material.setDiffuse(mtlDiffuse);
        material.setAmbient(mtlAmbient);
        material.setSpecular(mtlSpecular);
        material.setShininess(mtlShininess);
        material.setGlowColor(mtlEmissive);
        // activate this material
        material.apply();

        // set the background color
        gl.glClearColor(.2f, .2f, .23f, 1);

        // blending
        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

        // Force normals to length 1
        gl.glEnable(GL.GL_NORMALIZE);

        // Draw specular highlghts on top of textures (GL12.GL_SINGLE_COLOR to reset)
        gl.glLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL.GL_SEPARATE_SPECULAR_COLOR );
        
        // Create texture from image
        marbleTextureHandle = makeTexture("images/cement_stony.png");

        // load the teapot mesh
        JOOBJImporter import3DS = new JOOBJImporter();
        InputStream in3ds = getInputStream("models/teapotT.obj");
        obj = import3DS.importFromStream(in3ds);

        // Create a "display list" of the teapot
        teapotDisplayList = gl.glGenLists(1);           // allocate 1 display list
        gl.glNewList(teapotDisplayList, GL.GL_COMPILE); // start building the list
        renderMesh(obj,marbleTextureHandle);            // glVertex() commands will go into list, not to screen
        gl.glEndList();                                 // done building the display list
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	rotation += 2f;

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

        // reset the coordinate system to center of screen
        gl.glLoadIdentity();

        // set viewpoint
        glu.gluLookAt( 0,6f,-8,   0,0,10,   0,1,0);
        
    	// draw a grid of teapots
        for (int r=0; r < 20; r++) {
        	for (int c=0; c < 20; c++) {
        		gl.glPushMatrix();
        		{
        			gl.glTranslatef(-50+(c*5), 0, r*5);
        			gl.glRotatef(rotation, 0, .5f, 0);
        			if (useDisplayList) {
        				// draw the geometry that has been loaded into the GPU and pre-compiled
        				gl.glCallList(teapotDisplayList);
        			}
        			else {
        				// draw the geometry in "direct mode" 
        				renderMesh(obj,marbleTextureHandle);
        			}
        		}
        		gl.glPopMatrix();
        	}
        }
    }


    float tileFactorHoriz = 1f;
    float tileFactorVert = 1f;
    /**
     * Render mesh with normals and texture coordinates.  Loops through
     * all triangles in the mesh object.
     *
     * Several triangles may refer to the same vertex, but each face
     * can have different normals for that vertex.  This allows for
     * sharp edges between faces.
     *
     * @param o  mesh object to render
     */
    public void renderMesh(JOMesh o, int textureHandle)
    {
        JOTriangle t;
        gl.glBindTexture(GL.GL_TEXTURE_2D,textureHandle);
        gl.glBegin(GL.GL_TRIANGLES);
        for (int j = 0; j < o.triangles.length; j++) { // draw all triangles in object
            t = o.triangles[j];

            gl.glTexCoord2f(tileFactorHoriz*t.uvw1.x, tileFactorVert*t.uvw1.y);
            gl.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z);
            gl.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z);

            gl.glTexCoord2f(tileFactorHoriz*t.uvw2.x, tileFactorVert*t.uvw2.y);
            gl.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z);
            gl.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z);

            gl.glTexCoord2f(tileFactorHoriz*t.uvw3.x, tileFactorVert*t.uvw3.y);
            gl.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z);
            gl.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z);
        }
        gl.glEnd();
    }

    /**
     * Cleanup all the resources.
     */
    public void exit() {
        // de-allocate display list
        if (teapotDisplayList != -1) {
            gl.glDeleteLists(teapotDisplayList,1);
            teapotDisplayList = -1;
        }
        super.exit();
    }

    
    public void keyUp(int keycode) {
    	if (keycode == KeyEvent.VK_SPACE) {
    		useDisplayList = !useDisplayList;
    	}
    }


}
