package net.phatcode.rel;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

import net.phatcode.rel.assets.ImageTextureDataBullet;
import net.phatcode.rel.assets.ImageTextureDataDigiFont;
import net.phatcode.rel.assets.ImageTextureDataEnemies;
import net.phatcode.rel.assets.ImageTextureDataFont;
import net.phatcode.rel.assets.ImageTextureDataPlanets;
import net.phatcode.rel.assets.ImageTextureDataShip;
import net.phatcode.rel.assets.ImageTextureDataSpaceBackground;
import net.phatcode.rel.utils.Explosion;
import net.phatcode.rel.utils.ImageAtlas;
import net.phatcode.rel.utils.Keyboard;
import net.phatcode.rel.utils.Sonics;
import net.phatcode.rel.utils.SpriteFont;

public class Game
{

	private double accumulator = 0;

	private int fps = 0;
	private int framesPerSecond = 0;
	private double previousTime = 0;
	private double oldTime = 0;
	private double secondsElapsed = 0;

	private ImageAtlas spriteShip = new ImageAtlas( new ImageTextureDataShip() );
	private ImageAtlas spriteBullets = new ImageAtlas( new ImageTextureDataBullet() );
	private ImageAtlas spriteEnemies = new ImageAtlas( new ImageTextureDataEnemies() );
	
	private SpriteFont fontSi = new SpriteFont( new ImageAtlas( new ImageTextureDataFont(), 16, 16 ) );
	private SpriteFont fontDigi = new SpriteFont( new ImageAtlas( new ImageTextureDataDigiFont(), 16, 16 ) );

	private ShipEntity viper = new ShipEntity();
	private BulletFactory bullets = new BulletFactory(2048);
	private TurretFactory turrets = new TurretFactory(16);
	
	private LevelMap parallaxLevel = new LevelMap( 2, 256/2, 256, 
									 new ImageAtlas( new ImageTextureDataSpaceBackground(), 256, 256 ) );
	private LevelMap baseLevel = new LevelMap( 2, 256, 256, 
			 					 new ImageAtlas( new ImageTextureDataPlanets(), 256, 256 ) );

	private int counter = 0;
	
	private boolean drawAabbs = false;
	
	private ArrayList<BulletPattern> patterns = new ArrayList<BulletPattern>();
	
	
	private ArrayList<EnemyEntityFactory> enemies = new ArrayList<EnemyEntityFactory>();
	private EnemyEntityFactory enemyFangs = new EnemyEntityFactoryFang( 8 );
	
	public Game()
	{
		
	}

	public void init( BufferStrategy strategy, Screen screen )
	{
		 
		Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
		g2D.setColor(Color.BLACK);
		g2D.fillRect(0, 0, Constants.SCREEN_WIDTH, Constants.SCREEN_HEIGHT);

		Toolkit.getDefaultToolkit().sync();

		
		fontDigi.print(   0,  0, 
				"LOADING LEVELS...",
				screen, g2D );

		strategy.show();
		
		parallaxLevel.fill( 1 );
		baseLevel.randomize( baseLevel.getNumImages() );

		
		fontDigi.print(   0,  30, 
				"ADDING BULLET PATTERNS...",
				screen, g2D );

		strategy.show();

		patterns.add( BulletPatternFactory.fullSpread( new Animator(19,3) ) );
		patterns.add( BulletPatternFactory.wasak( new Animator( 26, 1, 24, Animator.Mode.ORIENTED) ) );
		patterns.add( BulletPatternFactory.shinto( new Animator(76,4) ) );
		patterns.add( BulletPatternFactory.arrowRing( new Animator(4,8) ) );
		patterns.add( BulletPatternFactory.arrowSpread( new Animator(56,4) ) );
		patterns.add( BulletPatternFactory.clawSpread( new Animator(22,4) ) );
		patterns.add( BulletPatternFactory.clawRing( new Animator(68,8) ) );
		patterns.add( BulletPatternFactory.ring( new Animator(79,4) ) );
		patterns.add( BulletPatternFactory.fullRingCenterAim( new Animator(76,4) ) );
		patterns.add( BulletPatternFactory.fullSpreadDecel( new Animator(52,3) ) );
		
			
		fontDigi.print(   0,  60, 
				"LOADING MUSIC AND EFFECTS...",
				screen, g2D );
		fontDigi.print(   0,  80, 
				"THIS MAY TAKE A WHILE.",
				screen, g2D );

		g2D.dispose();
		strategy.show();

		Sonics.getInstance().addMusic("media/level01.mid");
		Sonics.getInstance().playMusic("media/level01.mid", true);
		
		Sonics.getInstance().addEffect("media/shot.wav");
		Sonics.getInstance().addEffect("media/enemyhit.wav");
		
		enemies.add( enemyFangs );
		
		Explosion.getInstance().init();
		

	}
	
