package uk.co.beercave.gunboat.entities;

import uk.co.beercave.gunboat.ControlSystems;
import uk.co.beercave.gunboat.GameController;
import uk.co.beercave.gunboat.LevelOutro;
import uk.co.beercave.gunboat.ResourceManager;
import uk.co.beercave.gunboat.renderers.FaderRenderer;
import uk.co.beercave.gunboat.renderers.GuiRenderer;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;


public class GameWorld {
	//this is where all the objects live
	
	private static Array<Bullet> enemyShots;
	private static Array<SmokeParticle> smoke;
	private static Pool<SmokeParticle> smokePool;

	public static void initialise(){
		player = new Player();
		boats = new Array<Boat>();
		effects = new Array<Effect>();
		shots = new Array<Bullet>();
		enemyShots = new Array<Bullet>();
		smoke = new Array<SmokeParticle>();
		smokePool = new Pool<SmokeParticle>(){

			@Override
			protected SmokeParticle newObject() {
				return new SmokeParticle();
			}
			
		};
		boatPool = new Pool<Boat>(){
			
			@Override
			protected Boat newObject() {
				return new Boat();
			}
			
		};
		shotPool = new Pool<Bullet>(){

			@Override
			protected Bullet newObject() {
				return new Bullet();
			}
			
		};
		effectPool = new Pool<Effect>(){
			
			@Override
			protected Effect newObject() {
				return new Effect();
			}
			
		};
	}
	public static Player player;
	public static Array<Boat> boats;
	public static Array<Bullet> shots;
	private static Array<Effect> effects;

	public static Pool<Boat> boatPool;
	public static Pool<Bullet> shotPool;
	public static Pool<Effect> effectPool;
	
	public static int score = 0;
	public static int level = 0;
	public static int enemies = 10;
	public static float aggression = .5f;
	public static int maxAttackers = 1; //max number of enemies that can fire at once
	private static int health;
	private static boolean ending;
	private static float endTime = 0;
	
	public static void levelUp(){
		enemies += 5;
		aggression += .2f;
		level += 1;
		if (level %5 == 0) maxAttackers++;
	}
	public static void reset() {
		player.x = 400;
		health = 100;
		score = 0;
		lastShot = 0;
		shotPool.clear();
		boatPool.clear();
		effectPool.clear();
		shots.clear();
		boats.clear();
		effects.clear();
		multColor.set(Color.WHITE);
		multiplier = 1;
		betweenLevels = false;
		ended = false;
		ending = false;
		endTime = 0;
		
		level = 1;
		enemies = 10;
		aggression = .1f;
		maxAttackers = 1;
		startLevel();
	}
	private static void startLevel() {
		//generate some random boats
		for(int i=0; i< enemies; i++){
			Boat boat = boatPool.obtain();
			boat.init();
			boat.x = MathUtils.random(800);
			boat.z = MathUtils.random(1,9);
			boat.dx = MathUtils.random(1,5)*20*(MathUtils.randomBoolean()?1:-1);
			boats.add(boat);
		}
//
//		Boat boat = boatPool.obtain();
//		boat.init();
//		boat.x = 400;
////		boat.y = 0;
//		boat.z = 1;
//		boats.add(boat);
	}
	private static float lastShot;
	//shots per second
	private static float fireRate = 1.5f;
	public static void update(float delta) {
		lastShot += delta;
		drawGUI(delta);
		updatePhysics(delta);
		updateLogic(delta);
		if (boats.size == 0 && !betweenLevels){
			LevelOutro.reset();
			betweenLevels = true;
		}

	}
	
