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


package anima;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.FPSAnimator;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
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.JFrame;
import java.awt.event.*;
import com.sun.opengl.util.GLUT;
import javax.media.opengl.GLCapabilities;
import java.util.LinkedList;
import java.util.ArrayList;
import javax.swing.Timer;




/**
 * Display and controls for second level of Anima
 */
public class Level2 extends JFrame implements GLEventListener {

    GLCanvas canvas;
    float xgap, avX, avY, obstructOdds, shinyOdds;
    GL gl;
    int ballPosition, counter, problem, problemCounter, score, answer, time, delay;
    LinkedList rowPositions, solutions;
    ArrayList obstructions, shinies;
    boolean avUp, avDown, avLeft, avRight, out;
    Sound background;
    float[] avatar, obstruction, shiny, ambientLight, lightPosition, whiteLight, glow;
    //Timer timer;




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

        setUndecorated(true);
        setBounds(400, 400, 1200, 600);
        

        xgap = (float)(10.0 / 14.0);//gap between columns
        out = false;

        avX = avY = 5;
        avUp = false;
        avDown = false;
        avLeft = false;
        avRight = false;
        obstructOdds = 0.2f;
        shinyOdds = 0.02f;
        counter = 0;
        score = 0;
        delay = 20;
        obstructions = new ArrayList();
        shinies = new ArrayList();


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

        lightPosition = new float[4];
        lightPosition[0] = 5.0f;
        lightPosition[1] = 5.0f;
        lightPosition[2] = 20.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;

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

        shiny = new float[4];
        shiny[0] = 0.0f;
        shiny[1] = 0.0f;
        shiny[2] = 1.0f;
        shiny[3] = 1.0f;
        
        glow = new float[4];
        glow[0] = 1.0f;
        glow[1] = 0.0f;
        glow[2] = 0.0f;
        glow[3] = 1.0f;



        background = new Sound();
        background.play("C:/Users/Owner/Documents/NetBeansProjects/anima/industry.wav");

        GLCapabilities caps = new GLCapabilities();
        caps.setDoubleBuffered(true);
        caps.setHardwareAccelerated(true);

        canvas.setFocusable(true);
        canvas.addMouseListener(new mouseHandler());
        canvas.addKeyListener(new keyHandler());
        add(canvas);


        //time = 42;
        final FPSAnimator animator = new FPSAnimator(canvas, 60);
        //InnerHandler timeHandle = new InnerHandler();
        //new Timer(2000, (ActionListener)timeHandle).start();

