package org.crusty.wurrums.entities;

import org.crusty.wurrums.game.Game;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.Random;

import org.crusty.wurrums.levels.TerrainLevel;

import org.crusty.engine.CrustyEngine;
import org.crusty.engine.SoundManager;
import org.crusty.engine.entity.Entity;
import org.crusty.engine.sprite.Sprite;
import org.crusty.engine.sprite.SpriteManager;
import org.crusty.math.Vec2;
//import org.crusty.util.DoubleUtil;
import org.crusty.util.DoubleUtil;

public class Worm extends Entity {

	private boolean movementDebug = false;
	
//	BufferedImage background;
	
	TerrainLevel terrainLevel;
	
	boolean onGround = false;
	boolean canControl = true;
	double maxVel = 5000;
	double maxHorizontalAirSpeed = 0.09;
	double maxGroundSpeed = 0.08;
	double gravity = 5;
	int vertMaxStep = 5;
	/** Minimum vel needed to initiate ground mode */
	double initGroundVel = 0.3;
	int jumpAcc = -250;
	double friction = 0.96;
	/** true = right, false = left */
	boolean direction = true;
	long lastControl = 0;
	Gun gun = null;
	int downTimeIfHitRoof = 300;
	boolean dead = false;

	Vec2 prevPos = new Vec2(0, 0);

	private long lastDied = 0;

	private int deathTimeout = 2000;
	
	public Worm(TerrainLevel terrainLevel, double x, double y, Sprite[] sprites, int xoffset, int yoffset) {
		super(sprites, xoffset, yoffset);
		this.pos.x = x;
		this.pos.y = y;
		this.prevPos.x = x;
		this.prevPos.y = y;
		this.depth = 5;
//		this.background = background;
		this.terrainLevel = terrainLevel;
		timeBetweenFrames = 50;
		animating = true;
	}
	
	@Override
	public void draw(Graphics2D g) {
		
//		int size = 10;
//		g.setColor(Color.GREEN);
//		g.fillOval((int) this.pos.x - size, (int) this.pos.y - size, size*2, size*2);
		
		if (movementDebug) {
			g.setColor(Color.BLACK);
			g.fillRect(5, 5, 200, 120);
			g.setColor(Color.WHITE);
			g.drawRect(5, 5, 200, 120);
			g.drawString("pos: " + DoubleUtil.roundTwoDec(this.pos.x) + ", " + DoubleUtil.roundTwoDec(this.pos.y), 10, 40);
			g.drawString("vel: " + DoubleUtil.roundTwoDec(this.vel.x) + ", " + DoubleUtil.roundTwoDec(this.vel.y), 10, 60);
			g.drawString("acc: " + DoubleUtil.roundTwoDec(this.acc.x) + ", " + DoubleUtil.roundTwoDec(this.acc.y), 10, 80);
			g.drawString("onGround: " + onGround, 10, 100);
			g.drawString("prevPos: " + DoubleUtil.roundTwoDec(prevPos.x) + ", " + DoubleUtil.roundTwoDec(prevPos.y), 10, 120);
		}
		
//		g.setColor(Color.BLACK);
//		g.fillRect(5, 570, 280, 20);
//		g.setColor(Color.WHITE);
//		g.drawRect(5, 570, 280, 20);
//		g.drawString("Wurrums Prototype - Elliot Walmsley 26/04/11", 10, 585);
		
		super.draw(g);
		
//		if (gun != null)
//			gun.draw(g);
		
//		g.setColor(Color.YELLOW);
//		if (direction) {
//			g.drawLine((int) pos.x, (int) pos.y, (int) pos.x + 10, (int) pos.y);
//		} else {
//			g.drawLine((int) pos.x, (int) pos.y, (int) pos.x - 10, (int) pos.y);
//		}
		
	}
	
	public void setGun(Gun g) {
		this.gun = g;
	}
	
