/**
 * Anima: A singular journey
 * CS470 Capstone Project
 * Shawn Aldridge
 */

package anima;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;
import com.sun.opengl.util.texture.TextureCoords;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;


/**
 * Some code autogenerated by NetBeans
 */
public class Level4 extends JFrame implements GLEventListener {

    GLCanvas canvas;
    JMenuBar menubar;
    JMenu file, texture;
    JMenuItem closer, brush, kanji, glass;
    JOptionPane option, error;
    int numLines;
    int numXLines;
    int numYLines;
    int numSqr;
    float ygap, xgap;
    float ratX, ratY, north, south, east, west, fogDensity;
    float[] wallSpec, ratSpec, ratDiff, lightPosition, whiteLight, ambientLight, fogColor;
    float shininess;
    int ratFace, ratCell;
    int bottomHole, topHole;
    ArrayList<mazeNode> nodeList;
    int[] maze;
    int cellsInList, fogSetting;
    boolean ratsEye;
    Texture currentTexture, circuitTexture, goldTexture, borgTexture;
    String brushFile, kanjiFile, glassFile, currentFile;
    TextureIO texRead;


    public Level4()
    {
        super();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        canvas = new GLCanvas();

        setUndecorated(true);
        setExtendedState(this.MAXIMIZED_BOTH);
        numYLines = 16;
        numXLines = 16;

        shininess = 10.0f;
        wallSpec = new float[4];
        wallSpec[0] = 0.4f;
        wallSpec[1] = 0.0f;
        wallSpec[2] = 0.4f;
        wallSpec[3] = 1.0f;

        lightPosition = new float[4];
        lightPosition[0] = 10.0f;
        lightPosition[1] = 0.0f;
        lightPosition[2] = 30.0f;
        lightPosition[3] = 1.0f;

        whiteLight = new float[4];
        whiteLight[0] = 1.0f;
        whiteLight[1] = 1.0f;
        whiteLight[2] = 1.0f;
        whiteLight[3] = 1.0f;

        ambientLight = new float[4];
        ambientLight[0] = 0.4f;
        ambientLight[1] = 0.4f;
        ambientLight[2] = 0.4f;
        ambientLight[3] = 1.0f;

        ratSpec = new float[4];
        ratSpec[0] = 0.0f;
        ratSpec[1] = 0.8f;
        ratSpec[2] = 0.0f;
        ratSpec[3] = 1.0f;

        ratDiff = new float[4];
        ratDiff[0] = 1.0f;
        ratDiff[1] = 1.0f;
        ratDiff[2] = 1.0f;
        ratDiff[3] = 1.0f;

        fogColor = new float[4];
        fogColor[0] = 0.5f;
        fogColor[1] = 0.5f;
        fogColor[2] = 0.5f;
        fogColor[3] = 1.0f;

        fogDensity = 0.00f;
        add(canvas);
        this.addKeyListener(new keyHandler(this));
        final Animator animator = new Animator(canvas);
        setVisible(true);
        animator.start();
    }


    /**
     * Initializes values necessary to draw maze
     */
    public void initMaze()
    {
        numSqr = numXLines * numYLines;
        cellsInList = numSqr;
        xgap = 1600 / (numXLines + 2);
        ygap = 1200 / (numYLines + 2);

        bottomHole = (int)(Math.random() * numXLines);
        topHole = (int)((Math.random() * numXLines) + (numYLines - 1) * (numXLines));
        maze = new int[numXLines * numYLines];
        ratFace = 1;
        ratCell = bottomHole;
        setLocationRelativeTo(null);
    }

    /**
     * Create list of cells and union them all together
     * order to draw them.
     */
    public void createMaze()
    {
        initMaze();
        nodeList = new ArrayList<mazeNode>();

        //generate disjoint set to be unioned
        for(int i = 0; i < maze.length; i++) maze[i] = -1;

        //generate list of all nodes
        for(int i = 0; i < (numSqr); i++)
        {
            nodeList.add(new mazeNode(i, (((i % numXLines) + 1) * xgap) - 800, ((((i / numXLines) + 1) * ygap) - 600)));
        }

        //while there are cells yet to be unioned, continue unions
        while(cellsInList > 1)
        {
            int other = 0;
            int rand = (int)(Math.random() * (numSqr));
            int dir = (int)(Math.random() * 4);
            if(dir == 0)
            {
                if((rand % numXLines) == (numXLines - 1)) other = rand - 1;
                else other = rand + 1;
            }
            if(dir == 1)
            {
                if(rand >= (numXLines * (numYLines - 1))) other = rand - numXLines;
                else other = rand + numXLines;
            }
            if(dir == 2)
            {
                if(((rand % numXLines) == 0)) other = rand + 1;
                else other = rand - 1;
            }
            if(dir == 3)
            {
                if(rand < numXLines) other = rand + numXLines;
                else other = rand - numXLines;
            }
            if((root(rand)) != (root(other))) union(rand, other);
        }

    }//end method createMaze