        setLocationRelativeTo(null);
        setVisible(true);
        animator.start();

    }

    /**
     * Sets my intial OpenGL state variables
     * @param drawable
     */
    public void init(GLAutoDrawable drawable) {

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

        // Setup the drawing area and shading mode
        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        gl.glShadeModel(GL.GL_SMOOTH);
        gl.glEnable(GL.GL_DEPTH_TEST);

        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, 20, 0, 10, -10, 10); //left, right, down, up, near, far
        gl.glMatrixMode(GL.GL_MODELVIEW);

    }

    /**
     * Method called when window is resized, included strictly as backup, as application
     * is displayed in full screen
     * @param drawable
     * @param x
     * @param y
     * @param width
     * @param height
     */
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        gl = drawable.getGL();

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

        gl.glMatrixMode(GL.GL_PROJECTION); /* switch matrix mode */
        gl.glLoadIdentity();
        gl.glOrtho(0, 20, 0, 10, -10, 10);
        gl.glMatrixMode(GL.GL_MODELVIEW); /* return to modelview mode */
    }


    /**
     * Draws the smaller orb used as an avatar in the first level
     * @param gl
     * @param avPosition
     * @param avY
     */
    private void avatar(GL gl)
    {
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, avatar, 0);
        gl.glBegin(GL.GL_TRIANGLE_FAN);
        for(int i = 0; i < 180; i++)
        {
            float xcoor = (float)((0.05 * Math.cos(i)) + avX);
            float ycoor = (float)(0.05 * Math.sin(i) + avY);
            gl.glVertex2f(xcoor, ycoor);

            xcoor = (float)(0.05 * Math.cos(i + 0.1) + avX);
            ycoor = (float)(0.05 * Math.sin(i + 0.1) + avY);
            gl.glVertex2f(xcoor, ycoor);
        }
        gl.glEnd();
    }

    private void shiny(GL gl, ArrayList shinies)
    {
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, shiny, 0);
        //gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, glow, 0);

        gl.glColor3f(1.0f, 0.82f, 0.0f);
        gl.glBegin(GL.GL_POLYGON);
        gl.glVertex2f((Float)shinies.get(0) + 0.25f, (Float)shinies.get(1) + 0.25f);
        gl.glVertex2f((Float)shinies.get(0) + 0.5f, (Float)shinies.get(1));
        gl.glVertex2f((Float)shinies.get(0) + 0.25f, (Float)shinies.get(1) - 0.25f);
        gl.glVertex2f((Float)shinies.get(0), (Float)shinies.get(1));
        gl.glEnd();
    }

    private void obstruction(GL gl, ArrayList stats)
    {
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, obstruction, 0);
        gl.glColor3f(0.0f, 1.0f, 0.0f);
        gl.glBegin(GL.GL_QUADS);
        gl.glVertex2d((Float)stats.get(0), (Float)stats.get(1));
        gl.glVertex2d((Float)stats.get(0) + (Float)stats.get(3), (Float)stats.get(1));
        gl.glVertex2d((Float)stats.get(0) + (Float)stats.get(3), (Float)stats.get(1) + (Float)stats.get(2));
        gl.glVertex2d((Float)stats.get(0), (Float)stats.get(1) + (Float)stats.get(2));
        gl.glEnd();
    }

    /**
     * Check to see if avatar is attempting to intersect obstructions
     * @param x
     * @param y
     * @param height
     * @param width
     */
    private void collisionCheck(float x, float y, float height, float width)
    {
        //first check for pushing obstruction
        if((avY > y) && (avY < y + height))
        {
            if((avX < x) && (avX > x - 0.3)) avX -= 0.3;
        }

        //check for collision with back of obstruction
        if((avY > y) && (avY < y + height))
        {
            if((avX > x + width) && (avX < x + 0.2 + height)) avX += 0.2;
        }

        //check for collision with top of obstruction
        if((avX < x + width) && (avX > x))
        {
            if((avY > y + height) && (avY < y + height + 0.2)) avY += 0.2;
        }

        //check for collision with bottom of obstruction
        if((avX < x + width) && (avX > x))
        {
            if((avY < y) && (avY > y - 0.2)) avY -= 0.2;
        }
    }

    /**
     * Check to see if avatar has penetrated bounding box for shiny object
     * @param xpos
     * @param ypos
     * @param index
     */
    private void checkScore(float xpos, float ypos, int index)
    {
        if((avX > xpos) && (avX < xpos + 0.5f))
        {
            if((avY > ypos - 0.25f) && (avY < ypos + 0.25f))
            {
                shinies.remove(index);
                score += 1345392;
            }
        }
    }

    public void loseScreen()
    {
        gl.glRasterPos2f(8.5f, 5.0f);
        GLUT glut = new GLUT();
        glut.glutBitmapString(glut.BITMAP_TIMES_ROMAN_24, "You Lose");
    }




    /**
     * Display method for OpenGL, gets called in a continuous loop by the animator
     * @param drawable
     */
    public void display(GLAutoDrawable drawable) {
        gl = drawable.getGL();
        GLUT glut = new GLUT();


        // Clear the drawing area
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);


        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);
        // Reset the current matrix to the "identity"
        gl.glLoadIdentity();

        //set properties for dividing lines
        gl.glColor3f(0.3f, 0.3f, 1.0f);
        
        float dice = (float)Math.random();

        if(dice < obstructOdds)
        {
            ArrayList box = new ArrayList();
            box.add(20.0f);
            float height = (float)Math.random();
            float width = (float)Math.random();
            float yPosition = ((float)Math.random() * 10.0f) - height;
            box.add(yPosition);
            box.add(height);
            box.add(width);
            obstructions.add(box);
        }

        if(dice < shinyOdds)
        {
            ArrayList shiny = new ArrayList();
            float position = (float)Math.random() * 10.0f;
            shiny.add(20.0f);
            shiny.add(position);
            shinies.add(shiny);
        }


        for(int i = 0; i < obstructions.size(); i++)
        {
            ArrayList temp = (ArrayList)obstructions.get(i);
            float Xposition = (Float)temp.get(0);
            float Yposition = (Float)temp.get(1);
            float height = (Float)temp.get(2);
            float width = (Float)temp.get(3);

            collisionCheck(Xposition, Yposition, height, width);
            Xposition -= 0.1;

            temp.set(0, Xposition);
            obstructions.set(i, temp);
            obstruction(gl, (ArrayList)obstructions.get(i));
        }
        
        for(int j = 0; j < shinies.size(); j++)
        {
            ArrayList temp = (ArrayList)shinies.get(j);
            float Xpos = (Float)temp.get(0);
            float Ypos = (Float)temp.get(1);


            Xpos -= 0.1;
            temp.set(0, Xpos);
            shinies.set(j, temp);
            shiny(gl, (ArrayList)shinies.get(j));
            checkScore(Xpos, Ypos, j);
        }

        if(avUp)
        {
            avY += 0.2;
            avUp = false;
        }
        if(avDown)
        {
            avY -= 0.2;
            avDown = false;
        }
        if(avLeft)
        {
            avX -= 0.2;
            avLeft = false;
        }
        if(avRight)
        {
            avX += 0.2;
            avRight = false;
        }





        //change color to red and call avatar display method
        gl.glColor3f(1.0f, 0.0f, 0.0f);
        avatar(gl);


        //draw score in at bottom corner of screen
        gl.glRasterPos3f(0.2f,0.2f,0);
        glut.glutBitmapString(glut.BITMAP_HELVETICA_18, "Score: " + score);
 
        gl.glFlush();

        if(avX < 0.2)
        {
            loseScreen();
            delay--;
        }

        if(score > 14000000)
        {
            gl.glRasterPos2f(4.5f, 5.0f);
            glut.glutBitmapString(glut.BITMAP_TIMES_ROMAN_24, "Evolving");
            delay--;
        }

        if(delay < 0) out = true;

    }

    //required method for OpenGL, not used in this implementation
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}

    /**
     * Inner class to handle generated events
     */
    class InnerHandler implements ActionListener
    {

        public InnerHandler()
        {

        }

        public void actionPerformed(ActionEvent e)
        {
            //time--;
        }

    }//end class InnerHandler

    /**
     * Inner class to handle keyboard events
     */
    class keyHandler implements KeyListener
    {
        public void keyReleased(KeyEvent e){}

        public void keyPressed(KeyEvent e)
        {

            //allow player to move avatar left and right
            if(e.getKeyCode() == e.VK_LEFT)
            {
                //if( avX > 0.2) avX -= 0.1;
                if( avX > 0.2) avLeft = true;
            }

            if(e.getKeyCode() == e.VK_RIGHT)
            {
                //if( avX < 19.9) avX += 0.1;
                if( avX < 19.9) avRight = true;
            }

            //allow player to move the avatar up and down
            if(e.getKeyCode() == e.VK_DOWN)
            {
                //if(avY > 0.2) avY -= 0.1;
                if(avY > 0.2) avDown = true;
            }

            if(e.getKeyCode() == e.VK_UP)
            {
                //if(avY < 9.9) avY += 0.1;
                if(avY < 9.9) avUp = true;
            }

            
            if(e.getKeyCode() == e.VK_SPACE)
            {
                
            }

            
            if(e.getKeyCode() == e.VK_ENTER)
            {
                
            }

        }

        public void keyTyped(KeyEvent e){}

    }//end class keyHandler

    /**
     * Inner class to handle mouse events, not used yet
     */
    class mouseHandler implements MouseListener
    {
        public void mousePressed(MouseEvent e) {}

        public void mouseReleased(MouseEvent e) {}

        public void mouseEntered(MouseEvent e) {}

        public void mouseExited(MouseEvent e) {}

        public void mouseClicked(MouseEvent e)
        {

            if(e.getButton() == e.BUTTON1)
            {


            }

            if(e.getButton() == e.BUTTON3)
            {

            }

        }

    }//end inner class mouseHandler
}

