package helowars;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 * Framework to control the game, update it and render images on the screen.
 * 
 * @author Adarshkumar Pavani
 */

public class Framework extends UserInputControl {


	// frame width and height.

	public static int frameWidth, frameHeight;

	public static final long secsInNanosecs = 1000000000L;

	public static final long milisecsInNanosecs = 1000000L;

	//FPS - Frames per second
	private final int GAME_FPS = 60;
	
	//pause between updates (in nanosecs)
	private final long GAME_UPDATE_PERIOD = secsInNanosecs / GAME_FPS;

	/**
	 * All possible states of the game
	 */
	public static enum GameplayState{STARTING, VISUALIZING, GAME_LOADING, MAIN_MENU, OPTIONS, PLAYING, GAMEOVER, DESTROYED}
	/**
	 * object to store current state of game
	 */
	public static GameplayState gameState;

	//elapsed time (in nanosecs)
	private long gameTime;
	
	// used for calculating elapsed time.
	private long lastTime;

	// main game object
	private Game game;


	private Font fontHeader, fontBody;

	// images for menu
	private BufferedImage titleScreenImage;
	private BufferedImage frameBorderImage;
	private BufferedImage skyImage;
	private BufferedImage clouds1Image;
	private BufferedImage clouds2Image;


	//constructor
	public Framework ()
	{
		super();

		gameState = GameplayState.VISUALIZING;

		//starting game in new thread.
		Thread gameThread = new Thread() {
			@Override
			public void run(){
				GameLoop();
			}
		};
		gameThread.start();
	}


	/**
	 * Set variables and objects.
	 */
	private void Initialize()
	{
		//for headings
		fontHeader = new Font(Font.MONOSPACED, Font.BOLD, 40);
		
		//for body text
		fontBody= new Font(Font.SERIF, 0, 20);
	}

	/**
	 * To load image files
	 */
	private void LoadContent()
	{
		try 
		{
			URL frameBorderPath = this.getClass().getResource("/helowars/resources/frame_border.png");
			frameBorderImage = ImageIO.read(frameBorderPath);

			URL skyPath = this.getClass().getResource("/helowars/resources/sky.jpg");
			skyImage = ImageIO.read(skyPath);

			URL titleScreenPath = this.getClass().getResource("/helowars/resources/TitleScreen.png");
			titleScreenImage = ImageIO.read(titleScreenPath);

			URL clouds1Path = this.getClass().getResource("/helowars/resources/clouds1.png");
			clouds1Image = ImageIO.read(clouds1Path);
			URL clouds2Path = this.getClass().getResource("/helowars/resources/clouds2.png");
			clouds2Image = ImageIO.read(clouds2Path);
		} 
		catch (IOException ex) 
		{
			Logger.getLogger(Framework.class.getName()).log(Level.SEVERE, null, ex);
		}
	}


	/**
	 * w.r.t. GAME_UPDATE_PERIOD the game is updated periodically
	 */
	private void GameLoop()
	{
		// used in VISUALIZING state of the game to get correct window resolution.
		long visualizingTime = 0, lastVisualizingTime = System.nanoTime();

		// duration to put thread to sleep to meet the GAME_FPS.
		long beginTime, timeTaken, timeLeft;

		while(true)
		{
			beginTime = System.nanoTime();

			switch (gameState)
			{
			case PLAYING:
				gameTime += System.nanoTime() - lastTime;

				game.UpdateGame(gameTime, mousePosition());

				lastTime = System.nanoTime();
				break;
			case GAMEOVER:
				//...
				break;
			case MAIN_MENU:
				//...
				break;
			case OPTIONS:
				//...
				break;
			case GAME_LOADING:
				//...
				break;
			case STARTING:
				
				Initialize();

				LoadContent();

				//calling main menu
				gameState = GameplayState.MAIN_MENU;
				break;
			case VISUALIZING:

				
				if(this.getWidth() > 1 && visualizingTime > secsInNanosecs)
				{
					frameWidth = this.getWidth();
					frameHeight = this.getHeight();

					//change status on getting size of frame
					gameState = GameplayState.STARTING;
				}
				else
				{
					visualizingTime += System.nanoTime() - lastVisualizingTime;
					lastVisualizingTime = System.nanoTime();
				}
				break;
			}

			// Repaint the screen.
			repaint();

			// duration to make the thread sleep in order to meet the GAME_FPS.
			timeTaken = System.nanoTime() - beginTime;
			timeLeft = (GAME_UPDATE_PERIOD - timeTaken) / milisecsInNanosecs; // In milliseconds
			// putting thread to sleep for 10millisecs if the time is less than 10 millisecs, so that some other thread can do some work.
			if (timeLeft < 10) 
				timeLeft = 10; 
			try {
				
				Thread.sleep(timeLeft);
			} catch (InterruptedException ex) { }
		}
	}