	/**
	 * -1 Falling
	 * -2 Uphill
	 * Any other number is height worm should be at
	 * @param x
	 * @param low
	 * @param high
	 * @return
	 */
	public int groundPoint(int x, int low, int high) {
		int y = (low+high)/2;
//		if (y >= background.getHeight() - 1)
//			return -1;
		
		if (terrainLevel.isFree(x, y)) { // Middle is black
			// Downhill
			for (int i = (low+high)/2; i < low; i++) {
				if (i > terrainLevel.getHeight() - 1)
					return terrainLevel.getHeight() - 1;
				
				if (!(terrainLevel.isFree(x, i))) {
					return i - 1;
				}
			}
//			System.out.println("DOWNHILL");
			// Falling
			return -1;
		} else { // Middle is coloured
			// Uphill
			for (int i = (low+high)/2; i > high; i--) {
				if (i < 0)
					break;
				
//				int c1 = background.getRGB(x, i);
//				int  red1   = (c1 & 0x00ff0000) >> 16;
//				int  green1 = (c1 & 0x0000ff00) >> 8;
//				int  blue1  =  c1 & 0x000000ff;
				if (terrainLevel.isFree(x, i)) {
					return i;
				}
			}
//			System.out.println("UPHILL");
			return -2;
		}
		
		/*
		 *       |
		 *       | 
		 * ___  ---O-- 
		 *    \__|__
		 *       |  \_____
		 */
	}
	