    /**
     * finds the root of a node in the disjoint set
     * @param num
     * @return
     */
    public int root(int num)
    {
        if(maze[num] < 0) return num;
        else return root(maze[num]);
    }

    /**
     * places two cells into the same union, connecting them as you go
     * @param first
     * @param second
     */
    public void union(int first, int second)
    {
        mazeNode base = nodeList.get(first);
        mazeNode mate = nodeList.get(second);

        base.connections.add(mate);
        mate.connections.add(base);
        nodeList.set(first, base);
        nodeList.set(second, mate);

        int root1 = root(first);
        int root2 = root(second);
        int neg = maze[root2];
        maze[root2] = root1;
        maze[root1] += neg;
        cellsInList--;
    }

    /**
     * draws the maze given the list of nodes
     * @param gl
     * @param nodeList
     */
    public void drawMaze(GL gl, ArrayList<mazeNode> nodeList)
    {
        mazeNode temp = null;
        mazeNode connector = null;
        boolean left = true;
        boolean right = true;
        boolean up = true;
        boolean down = true;


        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, wallSpec, 0);
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, whiteLight, 0);
        gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, shininess);

        
        
        

        gl.glNormal3f(0.0f, 0.0f, 1.0f);
        //gl.glColor3f(0.5f, 0.2f, 0.5f);
        temp = nodeList.get(0);  //lower left
        gl.glVertex3f(temp.x, temp.y, 0);

        temp = nodeList.get(numXLines - 1); //lower right
        gl.glVertex3f(temp.x + xgap, temp.y, 0);

        temp = nodeList.get(nodeList.size() - 1);  //upper right
        gl.glVertex3f(temp.x + xgap, temp.y + ygap, 0);

        temp = nodeList.get((numYLines - 1) * numXLines);  //upper left
        gl.glVertex3f(temp.x, temp.y + ygap, 0);


        for(int i = 0; i < nodeList.size(); i++)
        {
            temp = nodeList.get(i);

            if(i == bottomHole) down = false;
            if(i == topHole) up = false;

            //set booleans for missing walls
            for(int j = 0; j < temp.connections.size(); j++)
            {
                connector = temp.connections.get(j);
                if(connector.name == temp.name + 1) right = false;
                if(connector.name == temp.name - 1) left = false;
                if(connector.name == temp.name + numXLines) up = false;
                if(connector.name == temp.name - numXLines) down = false;
            }



            if(left)
            {
                //bottom face
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 0);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 0);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 0);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 0);

                //west face
                gl.glNormal3f(-1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 0);

                //east face
                gl.glNormal3f(1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 0);

                //north face
                gl.glNormal3f(0.0f, 1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 0);

                //south face
                gl.glNormal3f(0.0f, -1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 0);

                //top face
                gl.glNormal3f(0.0f, 0.0f, 1.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + (xgap / 8), temp.y, 10);
            }

            if(up)
            {
                //bottom face
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 0);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 0);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 0);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 0);

                //west face
                gl.glNormal3f(-1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 0);


                //east face
                gl.glNormal3f(1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 0);


                //north face
                gl.glNormal3f(0.0f, 1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 0);

                //south face
                gl.glNormal3f(0.0f, -1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 0);

                //top face
                gl.glNormal3f(0.0f, 0.0f, 1.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y + ygap + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + ygap + (ygap / 8), 10);


            }

            if(right)
            {
                //bottom face
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 0);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 0);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 0);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 0);

                //west face
                gl.glNormal3f(-1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 0);

                //east face
                gl.glNormal3f(1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 0);

                //north face
                gl.glNormal3f(0.0f, 1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 0);

                //south face
                gl.glNormal3f(0.0f, -1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 0);

                //top face
                gl.glNormal3f(0.0f, 0.0f, 1.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y, 10);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap - (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y + ygap, 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap + (xgap / 8), temp.y, 10);

            }

            if(down)
            {
                //bottom face
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 0);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 0);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 0);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 0);

                //west face
                gl.glNormal3f(-1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 0);

                //east face
                gl.glNormal3f(1.0f, 0.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 0);

                //north face
                gl.glNormal3f(0.0f, 1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 0);

                //south face
                gl.glNormal3f(0.0f, -1.0f, 0.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 0);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 0);

                //top face
                gl.glNormal3f(0.0f, 0.0f, 1.0f);

                gl.glTexCoord2f(west, north);
                gl.glVertex3f(temp.x, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(east, north);
                gl.glVertex3f(temp.x + xgap, temp.y - (ygap / 8), 10);

                gl.glTexCoord2f(east, south);
                gl.glVertex3f(temp.x + xgap, temp.y + (ygap / 8), 10);

                gl.glTexCoord2f(west, south);
                gl.glVertex3f(temp.x, temp.y + (ygap / 8), 10);
            }

            left = true;
            right = true;
            up = true;
            down = true;

        }//endfor

    }//end method drawmaze

    public void init(GLAutoDrawable drawable) {

        GL gl = drawable.getGL();
        //GLU glu = new GLU();

        // Setup the drawing area and shading mode
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        gl.glShadeModel(GL.GL_SMOOTH);
        fogSetting = GL.GL_EXP;

        texRead = new TextureIO();

        brushFile = "trial.jpg";
        glassFile = "trial2.gif";
        kanjiFile = "trial3.jpg";

        try{
            borgTexture = texRead.newTexture(getClass().getResource(kanjiFile), false, ".jpg");
            borgTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
            borgTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
            borgTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            borgTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);

            goldTexture = texRead.newTexture(getClass().getResource(glassFile), false, ".gif");
            goldTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
            goldTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
            goldTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            goldTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);

            circuitTexture = texRead.newTexture(getClass().getResource(brushFile), false, ".jpg");
            circuitTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
            circuitTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
            circuitTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            circuitTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
        }catch(Exception e){
            System.err.println("Error generating texture");
            System.err.println("Exception is: " + e);
        }

        currentTexture = borgTexture;

        gl.glEnable(GL.GL_LIGHTING);
        gl.glEnable(GL.GL_LIGHT0);
        gl.glEnable(GL.GL_DEPTH_TEST);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glOrtho(0.0, canvas.getWidth(), 0.0, canvas.getHeight(), -10, 10);
        gl.glMatrixMode(GL.GL_MODELVIEW);
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {

        GL gl = drawable.getGL();
        GLU glu = new GLU();

        gl.glViewport(0, 0, width, height);

        gl.glMatrixMode(GL.GL_PROJECTION); /* switch matrix mode */
        gl.glLoadIdentity();
        if (width <= height)
        {
        gl.glOrtho(-600.0, 600.0, -600.0 * ((float) height / (float) width),
        600.0 * (float) height / (float) width, -1000, 1000);
        } else {
            gl.glOrtho(-600.0 * ((float) width / (float) height), 600.0 *
        (float) width / (float) height, -600.0, 600.0, -1000, 1000);
        }

        gl.glMatrixMode(GL.GL_MODELVIEW); /* return to modelview mode */

    }

    public void look(GLU glu, GL gl)
    {
        if(ratFace == 0)
        {
            gl.glRotatef(90, 0, 0, 1);
            glu.gluLookAt(ratX, ratY + (ygap / 6), 5, ratX + 5, ratY + (ygap / 6), -1.0, 0, 1.0, 0);
        }

        if(ratFace == 1)
        {
            glu.gluLookAt(ratX + (xgap / 6), ratY, 5, ratX + (xgap / 6), ratY + 5, -1.0, 0, 1.0, 0);
        }

        if(ratFace == 2)
        {
            gl.glRotatef(270, 0, 0, 1);
            glu.gluLookAt(ratX, ratY + (ygap / 6), 5, ratX - 5, ratY + (ygap / 6), -1.0, 0, 1.0, 0);
        }

        if(ratFace == 3)
        {
            gl.glRotatef(180, 0, 0, 1);
            glu.gluLookAt( ratX + (xgap / 6), ratY, 5, ratX + (xgap / 6), ratY - 5, -1.0, 0, 1.0, 0);
        }

    }

    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        GLU glu = new GLU();

        // Clear the drawing area
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        // Reset the current matrix to the "identity"
        gl.glLoadIdentity();

        gl.glEnable(GL.GL_LINE_SMOOTH);

        gl.glMatrixMode(GL.GL_MODELVIEW); /* return to modelview mode */
        gl.glLoadIdentity();

        gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPosition, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, whiteLight, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, whiteLight, 0);
        gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, ambientLight, 0);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
    
        glu.gluPerspective(120, 4, 1, 1000);
        look(glu, gl);

        gl.glMatrixMode(GL.GL_MODELVIEW);
        
        ratX = nodeList.get(ratCell).x + (xgap / 3);
        ratY = nodeList.get(ratCell).y + (ygap / 3);

        currentTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
        currentTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
        currentTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
        currentTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
        TextureCoords coords = currentTexture.getImageTexCoords();
        west = coords.left();
        north = coords.top();
        east = coords.right();
        south = coords.bottom();
        currentTexture.bind();
        currentTexture.enable();
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);


        gl.glBegin(GL.GL_QUADS);
        gl.glColor3f(0.3f, 0.3f, 1.0f);
        drawMaze(gl, nodeList);
        gl.glEnd();

        gl.glClearColor(0.5f,0.5f,0.5f,1.0f);

        gl.glFogi(GL.GL_FOG_MODE, fogSetting);
        gl.glFogfv(GL.GL_FOG_COLOR, fogColor, 0);
        gl.glFogf(GL.GL_FOG_DENSITY, fogDensity);
        gl.glHint(GL.GL_FOG_HINT, GL.GL_DONT_CARE);
        gl.glFogf(GL.GL_FOG_START, 1.0f);
        gl.glFogf(GL.GL_FOG_END, 50.0f);

        gl.glEnable(GL.GL_FOG);


        // Flush all drawing operations to the graphics card
        gl.glFlush();
        gl.glDisable(GL.GL_TEXTURE_2D);
    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}

    class innerHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            if(e.getSource() == closer)
            {
                System.exit(0);
            }

            if(e.getSource() == brush)
            {
                currentTexture = circuitTexture;
            }

            if(e.getSource() == kanji)
            {  
                currentTexture = borgTexture;
            }

            if(e.getSource() == glass)
            {
                currentTexture = goldTexture;
            }

        }
    }//end class innerHandler

    class keyHandler implements KeyListener
    {
        Level4 maze;


        public keyHandler(Level4 m)
        {
            maze = m;
        }

        public void keyReleased(KeyEvent e){}

        public void move(KeyEvent e, mazeNode connectNode, mazeNode node)
        {
            for(int i = 0; i < node.connections.size(); i++)
            {
                connectNode = node.connections.get(i);
                if((ratFace == 2) && (connectNode.name == ratCell - 1))
                {
                    ratCell -= 1;
                }else if((ratFace == 0) && (connectNode.name == ratCell + 1))
                {
                    ratCell += 1;
                }else if((ratFace == 1) && (ratCell == topHole))
                {
                    currentTexture = goldTexture;
                }else if((ratFace == 1) && (connectNode.name == ratCell + numXLines))
                {
                    ratCell += numXLines;
                }else if((ratFace == 3) && (connectNode.name == ratCell - numXLines)) ratCell -= numXLines;

            }
        }//end method move

        public void keyPressed(KeyEvent e)
        {
            mazeNode node = nodeList.get(ratCell);
            mazeNode connectNode = null;

            if(e.getKeyCode() == e.VK_LEFT)
            {
                if(ratFace < 3)
                {
                    ratFace++;
                }else ratFace = 0;
            }

            if(e.getKeyCode() == e.VK_RIGHT)
            {
                if(ratFace > 0)
                {
                    ratFace--;
                }else ratFace = 3;
            }

            if(e.getKeyCode() == e.VK_UP)
            {
                move(e, connectNode, node);
            }

            if(e.getKeyCode() == e.VK_DOWN)
            {
                ratFace = 3;
            }

            if(e.getKeyCode() == e.VK_R)
            {
                ratsEye = true;
                fogDensity = 0.2f;
            }

        }

        public void keyTyped(KeyEvent e)
        {

        }
    }//end class keyHandler

}//end class mouseMaze