	//here we decide if anything's firing back
	private static void updateLogic(float delta){
		if (ending){
			endTime += delta;
			if (endTime < 6){
				
				//spawn some explosions?
				if (MathUtils.random() < 3f*delta){
					ResourceManager.explosion.play();
					explode(player.x + MathUtils.random(-100,100), -0.5f, Color.BLACK);
				}
				
			}
			if (endTime > 4 && endTime < 6){
				FaderRenderer.fadeOut(1);
			}
			if (endTime > 6){
				FaderRenderer.fadeIn(1);
				GuiRenderer.Text("GAME OVER", 100, 300, Color.RED);
			}
			if (endTime > 10){
				ended = true;
				//clear everything up

				for(int i=boats.size-1; i>=0; i--){
					boatPool.free(boats.get(i));
					boats.removeIndex(i);
				}
				for(int i=effects.size-1; i>=0; i--){
					effectPool.free(effects.get(i));
					effects.removeIndex(i);
				}
				for(int i=shots.size-1; i>=0; i--){
					shotPool.free(shots.get(i));
					shots.removeIndex(i);
				}
				for(int i=enemyShots.size-1; i>=0; i--){
					shotPool.free(enemyShots.get(i));
					enemyShots.removeIndex(i);
				}
			}
		}
		
		if (!betweenLevels && !ending){
			pickAttackers(delta);
			for(int i=boats.size-1; i >=0; i--){
				Boat boat = boats.get(i);
				if (boat.attacking){
					boat.attackTime -= delta;
					if ((int) (boat.attackTime*5) != (int) ((boat.attackTime + delta)*5)){
						ring(boat.x, boat.z, boat.dx, boat.sprite.color);
						ResourceManager.alert.play();
					}
					if (boat.attackTime <= 0){
						fireAtPlayer(boat);
					}
				}
			}
		}
	}
	private static void fireAtPlayer(Boat boat) {
		//right - what do these bastards fire at you?
		//fuck it - they fire squares! Magenta ones!
		//Gdx.app.log("", "FIRE!");
		Bullet bullet = shotPool.obtain();
		bullet.init();
		bullet.sprite.color.set(1,0,1,.5f);
		bullet.x = boat.x - 25;
		float timeToTarget = boat.z/2; //bullets move at 2 Z per second
		float distance = player.x - boat.x;
		bullet.dx = distance / timeToTarget; //however far the bullet has to travel to hit the player position in teh time it'll take to get to z-1
		bullet.z = boat.z;
		enemyShots.add(bullet);
		
		boat.attacking = false;
	}
	private static void pickAttackers(float delta) {
		//probability of something firing every second = aggression
		//probability of something firing everyframe = aggression * delta
		//so if random() < aggression * delta we fire
		float chance = aggression * delta;
		float roll = MathUtils.random();
		
		//Gdx.app.log("LOGIC", String.format("Rolled %s, chance %s", roll, chance));
		if (roll > chance) return;
		
		//ok, how many targets - add some maths for this later
		int targets = 1;
		if (maxAttackers > 1) targets = MathUtils.random(maxAttackers-1)+1;
		for(int i=0; i<1; i++){
			//pick an attacker
			Boat attacker = boats.random();
			
			if (attacker == null || attacker.attacking) continue; //picked one that's already attacking - it's your lucky day!
			
			attacker.attacking = true;
			attacker.attackTime = 1f; //1 second warning should do
		}
	}
	