	public void logic(long dt) {
		
		if (dead) {
			if (CrustyEngine.currentTimeMillis() - lastDied  > deathTimeout) {
				terrainLevel.reset();
			} else {
				Random r = new Random();
				if (r.nextInt(8) == 0)
					CrustyEngine.getParticleManager().createBloodBurst(1, SpriteManager.getSprite("img/particles/bloodsmall.bmp"), pos);
			}
		}
		
		if (terrainLevel == null)
			return;
		
		if (animating) {
//			System.out.println(".");
			if (CrustyEngine.currentTimeMillis() - lastTime > timeBetweenFrames) {
				// Flip frames
				lastTime = CrustyEngine.currentTimeMillis();
				curImage++;
				if (curImage >= currentSprite.images.length) 
					curImage = 0;
				currentImage = currentSprite.images[curImage];
//				System.out.println("Flip: " + curImage + "/" + currentSprite.images.length);
			}
		}
		
//		if (gun != null)
//			gun.logic(dt);
		rotation += rotationalVel * dt / 1000000;
		
		this.acc.y = 0;
		this.acc.x = 0;
		
		/* FORCES */
		if (!onGround) {
			this.acc.y = gravity; // Gravity
		} else {
			// OnGround
		    if (canControl && (Game.keys[KeyEvent.VK_SPACE] || Game.keys[KeyEvent.VK_UP] || Game.keys[KeyEvent.VK_W])) {
		    	// Jump
		    	if (movementDebug)
					System.out.println("Jump");
		    	SoundManager.playSound("sounds/jump.wav");
		    	this.acc.y = jumpAcc;
		    	onGround = false;
		    	if (!direction)
		    		currentSprite = this.sprites[4];
		    	else
		    		currentSprite = this.sprites[5];
		    }
		}
	    
		if (!dead && !canControl && CrustyEngine.currentTimeMillis() - lastControl > downTimeIfHitRoof) {
			canControl = true;
		}
		
		boolean buttonPressed = false;
		if (canControl) {
		    if (Game.keys[KeyEvent.VK_LEFT] || Game.keys[KeyEvent.VK_A]) {
		    	this.acc.x = -3;
		    	direction = false;
		    	if (!onGround)
		    		currentSprite = this.sprites[4];
		    	else
		    		currentSprite = this.sprites[0];
		    	setAnimating(true);
		    	buttonPressed = true;
		    }
		    if (Game.keys[KeyEvent.VK_RIGHT] || Game.keys[KeyEvent.VK_D]) {
		    	this.acc.x = 3;
		    	direction = true;
		    	if (!onGround)
		    		currentSprite = this.sprites[5];
		    	else
		    		currentSprite = this.sprites[1];
		    	setAnimating(true);
	
		    	buttonPressed = true;
		    }
		}
	    if (!buttonPressed) {
	    	setAnimating(false);
	    	setCurImage(0);
	    }
	    
	    /* CALC NEW VEL */
		vel.x += acc.x / 1000;
		vel.y += acc.y / 1000;
		
	    /* LIMITS */
		if (onGround) {
			if (vel.length() > maxGroundSpeed) {
				Vec2 v = vel.normalise();
				vel.x = v.x * maxGroundSpeed;
				vel.y = v.y * maxGroundSpeed;
			}
		} else {
			if (vel.x > maxHorizontalAirSpeed) {
				vel.x = maxHorizontalAirSpeed;
			}
			if (vel.x < -maxHorizontalAirSpeed) {
				vel.x = -maxHorizontalAirSpeed;
			}
		}
		
		// Fix newPos
		Vec2 newPos = new Vec2(	pos.x + (vel.x * dt / 1000000), 
								pos.y + (vel.y * dt / 1000000) );
		if (newPos.x > terrainLevel.getWidth() - 1)
			newPos.x = terrainLevel.getWidth() - 1;
		if (newPos.x < 0)
			newPos.x = 0;
		if (newPos.y > terrainLevel.getHeight() - 1) {
			newPos.y = terrainLevel.getHeight() - 1;
			kill();
		}
		if (newPos.y < 0)
			newPos.y = 0;
		
		if (onGround) {
			if (movementDebug)
				System.out.println("--- OnGround");
			// Try walking
			int val = groundPoint((int) newPos.x, 
					(int) newPos.y + vertMaxStep, 
					(int) newPos.y - vertMaxStep);
			if (val == -2) {
				// Cant go uphill
				//onGround = true;
				vel.x = 0;
				vel.y = 0;
				if (movementDebug)
					System.out.println("onGround - Can't go Uphill.");
			} else if (val == -1) {
				// Falling
				onGround = false;
				pos.x = newPos.x;
				pos.y = newPos.y;
				if (movementDebug)
					System.out.println("onGround - Falling.");
			} else {
				// Walking
				pos.x = newPos.x;
				pos.y = newPos.y = val;
				//onGround = true;
				if (movementDebug)
					System.out.println("onGround - Walking.");
			}
			
			/* FRICTION */
			vel.x = vel.x * friction;
			vel.y = vel.y * friction;
			
		} else {
			
			if (movementDebug)
				System.out.println("--- NOT OnGround");
			
//			int c = background.getRGB((int) newPos.x, (int) newPos.y);
//			int red   = (c & 0x00ff0000) >> 16;
//			int green = (c & 0x0000ff00) >> 8;
//			int blue  =  c & 0x000000ff;
			
			if (terrainLevel.isFree((int) newPos.x, (int) newPos.y)) {
				pos.x = newPos.x;
				pos.y = newPos.y;
			} else {
				
				if (!dead)
					if (!direction)
						currentSprite = this.sprites[0];
					else
						currentSprite = this.sprites[1];
				
				// Collide with ground from air
				
				int val = groundPoint((int) newPos.x, 
						(int) newPos.y + vertMaxStep, 
						(int) newPos.y - vertMaxStep);
				if (vel.y > 0) {
					if (val == -2) {
						vel.x = -vel.x;
//						vel.y = -vel.y;
						vel.x = vel.x/2;
						vel.y = vel.y/2;
//						pos.x = prevPos.x;
//						pos.y = prevPos.y;
						SoundManager.playSound("sounds/hitground.wav");
						if (movementDebug)
							System.out.println("Collided too far into ground");
					} else if (val == -1) {
						// Falling
						if (movementDebug)
							System.out.println("Wat Down");
					} else {
						if (vel.length() < initGroundVel) {
							// Walking
							newPos.y = val;
							pos.x = newPos.x;
							pos.y = newPos.y;
							onGround = true;
							vel.x = 0;
							vel.y = 0;
							if (movementDebug)
								System.out.println("Going to ground");
						} else {
							// Bounce on ground
							vel.y = -vel.y * 0.5;
							vel.x = vel.x * 0.5;
							SoundManager.playSound("sounds/hitground.wav");
							if (movementDebug)
								System.out.println("Bounce");
						}
					}
				} else {
					// Bounce on roof
					vel.y = -vel.y * 0.5;
//					vel.x = -vel.x * 0.5;
					SoundManager.playSound("sounds/hitground.wav");
					
					lastControl = CrustyEngine.currentTimeMillis();
					canControl = false;
					
					if (movementDebug)
						System.out.println("Bounce on Roof.");
				}
			}
		}
		
		// Record last black position
		if (terrainLevel.isFree((int) pos.x, (int) pos.y)) {
			prevPos.x = pos.x; // + 1
			prevPos.y = pos.y;
		}
		
	}
	
	public boolean isDead() {
		return dead;
	}
	
	public void kill() {
//		System.out.println("Trigger Death");
		if (!dead) {
			lastDied = CrustyEngine.currentTimeMillis();
			dead = true;
//			vel.x = 0;
//			vel.y = 0;
			terrainLevel.createHole(pos.x, pos.y, 40, this);
			canControl = false;
			gun.canShoot = false;
			
			if (direction)
				currentSprite = this.sprites[3];
			else
				currentSprite = this.sprites[2];
			
			terrainLevel.drawBloodSplat(pos.x, pos.y);
			
			SoundManager.playSound("sounds/die.wav");
			
			CrustyEngine.getParticleManager().createBloodBurst(40, SpriteManager.getSprite("img/particles/blood.bmp"), pos);
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		
	}
	
}
