package classwork;

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

/**
 * GLART_11_timing.java
 * <P>
 * Simulates a clock with second hand moving in real-time.  Demonstrates
 * use of a timing loop to control program execution speed.
 * <P>
 * Hit SPACE to reset the timer.
 * <P>
 * The frequency at which screen images are drawn is called the
 * framerate and is usually measured in frames per second.  The goal
 * in this demo is to animate a clock at the same speed (real time)
 * no matter how fast or slow the system is rendering.
 * <P>
 * Since cpu performance varies, program execution speed can vary from
 * one computer to another.  If you move an animation a fixed amount
 * in each render(), then the animation will move at different rates on
 * different computers, as render() is called more often on the faster
 * cpu.
 * <P>
 * VSync also affects the framerate.  If VSync is enabled then
 * OpenGL frame updates are synchronized with the monitor refresh rate.
 * Most monitors refresh the screen 60 or 75 per second, and VSync will
 * force updates to wait till the monitor is ready to draw.
 * This limits the refresh rate to something constant, but monitors can
 * have different refresh rates, and slower graphics cards will slow
 * down the refresh rate.
 * <P>
 * To insure that animations run at the same rate across various
 * computers, you need to move animations according to actual time
 * elapsed, not frame render rate.
 * <P>
 * Check how much time has elapsed since the last frame rendered, then
 * "step" the simulation forward in proportion to that amount of time.
 * The simulation keeps track of how much time has elapsed.
 * <P>
 * Java's time function, System.currentTimeMillis(), does not measure
 * time accurately below 10 milliseconds, and will not give accurate
 * results when measuring time elapsed between frames.  Use
 * System.nanoTime() instead.
 * <P>
 * Java includes a hardware timer that measures nanoseconds (System.nanoTime()).
 * Use double datatype to hold nanosecond time values type to prevent loss of
 * precision.
 * <P>
 * The JOApp.getSecondsPerFrame() function returns the average seconds per frame
 * for the past 100 frames. This creates smoother timing than reacting to the
 * exact time elapsed between each frame (which can vary unpredictably
 * depending on cpu load).
 */
public class GLART_11_timing extends JOApp {
    float rotation = 0f;

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

    // For time tracking
    double startTime;       // starting time of simulation (in seconds)
    double simulationTime;  // current time of simulation (in seconds)


    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_11_timing app = new GLART_11_timing();
        windowTitle = "Timing Demo";
        displayWidth = 800;
        displayHeight = 600;
        app.useCurrentDisplay = false;
        app.fullScreen = false;
        app.run();
    }

    /**
     * Initialize the environment
     */
    public void setup() {
        // Allocate and configure a texture based on the image
        myTextureHandle = makeTexture("images/grid_texture_marked.png");

        //----------------------------------------------------
        // Initialize values for tracking time:
        //----------------------------------------------------
        resetTimeCounters();

        // 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

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

    /**
     * Reset the timer values.
     */
    public void resetTimeCounters() {
        // reset simulation time
    	startTime = simulationTime = getTimeInSeconds();

        // rotation of clock hand in degrees
        rotation = 0;
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	// how far should second hand rotate per second
    	float degreesPerSecond = 6f;

    	// seconds elapsed since we last rendered
    	double secsSinceLastFrame = (double)getTimeInSeconds() - simulationTime;

    	// update the simulation current time
    	simulationTime += secsSinceLastFrame;

    	///////////////////////////////////////////////////////
    	// Animate second hand of clock PICK ONE:

    	// OLD WAY: rotate an arbitrary amount each frame
    	//rotation += .2f;

    	// BETTER: rotate in proportion to time elapsed
    	rotation += degreesPerSecond * secsSinceLastFrame;

    	// done
    	///////////////////////////////////////////////////////

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

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

        // Reset the Modelview matrix
        // this resets the coordinate system to center of screen
        gl.glLoadIdentity();

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

        // draw the clock face
        gl.glDisable(GL.GL_TEXTURE_2D);
        gl.glColor4f(.5f, .5f, .5f, 1f);
        gl.glPushMatrix();
        {
        	// draw markers every 5 minutes (30 degrees)
 	        for (int i=0; i < 12; i++) {
	        	gl.glRotatef(30f, 0,0,1);
		        gl.glBegin(GL.GL_LINES);
		        {
		            gl.glVertex3f(0f, 1.5f, -.1f);
		            gl.glVertex3f(0f, 2f, -.1f);
		        }
		        gl.glEnd();
	        }
        }
        gl.glPopMatrix();

        // draw the second hand
        gl.glColor4f(.9f, .8f, .8f, 1f);
        gl.glPushMatrix();
        {
	        // rotate coordinate system
	        gl.glRotatef(-rotation, 0,0,1);
	    	// draw a triangular second hand
	        gl.glBegin(GL.GL_TRIANGLES);
	        {
	            gl.glTexCoord2f(0, 0);
	            gl.glVertex3f(-.1f, 0f, 0f);         // Bottom Left

	            gl.glTexCoord2f(1, 0);
	            gl.glVertex3f( .1f, 0f, 0f);         // Bottom Right

	            gl.glTexCoord2f(.5f, .5f);
	            gl.glVertex3f( 0, 2f, 0f);           // Top
	        }
	        gl.glEnd();
        }
        gl.glPopMatrix();

        // text needs textures ON
        gl.glEnable(GL.GL_TEXTURE_2D);

        // Show the simulated time elapsed.
        // This is the time that we have processed within our simulation.
        printZ(-4.5f, -.25f, 0f, 0, .03f, formatTime(simulationTime-startTime) );

        // Show the actual system time elapsed.
        // This is the computer clock time that has elapsed since we started the simulation.
        print(50, 40, "Actual time elapsed: " + formatTime(getTimeInSeconds()-startTime) );
    }

    /**
     * Given time in seconds, returns minutes:seconds formatted string.
     */
    public String formatTime(double seconds) {
    	int secs = (int)(seconds % 60);
    	String strSecs = (secs < 10)? "0" + secs : "" + secs;
    	return "" + ((int)(seconds/60f)) + ":" + strSecs;
    }

    /**
     * SPACEBAR resets clock
     */
	public void keyUp(int keycode) {
    	if (keycode == KeyEvent.VK_SPACE) {
	            resetTimeCounters();
    	}
    }

}
