package pl.shockah.ts.entities;

import java.util.List;
import org.lwjgl.input.Keyboard;
import pl.shockah.Math2;
import pl.shockah.Util;
import pl.shockah.glib.geom.Rectangle;
import pl.shockah.glib.geom.vector.Vector2d;
import pl.shockah.glib.gl.BlendMode;
import pl.shockah.glib.gl.Graphics;
import pl.shockah.glib.gl.color.Color;
import pl.shockah.glib.input.KInput;
import pl.shockah.glib.logic.standard.Entities;
import pl.shockah.glib.logic.standard.EntityBase;
import pl.shockah.glib.logic.standard.EntityCollidable;
import pl.shockah.glib.logic.standard.Renderable;
import pl.shockah.glib.particle.ParticleSystem;
import pl.shockah.glib.state.State;
import pl.shockah.glib.state.transitions.TransitionThroughColor;
import pl.shockah.ts.Assets;
import pl.shockah.ts.Main;
import pl.shockah.ts.entities.effects.EffectJumpRipple;
import pl.shockah.ts.entities.waterblur.IWaterBlur;
import pl.shockah.ts.entities.waterblur.WaterBlur;
import pl.shockah.ts.gameplay.ETime;
import pl.shockah.ts.gameplay.Game;
import pl.shockah.ts.gameplay.TestLevel;
import pl.shockah.ts.particles.PtLand;
import pl.shockah.ts.particles.PtSkidJump;
import pl.shockah.ts.states.StateGame;

public class EntityPlayer extends EntityCollidable implements IGravity,IWaterBlur {
	public Vector2d toMove = new Vector2d(), vel = new Vector2d();
	public Jumps jumps = new Jumps();
	protected int frame = 0;
	protected boolean doingSkid = false, doSlide = false, didPush = false;
	protected ParticleSystem ps = new ParticleSystem(), ps2 = new ParticleSystem(BlendMode.Normal);
	
	public EntityPlayer() {
		baseDepth = 5;
	}
	
	protected void onCreate() {
		shape = new Rectangle(pos.x,pos.y,24,24);
	}
	
	protected double getXAcceleration() {
		return .5d;
	}
	protected double getXDeceleration() {
		return .25d;
	}
	protected double getYAcceleration(boolean againstWall, boolean water) {
		return (againstWall ? .125d : .5d)*(water ? 2f/3f : 1f);
	}
	
	protected double getXVelocityLimit() {
		return 4d;
	}
	protected double getYVelocityLimit(boolean againstWall) {
		return againstWall ? 3d : 12d;
	}
	
	protected double getWallJumpXVelocity() {
		return 8d;
	}
	protected double getJumpYVelocity(boolean skid, boolean wall) {
		if (wall) return 6d;
		if (skid) return 11d;
		return 8.5d;
	}
	