	/**
	 * render the game on the screen.
	 */
	@Override
	public void Draw(Graphics2D g2d)
	{
		switch (gameState)
		{
		case PLAYING:
			game.Draw(g2d, mousePosition(), gameTime);
			break;
		case GAMEOVER:
			drawMenuBackground(g2d);
			g2d.setColor(Color.black);
			g2d.setFont(fontBody);
			g2d.drawString("Press ENTER to restart or ESC to exit.", frameWidth/2 - 160, frameHeight/4 + 50);
			game.drawStats(g2d, gameTime);
			g2d.setFont(fontHeader);
			g2d.drawString("GAME OVER", frameWidth/2 - 120, frameHeight/4);
			break;
		case MAIN_MENU:
			drawMenuBackground(g2d);
			g2d.drawImage(titleScreenImage, frameWidth/2 - titleScreenImage.getWidth()/2, frameHeight/2 - titleScreenImage.getHeight()/2, null);
			g2d.setColor(Color.black);
			break;
		case OPTIONS:
			//...
			break;
		case GAME_LOADING:
			g2d.setColor(Color.white);
			g2d.drawString("LOADING...", frameWidth/2 - 50, frameHeight/2);
			break;	
		}
	}


	/**
	 * Starts new game.
	 */
	private void newGame()
	{
		// setting elapsed time to zero and lastTime to current time for later calculations.
		gameTime = 0;
		lastTime = System.nanoTime();

		game = new Game();
	}

	/**
	 * to restart the game so as to reset some variables.
	 */
	private void restartGame()
	{
		gameTime = 0;
		lastTime = System.nanoTime();

		game.restartGame();

		// changing game status to begin play
		gameState = GameplayState.PLAYING;
	}


	/**
	 * get get the position of mouse pointer in game window.
	 * @return Point of mouse coordinates.
	 */
	private Point mousePosition()
	{
		try
		{
			Point mp = this.getMousePosition();

			if(mp != null)
				return this.getMousePosition();
			else
				return new Point(0, 0);
		}
		catch (Exception e)
		{
			return new Point(0, 0);
		}
	}


	/**
	 * This method is called when keyboard key is released.
	 * @param e KeyEvent
	 */
	@Override
	public void keyReleasedFramework(KeyEvent e)
	{
		if(e.getKeyCode() == KeyEvent.VK_ESCAPE)
			System.exit(0);

		switch(gameState)
		{
		case GAMEOVER:
			if(e.getKeyCode() == KeyEvent.VK_ENTER)
				restartGame();
			break;
		case MAIN_MENU:
			newGame();
			break;
		}
	}

	/**
	 * This method is called when mouse button is clicked.
	 * @param e MouseEvent
	 */
	@Override
	public void mouseClicked(MouseEvent e)
	{

	}
	
	//rendering menu images on the screen
	private void drawMenuBackground(Graphics2D g2d){
		g2d.drawImage(skyImage,    0, 0, Framework.frameWidth, Framework.frameHeight, null);
		g2d.drawImage(clouds1Image, 0, 0, Framework.frameWidth, Framework.frameHeight, null);
		g2d.drawImage(clouds2Image, 0, 0, Framework.frameWidth, Framework.frameHeight, null);
		g2d.drawImage(frameBorderImage,  0, 0, Framework.frameWidth, Framework.frameHeight, null);
		g2d.setColor(Color.white);
		g2d.drawString("ADARSHKUMAR PAVANI", 7, frameHeight - 5);
	}
}
