
import java.util.ArrayList;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.input.Mouse;


/**
 * This is the main class of  the game. This renders the game, handles input and output etc.
 * Important methods: update, renderGL
 * @author vanitas
 *
 */
public class Game{
	long lastFrame, lastFPS;										//Values to calibrate the framerate etc.
	int fps;
	public static final int frameSizeX = 800, frameSizeY = 600;		//Resolution of the Window
	@SuppressWarnings("rawtypes")
	private ArrayList<ArrayList> objects;							//A ArrayList containing th following Lists
	private ArrayList<Metroid> metroids;							//A List Containing all the metroids
	private ArrayList<MovingObject> ships;							//A List Containing all the ships etc
	private static ArrayList<Ammunition> munition;					//A List Containing all the active fired projectiles
	
	private ControlledShip playerShip;								//The players ship
	
	private Menu menu;												//The Menu-Display
	private boolean menuIsActive;									//if menu is active this variable is true
	private boolean closeRequested = false;
	/**
	 * Constructor
	 */
	@SuppressWarnings("rawtypes")
	public Game()
	{
		//
		//Initialize ship and Objects (Add Objects here and dont forget to add them to the ArrayLists as well;)
		playerShip = new ControlledShip();
		Turret geschuetz = new Turret(playerShip.getX(),playerShip.getY());
		geschuetz.setParent(playerShip);
		
		//Initialize ArrayLists
		objects = new ArrayList<ArrayList>();
		metroids = new ArrayList<Metroid>();
		ships = new ArrayList<MovingObject>();
		munition = new ArrayList<Ammunition>();
		
		//Add Objects to Lists
		ships.add(playerShip);
		ships.add(geschuetz);
		
		//Pack Lists together
		objects.add(metroids);
		objects.add(ships);
		objects.add(munition);
	}
	/**
	 * This method updates all the objects (moves them, captures keyboard and mouse input etc)
	 * @param delta 
	 */
	public void update(int delta) 
	{
		//Handle Keyboard KeyPresses
		//Gas geben ;)
		if (Keyboard.isKeyDown(Keyboard.KEY_UP))	//Arrow up
		{
			playerShip.setVelocity(1f);	//Set the velocity to 1 (Power on)
		}
		//rotation (Arrow right)
		if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) 
		{
			playerShip.turnRight(delta);
		}
		//Arrow left
		if (Keyboard.isKeyDown(Keyboard.KEY_LEFT))
		{
			playerShip.turnLeft(delta);
		}
		//When Arrow up is released, turn down the power.
		if(!Keyboard.isKeyDown(Keyboard.KEY_UP))
		{
			playerShip.setVelocity(0f);
		}
		//If Spacebar is pressed and shotburnout is over, fire a projectile
		if(Keyboard.isKeyDown(Keyboard.KEY_SPACE))
		{
			playerShip.fire(delta);
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
		{
			menuIsActive = true;
		}
		//Call an update (movements etc.) for every Object in the game
		updateObjects(delta);
		updateFPS(); // update FPS Counter
	}
	/**
	 * Update (accelerate and move) all the Objects in the ArrayLists
	 * @param delta
	 */
	public void updateObjects(float delta)
	{
		for(int i = 0; i<objects.size(); i++)
		{
			ArrayList<?> l = objects.get(i);
			for(int j = 0; j<l.size(); j++)
			{
				((MovingObject) l.get(j)).accelerate();
				((MovingObject) l.get(j)).move(delta, true);
				
			}
		}
	}
	/**
	 * Main Rendering method. This calls the subrendermethods for the ship, metroids etc.
	 * This renderes all the Objects in the Lists.
	 * Remember: Whatever gets rendered last gets rendered on top of everything else (Stack)
	 */
	public void renderGL() {
		// Clear The Screen And The Depth Buffer
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		//Load ArrayLists and render every Object inside
			for(int i = 0; i<objects.size(); i++)
			{
				ArrayList<?> l = objects.get(i);
				for(int j = 0; j<l.size(); j++)
				{
					//Render
					renderObject((MovingObject) l.get(j));
				}
			}
	}
	/**
	 * This Method should be able to render any Shape with vertices given by Shape
	 * @param o
	 */
	public void renderObject(MovingObject o)
	{
		//Get the shape of the object o
		Shape s = o.getShape();
		//get the color
		float[] c = s.getColor();
		//Beginn a matrix
		GL11.glPushMatrix();
		//Set color
		GL11.glColor3f(c[0], c[1], c[2]);
		//Move it
		GL11.glTranslatef(o.getX(), o.getY(), 0f);
		//Rotate and scale
		GL11.glRotatef(-o.getRotation(), 0f, 0f, 1f);
		GL11.glScalef(s.getSize(), s.getSize(), 1f);
		//Translate it back
		GL11.glTranslatef(-o.getX(), -o.getY(), 0f);
		//Load all the Vertices
		GL11.glBegin(GL11.GL_POLYGON);
		for(int i = 0; i<s.getLength(); i++)
		{
			GL11.glVertex2f(o.getX()+s.getX(i),o.getY()+s.getY(i));
		}
		GL11.glEnd();
		GL11.glPopMatrix();
	}
	