	protected void onUpdate() {
		if (KInput.isPressed(Keyboard.KEY_1)) State.change(new StateGame(TestLevel.level1),new TransitionThroughColor(Color.Black,.025f),new TransitionThroughColor(Color.Black,.025f,true));
		if (KInput.isPressed(Keyboard.KEY_2)) State.change(new StateGame(TestLevel.level2),new TransitionThroughColor(Color.Black,.025f),new TransitionThroughColor(Color.Black,.025f,true));
		if (KInput.isPressed(Keyboard.KEY_3)) State.change(new StateGame(TestLevel.level3),new TransitionThroughColor(Color.Black,.025f),new TransitionThroughColor(Color.Black,.025f,true));
		if (KInput.isPressed(Keyboard.KEY_4)) State.change(new StateGame(TestLevel.level4),new TransitionThroughColor(Color.Black,.025f),new TransitionThroughColor(Color.Black,.025f,true));
		
		if (KInput.isPressed(Keyboard.KEY_Z)) Game.me.time = ETime.Stop;
		if (KInput.isPressed(Keyboard.KEY_X)) Game.me.time = ETime.Slow;
		if (KInput.isPressed(Keyboard.KEY_C)) Game.me.time = ETime.Normal;
		
		List<EntityCollidable> platforms = Util.getAllOf(getPlatforms(),EntityCollidable.class);
		didPush = false;
		
		boolean collidesU = collidesAt(platforms,pos.x,pos.y-1);
		boolean collidesD = collidesAt(platforms,pos.x,pos.y+1);
		boolean inWater = WaterBlur.isInWater(this);
		
		boolean l = KInput.isDown(Keyboard.KEY_LEFT), r = KInput.isDown(Keyboard.KEY_RIGHT);
		int dir = (r?1:0)-(l?1:0);
		int sign = (int)Math.signum(vel.x);
		if (dir == 0 || (dir == sign && Math.abs(vel.x) >= getXVelocityLimit())) {
			vel.x -= getXDeceleration()*sign;
			if (dir != 0) doSlide = true;
			int sign2 = (int)Math.signum(vel.x);
			if (sign != sign2) vel.x = 0;
		} else {
			if (Math.abs(vel.x) >= getXVelocityLimit()) frame += sign*4;
			if (sign != dir && sign != 0) vel.x += getXDeceleration()*dir;
			vel.x += getXAcceleration()*dir;
			int sign2 = (int)Math.signum(vel.x);
			if (sign2 == dir && Math.abs(vel.x) > getXVelocityLimit()) vel.x = getXVelocityLimit()*sign2;
		}
		if (dir != 0 && sign != 0 && dir != sign && Math.abs(vel.x) >= 0 && doSlide && collidesD) {
			for (int i = 0; i < getXVelocityLimit(); i++) PtLand.make(ps2,pos.Add(12,24),vel.x > 0);
			doSlide = false;
		}
		
		if (KInput.isPressed(Keyboard.KEY_UP) && !collidesU) {
			if (dir != 0 && !collidesD && !inWater && collidesAt(platforms,pos.x+dir,pos.y)) {
				vel.y = -getJumpYVelocity(false,true);
				vel.x = -getWallJumpXVelocity()*dir;
				new EffectJumpRipple(EffectJumpRipple.EOrientation.Vertical).create(pos.Add(12+dir*12,12));
				//TODO count jumps
				//TODO count wall jumps
			} else if (jumps.canMake() || inWater) {
				//TODO count jumps
				double jumpPower = getJumpYVelocity(false,false);
				boolean skid = false;
				doingSkid = false;
				if (jumps.firstJump()) {
					if (dir == -sign && vel.x != 0 && !inWater) {
						jumpPower = getJumpYVelocity(true,false);
						skid = true;
						doingSkid = true;
						//TODO count skid jumps
					}
				} else {
					new EffectJumpRipple(EffectJumpRipple.EOrientation.Horizontal).create(pos.Add(12,24));
					//TODO count double jumps
				}
				jumps.make();
				vel.y = -jumpPower;
				if (dir != 0 && dir != sign) vel.x = skid ? 0 : -vel.x;
			}
		} else {
			if (!collidesD) {
				jumps.fall();
				boolean againstWall = dir != 0 && collidesAt(platforms,pos.x+dir,pos.y) && vel.y >= 0f && !inWater;
				double fallSpeed = getYAcceleration(againstWall,inWater), limit = getYVelocityLimit(againstWall);
				if (vel.y < limit) vel.y += fallSpeed;
				if (vel.y > limit) {
					vel.y -= fallSpeed;
					if (vel.y < limit) vel.y = limit;
				}
				if (againstWall && vel.y >= limit && Main.rand.nextInt(3) == 0) PtLand.make(ps2,pos.Add(12+dir*12,0),90d+dir*15d);
			} else jumps.reset();
		}
		
		if (vel.y >= 0) doingSkid = false;
		toMove.add(new Vector2d(inWater ? vel.x/4d : vel.x,inWater ? vel.y/4d : vel.y));
		while (true) {
			boolean h = false, v = false;
			
			if (toMove.x >= .5d || toMove.x < -.5d) {
				h = true;
				int sign2 = (int)Math.signum(toMove.x);
				
				List<EntityCollidable> coll = collidesAtWith(platforms,pos.x+sign2,pos.y);
				if (coll.isEmpty()) {
					pos.x += sign2;
					toMove.x -= sign2;
				} else if (coll.size() == 1 && coll.get(0) instanceof EntityBoxPushable && collidesD) {
					EntityCollidable ec = coll.get(0);
					if (!ec.collidesAt(platforms,ec.pos.x+sign2,ec.pos.y)) {
						ec.pos.x += sign2;
						pos.x += sign2;
						toMove.x -= 1d*sign2;
						vel.x /= 2d;
						
						if (!didPush && !inWater && Main.rand.nextInt(3) == 0) {
							PtLand.make(ps2,pos.Add(12,24),vel.x < 0);
							didPush = true;
						}
					} else {
						toMove.x = 0;
						vel.x = 0;
					}
				} else {
					toMove.x = 0;
					vel.x = 0;
				}
			}
			if (toMove.y >= .5d || toMove.y < -.5d) {
				v = true;
				int sign2 = (int)Math.signum(toMove.y);
				if (!collidesAt(platforms,pos.x,pos.y+sign2)) {
					pos.y += sign2;
					toMove.y -= sign2;
				} else {
					if (vel.y > 0 && !inWater && (dir == 0 || !collidesAt(platforms,pos.x+dir,pos.y))) for (int i = 0; i < vel.y; i++) PtLand.make(ps2,pos.Add(12,24));
					
					toMove.y = 0;
					vel.y = 0;
				}
			}
			
			if (!h && !v) break;
		}
		
		if (Math.signum(frame) != dir && frame != 0 && dir != 0) frame += dir;
		frame += dir;
		if (!(l ^ r) && frame != 0) frame -= (int)Math.signum(frame);
		frame = Math2.limit(frame,8);
		
		if (doingSkid) PtSkidJump.make(ps,pos.Add(12,12));
		ps.update();
		ps2.update();
	}
	protected List<EntityBase> getPlatforms() {
		return Entities.getTypes(EntityWall.class,EntityBox.class);
	}
	
