/**
 * Lesson11.java
 *
 * Author: Darren Hodges
 * Date: 23/12/1999
 *
 * Port of the NeHe OpenGL Tutorial (Lesson 11: "OpenGL Flag Effect")
 * to Java using the GL4Java interface to OpenGL.
 *
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

//GL4Java classes
import gl4java.GLContext;
import gl4java.awt.GLAnimCanvas;
import gl4java.utils.textures.*;


public class Lesson11 extends Applet
{   
    //Our rendering canvas
   	//We are using GLAnimCanvas because we want the canvas
   	//to be constantly redrawn
    renderCanvas canvas = null;


	/**
     * void init()
     *
     * Initialise the applet.
     */
	public void init()
	{   
        //We will use BorderLayout to layout the applet components
        setLayout(new BorderLayout());
        
        //Create our canvas and add it to the center of the applet
        canvas = new renderCanvas(getSize().width, getSize().height);
	canvas.requestFocus();
        add("Center", canvas);
	}
	
	
	/**
     * void start()
     *
     * Start the applet.
     */
	public void start()
    {
        //Start animating the canvas
        canvas.start();
    }

	
	/**
     * void stop()
     *
     * Stop the applet.
     */
    public void stop()
    {
        //Stop animating the canvas
        canvas.stop();
    }
    
    
    /**
     * void destroy()
     *
     * Destroy the applet.
     */
    public void destroy()
    {
        //Stop animating the canvas
        canvas.stop();
        //Destroy the canvas
        canvas.destroy();
    }



    private class renderCanvas extends GLAnimCanvas
    	implements KeyListener, MouseListener
    {
    	float xrot = 0.0f;	//X Rotation
		float yrot = 0.0f;	//Y Rotation
		float zrot = 0.0f;	//Z Rotation

    	//The array for the points on the grid of our "wave"
    	float[][][] points = new float[45][45][3];
    
    	int wiggle_count = 0;
	
		int[] texture = new int[1]; //Storage for one texture
		
		
        /**
         * renderCanvas(int w, int h)
         *
         * Constructor.
         */
        public renderCanvas(int w, int h)
        {
            super(w, h);
            
            //Registers this canvas to process keyboard events
        	addKeyListener(this);
                addMouseListener(this);    
        }
    
        
        /**
         * void preInit()
         *
         * Called just BEFORE the GL-Context is created.
         */
        public void preInit()
        {
            //We want double buffering
            doubleBuffer = true;
            //But we dont want stereo view
            stereoView = false;
        }
    
    
        /**
         * void LoadGLTextures()
         *
         * Load textures.
         */
        public void LoadGLTextures()
    	{
    		PngTextureLoader texLoader = new PngTextureLoader(gl, glu);
    		texLoader.readTexture(getCodeBase(), "data/tim.png");
    		
    		if(texLoader.isOk())
    		{
				//Create Texture
				gl.glGenTextures(1, texture);
				gl.glBindTexture(GL_TEXTURE_2D, texture[0]);
			
				gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			
				gl.glTexImage2D(GL_TEXTURE_2D,
						 		0,
						 		3,
						 		texLoader.getImageWidth(),
						 		texLoader.getImageHeight(),
						 		0,
						 		GL_RGB,
						 		GL_UNSIGNED_BYTE,
						 		texLoader.getTexture());
    		}
    	}
    	
        
        /**
         * void init()
         *
         * Called just AFTER the GL-Context is created.
         */
        public void init()
        {
            //Load The Texture(s)
            LoadGLTextures();
			//Enable Texture Mapping
			gl.glEnable(GL_TEXTURE_2D);
            
            //This Will Clear The Background Color To Black
			gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

			//Enables Clearing Of The Depth Buffer
			gl.glClearDepth(1.0);
			//The Type Of Depth Test To Do
			gl.glDepthFunc(GL_LESS);
			//Enables Depth Testing
			gl.glEnable(GL_DEPTH_TEST);

			//Enables Smooth Color Shading
			gl.glShadeModel(GL_SMOOTH);
			//Select The Projection Matrix
			gl.glMatrixMode(GL_PROJECTION);
			//Reset The Projection Matrix
			gl.glLoadIdentity();
			//Calculate The Aspect Ratio Of The Window
			glu.gluPerspective(45.0f, (float)getSize().width / (float)getSize().height, 0.1f, 100.0f);
			//Select The Modelview Matrix
			gl.glMatrixMode(GL_MODELVIEW);
			
			
			for(float float_x = 0.0f; float_x < 9.0f; float_x += 0.2f)
			{
				for(float float_y = 0.0f; float_y < 9.0f; float_y += 0.2f)
				{
					points[(int)(float_x * 5)][(int)(float_y * 5)][0] = float_x - 4.4f;
					points[(int)(float_x * 5)][(int)(float_y * 5)][1] = float_y - 4.4f;
					points[(int)(float_x * 5)][(int)(float_y * 5)][2] = (float)(Math.sin(((float_x * 5 * 8) / 360) * 3.14159 * 2));
				}
			}
        }
    
    
        /**
         * void destroy()
         *
         * Destroy the canvas.
         */
        public void destroy()
        {
            //Destroy the GLContext
            cvsDispose();
        }
    
		
		/**
		 * void reshape(int width, int height)
		 *
		 * Called after the first paint command.
		 */
        public void reshape(int width, int height)
        {
            //Reset The Current Viewport And Perspective Transformation
			gl.glViewport(0, 0, width, height);

			//Select The Projection Matrix
			gl.glMatrixMode(GL_PROJECTION);
			//Reset The Projection Matrix
			gl.glLoadIdentity();
			//Calculate The Aspect Ratio Of The Window
			glu.gluPerspective(45.0f, (float)getSize().width / (float)getSize().height, 0.1f, 100.0f);
			//Select The Modelview Matrix
			gl.glMatrixMode(GL_MODELVIEW);
        }

        
        /**
         * void display()
         *
         * Draw to the canvas.
         */
        public void display()
        {
            //Ensure GL is initialised correctly
            if (glj.gljMakeCurrent(true) == false)
            	return;
            
            int x, y;
			float float_x, float_y, float_xb, float_yb;
			
			gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
			gl.glLoadIdentity();							
			gl.glTranslatef(0.0f, 0.0f, -15.0f);

			gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);
			gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);  
			gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);

			gl.glBindTexture(GL_TEXTURE_2D, texture[0]);
			
			gl.glPolygonMode(GL_BACK, GL_FILL);
			gl.glPolygonMode(GL_FRONT, GL_LINE);

			gl.glBegin(GL_QUADS);

			for(x = 0; x < 44; x++)
			{
				for(y = 0; y < 44; y++)
				{
					float_x = (float)x / 44;
					float_y = (float)y / 44;
					float_xb = (float)(x + 1) / 44;
					float_yb = (float)(y + 1) / 44;
					
					gl.glTexCoord2f(float_x, float_y);
					gl.glVertex3f(points[x][y][0], points[x][y][1], points[x][y][2]);
			
					gl.glTexCoord2f(float_x, float_yb);
					gl.glVertex3f(points[x][y + 1][0], points[x][y + 1][1], points[x][y + 1][2]);
			
					gl.glTexCoord2f(float_xb, float_yb);
					gl.glVertex3f(points[x + 1][y + 1][0], points[x + 1][y + 1][1], points[x + 1][y + 1][2]);
			
					gl.glTexCoord2f(float_xb, float_y);
					gl.glVertex3f(points[x + 1][y][0], points[x + 1][y][1], points[x + 1][y][2]);
				}
			}
			
			gl.glEnd();
			
			if(wiggle_count == 2)
			{
				for(y = 0; y < 45; y++)
				{
					points[44][y][2] = points[0][y][2];
				}
				
				for(x = 0; x < 44; x++)
				{
					for(y = 0; y < 45; y++)
					{
						points[x][y][2] = points[x + 1][y][2];
					}
				}
				
				wiggle_count = 0;
			}
			
			wiggle_count++;
			
			xrot += 0.3f;
			yrot += 0.2f;
			zrot += 0.4f;
            
            //Swap buffers
            glj.gljSwap();
            glj.gljFree();
        }
        
        
        /**
     	 * void keyTyped(KeyEvent e)
     	 *
     	 * Invoked when a key has been typed. This event occurs when a key press is followed by a key release. 
     	 */
    	public void keyTyped(KeyEvent e)
		{
			//We are not handling any keyboard events
		}


 		/**
 	 	 * void keyPressed(KeyEvent e)
 	 	 *
 	 	 * Invoked when a key has been pressed.
 	 	 */
 		public void keyPressed(KeyEvent e)
		{
			//We are not handling any keyboard events
		}


		/**
	 	 * void keyReleased(KeyEvent e)
	 	 *
	 	 * Invoked when a key has been released. 
	 	 */
		public void keyReleased(KeyEvent e)
		{
			//We are not handling any keyboard events
		}

        // Methods required for the implementation of MouseListener
        public void mouseEntered( MouseEvent evt )
        {
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
        }

        public void mouseExited( MouseEvent evt )
        { }
        public void mousePressed( MouseEvent evt )
        { }
        public void mouseReleased( MouseEvent evt )                                     
	{ }
        public void mouseClicked( MouseEvent evt )
        { 
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
	}

    }
}