	/**
	 * Initializes the OpenGL System. Ignore this.
	 */
	public void initGL() 
	{
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, frameSizeX, 0, frameSizeY, 1, -1);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}
	/**
	 * This method starts the game
	 */
	public void start() 
	{
		try 						//Create the Display
		{
			Display.setDisplayMode(new DisplayMode(frameSizeX, frameSizeY));
			Display.create();
		} 
		catch (LWJGLException e) 	//When something goes wrong, evacuate the dancefloor!!!
		{
			e.printStackTrace();
			System.exit(0);
		}
		initGL(); 					//initialize OpenGL
		getDelta(); 				
		lastFPS = getTime(); 		//initialize timer (just for calibrating framerate, ignore this!)
		//This loop runs as long as the game is running. Every run creates a frame
		while (!Display.isCloseRequested() && !this.closeRequested) 
		{
			int delta = getDelta();	//Value to sync fps and movements (lower fps != lower moves)

			update(delta);
			renderGL();				//Render the frame

			if(!menuIsActive) Display.update();		//Update the display (I have no idea ;))
			Display.sync(60);		//cap fps to 60fps 
		}
		
		Display.destroy();			//Kill the display, when the window is closed
	}
	/**
	 * Returns a value used to sync fps and movements
	 * @return
	 */
	public int getDelta() 
	{
		long time = getTime();
		int delta = 0;
		if(!menuIsActive) 
		{
			delta = (int) (time - lastFrame); //If the menu is active, there should be no more movements etc
		}
		lastFrame = time;

		return delta;
	}
	/**
	 * Returns the time of the system in (i think) milliseconds
	 * @return
	 */
	public long getTime() 
	{
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
	/**
	 * Updates the title of the screen with the current fps
	 */
	public void updateFPS() 
	{
		if (getTime() - lastFPS > 1000) 
		{
			Display.setTitle("The best game in the world. FPS: " + fps);
			fps = 0;
			lastFPS += 1000;
		}
		fps++;
	}
	public ArrayList<ArrayList> getObjects()
	{
		return objects;
	}
	public void setObjects(ArrayList<ArrayList> objects)
	{
		this.objects = objects;
	}
	public ArrayList<Metroid> getMetroids()
	{
		return metroids;
	}
	public void setMetroids(ArrayList<Metroid> metroids)
	{
		this.metroids = metroids;
	}
	public ArrayList<MovingObject> getShips()
	{
		return ships;
	}
	public void setShips(ArrayList<MovingObject> ships)
	{
		this.ships = ships;
	}
	public static ArrayList<Ammunition> getMunition()
	{
		return munition;
	}
	public void setMunition(ArrayList<Ammunition> munition)
	{
		this.munition = munition;
	}
}
