public class Player extends Sprite{
	public static final float DEF_RUN_SPEED = 4;
	public static final float DEF_WALK_SPEED = 2;
	public static final float DEF_HORIZ_ACC = 0.2f;
	public static final float DEF_HORIZ_AIR_ACC = 0.1f;
	public static final float DEF_GRAV = 0.2f;
	public static final float DEF_FALL_SPEED = 4;
	public static final int DEF_MAX_JUMP = 32;
	public static final float DEF_JUMP_SPEED = -3;
	public static final int DEF_MAX_FIRE_TIME = 40;
	public static final int ANIM_SPEED_RUN = 8;
	public static final int ANIM_SPEED_WALK = 12;
	
	private Level l;
	private static final int[] animLengths = {1,1,2,2};
	protected float vx, vy;
	protected float runSpeed, walkSpeed;
	protected float hAcc, hAirAcc;
	protected float maxvy;
	protected float grav, jumpv;
	protected int maxJump, jumpCounter;
	protected int maxFireTime, fireCounter;
	protected int fireEnable;
	private boolean midair;
	private boolean facingRight;
	private boolean bounce;
	private byte keyState;
	private float spawnX, spawnY;
	
	public Player(Level l) {
		super(l.spriteCache, "img/hobo/hobo", animLengths);
		x=y=spawnX=spawnY=32;
		this.l=l;
		init();
	}
	public Player(Level l, float x, float y) {
		this(l);
		this.x=spawnX=x;
		this.y=spawnY=y;
	}
	private void init() {
		w=1;
		h=1;
		vx=vy=0;
		maxvy = DEF_FALL_SPEED;
		runSpeed = DEF_RUN_SPEED;
		walkSpeed = DEF_WALK_SPEED;
		hAcc = DEF_HORIZ_ACC;
		hAirAcc = DEF_HORIZ_AIR_ACC;
		grav = DEF_GRAV;
		jumpv = DEF_JUMP_SPEED;
		maxJump = DEF_MAX_JUMP;
		maxFireTime = DEF_MAX_FIRE_TIME;
		fireEnable = 0;
		midair = true;
		keyState=0;
		facingRight = true;
		setSpriteSpeed(ANIM_SPEED_WALK);
	}
	public boolean isFacingRight() {
		return facingRight;
	}
	
	public void bounce() {
		bounce = true;
	}
	
	public void setKeys(byte keyState) {
		this.keyState = keyState;
	}
	
	public void act() {
		if (bounce) {
			bounce = false;
			vy *= -1;
		}
		
		x+=vx;
		y+=vy;
		
		if (!l.onMap(this)) {
			respawn();
			return;
		}
		
		byte hitState = l.hitDetect((int)x, (int)y, w*32, h*32, vx, vy);
		if ((hitState & Level.HIT_RIGHT) != 0) {
			x-=x%32;
			if (vx > 0) vx=0;
		}
		if ((hitState & Level.HIT_LEFT) != 0) {
			x-=x%32;
			x+=32;
			if (vx < 0) vx=0;
		}
		if ((hitState & Level.HIT_TOP) != 0) {
			jumpCounter = 0;
			y-=y%32;
			y+=32;
			if (vy < 0) vy=0;
		}
		if ((hitState & Level.HIT_BOTTOM) != 0) {
			midair=false;
			y-=y%32;
			if (vy > 0) vy=0;
		} else {
			vy += grav;
			midair=true;
		}
		animate = !midair;
		if ((keyState & KEY_JUMP) != 0) {
			if (!midair) {
				midair = true;
				jumpCounter = maxJump;
			}
			if (jumpCounter > 0) {
				vy = jumpv;
				jumpCounter--;
			}
		} else {
			jumpCounter = 0;
		}
		if ((keyState & KEY_LEFT) != 0) {
			facingRight = false;
			if (!midair) {
				vx -= hAcc;
			} else if (vx > -1* walkSpeed) {
				vx -= hAirAcc;
			}
			if ((keyState & KEY_RUN) != 0) {
				setSpriteSpeed(ANIM_SPEED_RUN);
				if (vx < -1*runSpeed)
					vx = -1*runSpeed;
			} else {
				setSpriteSpeed(ANIM_SPEED_WALK);
				if (vx < -1*walkSpeed)
					vx = -1*walkSpeed;
			}
		} else if ((keyState & KEY_RIGHT) != 0) {
			facingRight = true;
			if (!midair) {
				vx += hAcc;
			} else if (vx < walkSpeed) {
				vx += hAirAcc;
			}
			if ((keyState & KEY_RUN) != 0) {
				setSpriteSpeed(ANIM_SPEED_RUN);
				if (vx > runSpeed)
					vx = runSpeed;
			} else {
				setSpriteSpeed(ANIM_SPEED_WALK);
				if (vx > walkSpeed)
					vx = walkSpeed;
			}
		} else {
			vx -= hAcc*Math.signum(vx);
			if (Math.abs(vx) < .1)
				vx = 0;
		}
		if (fireEnable>0) {
			if ((keyState & KEY_FIRE) != 0) {
				if (fireCounter <= 0) {
					if (fireEnable==1) {
						l.projectiles.add(new Projectile(l, x, y, facingRight ? 5 : -5));
					} else {
						l.projectiles.add(new Projectile(l, x, y-7, facingRight ? 5 : -5));
						l.projectiles.add(new Projectile(l, x, y+7, facingRight ? 5 : -5));
					}
					fireCounter = maxFireTime;
				}
			}
			fireCounter--;
		}		
		if (vx == 0)
			setSpriteMode(facingRight ? 0 : 1);
		else
			setSpriteMode(2 + (facingRight ? 0 : 1));
		
		if (Math.abs(vy) > maxvy)
			vy = maxvy*Math.signum(vy);
	}
	public void respawn() {
		l.gamestate.addLives(-1);
		l.gamestate.addPoints(-20);
		x=spawnX;
		y=spawnY;
		init();
	}
	public void setSpawn(float x, float y) {
		spawnX = x;
		spawnY = y;
	}
	public float getSpawnX() {
		return spawnX;
	}
	public float getSpawnY() {
		return spawnY;
	}
}