	private void update() 
	{

		counter++;
		
		if( (counter & 255) == 0 )
		{
			enemyFangs.spawn( turrets, 0, 0, 0, 0 );
		}
		
		handleCollisions();
		
		if( Keyboard.getInstance().isKeyReleased(KeyEvent.VK_F2))
		{
			drawAabbs = !drawAabbs;
		}
		
		if( Keyboard.getInstance().isKeyReleased(KeyEvent.VK_F3))
		{
			bullets.destroyAllBullets();
		}
		
		turrets.updateTurrets( bullets, viper.position );
		
		
		
		viper.update();
		bullets.updateBullets( viper.position );
		parallaxLevel.update( 2.0f );
		baseLevel.update( 4.0f );
		
		int size = enemies.size();
		for( int i = 0; i < size; i++ )
		{
			enemies.get(i).updateAll( bullets, viper.position );
		}
		
		Explosion.getInstance().updateAll();
		
		Keyboard.getInstance().resetKeys();
		
		
	}
	
	private void handleCollisions()
	{
		bullets.collideWithPlayer( viper );
		
		int size = enemies.size();
		for( int i = 0; i < size; i++ )
		{
			enemies.get(i).collideWithPlayer( viper );
		}
		
	}
	
	private void render( Graphics2D g2D, Screen screen ) 
	{

		parallaxLevel.renderMap( screen, g2D );
		baseLevel.renderMap( screen, g2D );
		
		int size = enemies.size();
		for( int i = 0; i < size; i++ )
		{
			enemies.get(i).renderAll( screen, g2D, spriteEnemies );
		}
		
		viper.render( screen, g2D, spriteShip );
		
		bullets.renderBullets( screen, g2D, spriteBullets );
		
		Explosion.getInstance().renderAll( screen, g2D );
		
		if( drawAabbs )
		{
			bullets.renderAabbs( screen, g2D, spriteBullets );
			viper.renderAabb( screen, g2D, spriteShip );
			int s = enemies.size();
			for( int i = 0; i < s; i++ )
			{
				enemies.get(i).renderAabbs( screen, g2D, spriteEnemies );
			}
			
		}
		
		fontSi.print(   0,   0, "FPS :" + fps, screen, g2D );
		fontSi.print( 200,   0, "SECONDS :" + (int) secondsElapsed, screen, g2D );
		
		fontDigi.printCenter(   0,  20, 
								Constants.SCREEN_WIDTH,
								" ACTIVE BULLETS :" + bullets.getActiveBullets() + " ",
								screen, g2D );
		fontDigi.printCenter(   0,  40, 
								Constants.SCREEN_WIDTH,
								" ACTIVE SHOTS :" + viper.getActiveShots() + " ",
								screen, g2D );
	
		fontDigi.print( 0,   60, "TURRETS :" + (int)turrets.getActiveTurrets(), screen, g2D );
		fontDigi.print( 0,   80, "FANGS :" + (int)enemies.get(0).getActiveEnemies(), screen, g2D );
		fontDigi.print( 0,   100, "EXPLOSIONS :" + (int)Explosion.getInstance().getActiveExplosions(), screen, g2D );
		
		
	}


	public void run( BufferStrategy strategy, Screen screen ) 
	{

		double dt = getDeltaTime(getSystemTime());
		
		init( strategy, screen  );
		
		while (true) 
		{

			dt = getDeltaTime(getSystemTime());
			if (dt > Constants.FIXED_TIME_STEP) dt = Constants.FIXED_TIME_STEP;
			accumulator += dt;
			secondsElapsed += dt;
			
			while( accumulator >= Constants.FIXED_TIME_STEP ) 
			{

				accumulator -= Constants.FIXED_TIME_STEP;
				update();
								
			}

			
			Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
			g2D.setColor(Color.BLACK);
			g2D.fillRect(0, 0, Constants.SCREEN_WIDTH, Constants.SCREEN_HEIGHT);

			render( g2D, screen );
			g2D.dispose();
			strategy.show();
			Toolkit.getDefaultToolkit().sync();
			

			if( Keyboard.getInstance().isKeyPressed( KeyEvent.VK_ESCAPE ) )
			{
				Sonics.getInstance().shutDown();
				System.exit(0);
			}
			
			try 
			{
				Thread.sleep(1);
			} 
			catch (InterruptedException e)
			{
				System.out.println("interrupted");
			}

		}

	}

	public ShipEntity getShip()
	{
		return viper;
	}
	
	
	public Keyboard getKeyHandler()
	{
		return Keyboard.getInstance();
	}
	
	
	public double getSystemTime() 
	{
		return System.nanoTime() / 1000000000.0;
	}

	public double getDeltaTime( double timerInSeconds ) 
	{

		double currentTime = timerInSeconds;
		double elapsedTime = currentTime - oldTime;
		oldTime = currentTime;

		framesPerSecond++;

		if ((currentTime - previousTime) > 1.0) 
		{
			previousTime = currentTime;
			fps = framesPerSecond;
			framesPerSecond = 0;
		}

		return elapsedTime;
	}

		
}  // end class