	private static Color SmokeColor = new Color(.1f,.1f,.1f,1);
	private static void updatePhysics(float delta) {

		for(int i=smoke.size-1; i>=0; i--){
			SmokeParticle s = smoke.get(i);
			s.y += s.dy * delta;
			SmokeColor.a = s.alpha;
			s.alpha = s.alpha * (1-delta);
			float x = s.x + MathUtils.sinDeg(s.y * s.frequency*delta + s.offset) * s.amplitude;
			GuiRenderer.Sprite(ResourceManager.blankTx, x, s.y, MathUtils.random(.9f, 1.1f), SmokeColor);
			if (s.alpha <= .01f){
				smokePool.free(s);
				smoke.removeIndex(i);
			}
		}
		
		//should be random dependant on health level
		if (health <= 50){
			spawnSmoke();
		}
		if (!betweenLevels && !ending){
			GameController ctrl =ControlSystems.current; 
			if (ctrl != null){
				ctrl.update(delta, GuiRenderer.instance.camera);
				player.x += ctrl.getX() * delta * 100;
				player.x = Math.max(30, Math.min(770, player.x));
				//Gdx.app.log("Player position", new Float(player.x).toString());
				if (ctrl.getFire()){
					if (lastShot > 1/fireRate){
						fire();
						lastShot = 0;
					}
				}
			}
		}

		//collide boats with shots
		for(int i=0; i<boats.size; i++){
			Boat boat = boats.get(i);
			for(int j=0; j<shots.size; j++){
				Bullet bullet = shots.get(j);
				if (!bullet.alive) continue;

				float zScale = 1f/(boat.z+1);
				float w2 = boat.width/2*zScale;
				if (bullet.x < boat.x - w2 || bullet.x > boat.x + w2) continue;
				if (bullet.prevz < boat.z && bullet.z > boat.z){
					Gdx.app.log("", "Collided bullet with boat");
					bullet.alive = false;
					boat.alive = false;
					//this is where we spawn the explosions
					explode(boat.x, boat.z, boat.sprite.color);
					//scoring
					if (boat.sprite.color.equals(multColor)){
						multiplier++;
					}else{
						multColor.set(boat.sprite.color);
						multiplier = 1;
					}
					ResourceManager.boatKilled.play();
					score += 100*multiplier;
				}
				
			}
		}
		//collide enemy shots with player
		if (!betweenLevels){
			for(int j=0; j<enemyShots.size; j++){
				Bullet bullet = enemyShots.get(j);
				if (!bullet.alive) continue;
	
				float w2 = 50;
				//Gdx.app.log("", String.format("Checking for collision with player %s, %s", bullet.x, player.x));
				if (bullet.x < player.x - w2 || bullet.x > player.x + w2) continue;
				//Gdx.app.log("", String.format("Checking z, prevz : %s, %s", bullet.z, bullet.prevz));
				if (bullet.prevz > -1 && bullet.z < -1){
					Gdx.app.log("", "Collided bullet with player");
					bullet.alive = false;
					explode(player.x, -0.9f, Color.BLACK);
					//now we need to take some health from the player
					health -= 10;
					if (health < 0) health = 0;
					ResourceManager.playerHit.play();
					if (health <= 0 && !ending){
						ending = true;
						endTime = 0;
					}
				}
				
			}
		}
		for(int i=boats.size-1; i >= 0; i--){
			Boat b = boats.get(i);
			float zScale = 1f/(b.z+1);
			b.x += b.dx * delta * zScale;

			zScale = 1f/(b.z+1);
			float w2 = b.width/2*zScale;
			
			if (b.x > 800+150*zScale) b.x = -150*zScale;
			if (b.x < -150*zScale) b.x = 800+150*zScale;
			if (!b.alive){
				boats.removeIndex(i);
				boatPool.free(b);
			}
		}
		//Gdx.app.log("", "Moving bullets");
		for(int i=shots.size-1; i >= 0; i--){
			Bullet b = shots.get(i);
			b.prevz = b.z;
			b.z += 5*delta;
			b.x += b.dx * delta;
			//Gdx.app.log("Moved bullet to ", new Float(b.z).toString());
			if (b.z >= 10 || !b.alive){
				shots.removeIndex(i);
				shotPool.free(b);
			}
		}
		for(int i=enemyShots.size-1; i >= 0; i--){
			Bullet b = enemyShots.get(i);
			b.prevz = b.z;
			b.z -= 2*delta;
			b.x += b.dx * delta;
			//Gdx.app.log("Moved bullet to ", new Float(b.z).toString());
			if (b.z < -2 || !b.alive){
				enemyShots.removeIndex(i);
				shotPool.free(b);
			}
		}
		for(int i=effects.size-1; i >= 0; i--){
			Effect b = effects.get(i);
			float zScale = 1f/(b.z+1);
			b.sprite.rotation += b.dt * delta;
			b.sprite.scale += b.ds*delta;
			b.sprite.parent.x += b.dx * delta * zScale;
			b.life -= delta;
			//Gdx.app.log("Moved bullet to ", new Float(b.z).toString());
			if (!b.alive || b.life <= 0){
				effects.removeIndex(i);
				effectPool.free(b);
			}
		}
	}
	private static Color multColor = new Color();;
	private static int multiplier;
	private static boolean betweenLevels = true;
	private static float time = 0;
	public static boolean ended;
	private static void drawGUI(float delta) {
		//score
		GuiRenderer.Text(String.format("%06d", score), 0, 470, .2f);
		//multiplier and color
		GuiRenderer.Sprite(ResourceManager.blankTx, 220, 460, 1, multColor);
		GuiRenderer.Text(String.format("X%02d", multiplier), 240, 470, .2f);
		//level
		GuiRenderer.Text(String.format("LEVEL:%02d", level+1), 0, 450, .2f);
		
		GuiRenderer.Text("LIFE", 500, 470, .2f);
		Color lifeColor = Color.GREEN;
		if (health <= 50) lifeColor = Color.YELLOW;
		if (health <= 20) lifeColor = Color.RED;
		GuiRenderer.Sprite(ResourceManager.blankTx, 520, 440, health/16f, 1, lifeColor);
		
		//anything else?
		if (betweenLevels){
			//Gdx.app.log("WORLD", "rendering GUI");
			if (LevelOutro.update(delta)){
				betweenLevels = false;
				levelUp();
				startLevel();
				
			}
		}
		
	}

	private static void spawnSmoke() {
		SmokeParticle s = smokePool.obtain();
		s.init();
		s.x = player.x + MathUtils.random(-50,50);
		smoke.add(s);
	}
	private static void ring(float x, float z, float dx, Color color){
		Effect e = effectPool.obtain();
		e.init();
		e.x = x;
		e.dx = dx;
		e.z = z;
		e.sprite.tx = ResourceManager.ring;
		e.sprite.color.set(color);
		e.sprite.color.a = .8f;
		e.sprite.scale = 2;
		e.dt = 0;
		e.ds = 5;
		e.life = 2;
		effects.add(e);
	}
	private static void explode(float x, float z, Color color) {
		for(int i=5; i>0; i--){
			Effect e = effectPool.obtain();
			e.init();
			e.x = x;
			e.z = z;
			e.sprite.color.set(color);
			e.sprite.color.a = .1f;
			e.sprite.scale = 0;
			e.dt = 180;
			e.ds = 5;
			e.life = 1;
			effects.add(e);
			
			e = effectPool.obtain();
			e.init();
			e.x = x;
			e.z = z;
			e.sprite.color.set(color);
			e.sprite.color.a = .1f;
			e.sprite.scale = 0;
			e.dt = -180;
			e.ds = i;
			e.life = 1;
			effects.add(e);
		}
	}
	private static void fire() {
		//Gdx.app.log("", "FIRE!");
		ResourceManager.gunshot.play();
		Bullet bullet = shotPool.obtain();
		bullet.init();
		bullet.x = player.x - 25;
		bullet.dx = 12.5f;
		bullet.z = -0.5f;
		shots.add(bullet);

		bullet = shotPool.obtain();
		bullet.init();
		bullet.x = player.x + 25;
		bullet.dx = -12.5f;
		bullet.z = -0.5f;
		shots.add(bullet);
	} 
}