	protected void onRegister() {
		new Renderable(this,5d-.1d){
			protected void onRender(Graphics g) {
				if (!WaterBlur.blur) {
					ps.render(g);
					ps2.render(g);
				}
			}
		}.create();
		
		new Renderable(this,5d-.2d){
			protected void onRender(Graphics g) {
				g.setColor(Color.White.alpha(WaterBlur.blurAlpha));
				g.draw(Assets.sPlayerBase,pos.x,pos.y);
			}
		}.create();
		
		new Renderable(this,5d-.3d){
			protected void onRender(Graphics g) {
				g.setColor(Color.White.alpha(WaterBlur.blurAlpha));
				g.pushTransformedClip(pos.x,pos.y,24,24);
				g.draw(Assets.sPlayerEye,pos.x+6+frame*(frame > 0 ? 1 : 2),pos.y+6);
				g.draw(Assets.sPlayerEye,pos.x+15+frame*(frame > 0 ? 2 : 1),pos.y+6);
				g.popTransformedClip();
			}
		}.create();
		
		new Renderable(this,5d-.4d){
			protected void onRender(Graphics g) {
				g.setColor(Color.White.alpha(WaterBlur.blurAlpha));
				g.draw(Assets.ssTileBorderThin.getImage(0),pos.x,pos.y);
			}
		}.create();
	}
	
	public class Jumps {
		public int max = 2, available = max;
		
		public void reset() {
			available = max;
		}
		public boolean make() {
			if (canMake()) {
				available--;
				return true;
			}
			return false;
		}
		public boolean canMake() {
			return available > 0;
		}
		public boolean firstJump() {
			return available == max;
		}
		public void fall() {
			if (available == max) available--;
		}
	}
}