package de.samdev.colorrunner.game.world.entities;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;

import de.samdev.colorrunner.game.world.CRGameWorld;

public abstract class MovingEntity extends CRGameEntity {
	protected Vector2 velocity = new Vector2();

	private boolean face_TOP_isTouching = false;
	private boolean face_LEFT_isTouching = false;
	private boolean face_BOTTOM_isTouching = false;
	private boolean face_RIGHT_isTouching = false;
	
	public MovingEntity(CRGameWorld _owner, float width, float height) {
		this(_owner, 0, 0, width, height);
	}
	
	public MovingEntity(CRGameWorld _owner, float x, float y, float width, float height) {
		super(_owner, x, y, width, height);
	}

	protected boolean moveByX(float bx) {
		if (bx == 0) return false;
		
		boolean collided = false;
		
		Rectangle original = new Rectangle(bounds);
		
		bounds.x += bx;
		
		for (CRGameEntity ent : world.entities) {
			if (ent == this) continue;
			
			if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this)) {
				
				if (ent.bounds.overlaps(original)) {
					Gdx.app.log("Collision", "Ignore in bounds collision");
					continue;
				}
				
				if (bx < 0) { // LEFT
					float correction = (ent.bounds.x + ent.bounds.width) - bounds.x;
					
					bounds.x += correction;
					bx += correction;
					
					collided = true;
				} else { // RIGHT
					float correction = ent.bounds.x - (bounds.x + bounds.width);
					
					bounds.x += correction;
					bx += correction;
					
					collided = true;
				}
			}
		}
		
		return collided;
	}
	
	protected boolean moveByY(float by) {
		if (by == 0) return false;
		
		boolean collided = false;

		Rectangle original = new Rectangle(bounds);
		
		bounds.y += by;
		
		for (CRGameEntity ent : world.entities) {
			if (ent == this) continue;
			
			if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this)) {
				
				if (ent.bounds.overlaps(original)) {
					Gdx.app.log("Collision", "Ignore in bounds collision");
					continue;
				}
				
				if (by < 0) { // DOWN
					float correction = (ent.bounds.y + ent.bounds.height) - bounds.y;
					
					bounds.y += correction;
					by += correction;
					
					collided = true;
				} else { // UP
					float correction = ent.bounds.y - (bounds.y + bounds.height);
					
					bounds.y += correction;
					by += correction;
					
					collided = true;
				}
			}
		}
		
		return collided;
	}
	
	protected boolean updateHitBox(float width, float height) {
		boolean succ_x = updateHitBoxWidth(width);
		boolean succ_y = updateHitBoxHeight(height);
		
		return succ_x && succ_y;
	}
	
	private boolean updateHitBoxWidth(float width) {
		Rectangle original = new Rectangle(bounds);
		
		float add = width - bounds.width;
		
		if (isTouching_LEFT() && ! isTouching_RIGHT()) {
			bounds.width += add;
			bounds.x -= add;
		} else if (! isTouching_LEFT() && isTouching_RIGHT()) {
			bounds.width += add;
		} else {
			bounds.width += add;
			bounds.x -= add/2;
		}
		
		if (add < 0) {
			return true;
		} else {
			for (CRGameEntity ent : world.entities) {
				if (ent == this) continue;
				
				if (ent.bounds.overlaps(bounds) && ent.canCollide(false, this)) {

					if (ent.bounds.overlaps(original)) {
						Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand");
						continue;
					}
					
					
					if (ent.bounds.x > bounds.x) {
						bounds.x -= (bounds.x + bounds.width) - ent.bounds.x;
						
						for (CRGameEntity ent2 : world.entities) {
							if (ent2 == this) continue;
							
							if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
								bounds = original;
								
								Gdx.app.log("Entity Hitbox", "Expand failed X+");
								return false;
							}
						}
					} else if (ent.bounds.x < bounds.x) {
						bounds.x += (ent.bounds.x + ent.bounds.width) - bounds.x;
						
						for (CRGameEntity ent2 : world.entities) {
							if (ent2 == this) continue;
							
							if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
								bounds = original;

								Gdx.app.log("Entity Hitbox", "Expand failed X-");
								return false;
							}
						}
					}
				}
			}

			return true;
		}
	}
	
	private boolean updateHitBoxHeight(float height) {
		Rectangle original = new Rectangle(bounds);
		
		float add = height - bounds.height;

		if (isTouching_BOTTOM() && ! isTouching_TOP()) {
			bounds.height += add;
		} else if (! isTouching_BOTTOM() && isTouching_TOP()) {
			bounds.height += add;
			bounds.y -= add;
		} else {
			bounds.height += add;
			bounds.y -= add/2;
		}
		
		if (add < 0) {
			return true;
		} else {
			for (CRGameEntity ent : world.entities) {
				if (ent == this) continue;
				
				if (ent.bounds.overlaps(bounds) && ent.canCollide(false, this)) {

					if (ent.bounds.overlaps(original)) {
						Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand");
						continue;
					}
					
					
					if (ent.bounds.y > bounds.y) {
						bounds.y -= (bounds.y + bounds.height) - ent.bounds.y;
						
						for (CRGameEntity ent2 : world.entities) {
							if (ent2 == this) continue;
							
							if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
								bounds = original;
								
								Gdx.app.log("Entity Hitbox", "Expand failed Y+");
								return false;
							}
						}
					} else if (ent.bounds.y < bounds.y) {
						bounds.y += (ent.bounds.y + ent.bounds.height) - bounds.y;
						
						for (CRGameEntity ent2 : world.entities) {
							if (ent2 == this) continue;
							
							if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
								bounds = original;

								Gdx.app.log("Entity Hitbox", "Expand failed Y-");
								return false;
							}
						}
					}
				}
			}

			return true;
		}
	}
	
	private void updateTouchCollisions() {
		face_TOP_isTouching = false;
		face_LEFT_isTouching = false;
		face_BOTTOM_isTouching = false;
		face_RIGHT_isTouching = false;
		
		for (CRGameEntity ent : world.entities) {
			if (ent == this) continue;
			
			if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
				face_BOTTOM_isTouching = true;
			}
			
			if (Math.abs((bounds.y + bounds.height) - ent.bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
				face_TOP_isTouching = true;
			}
			
			if (Math.abs((bounds.x + bounds.width) - ent.bounds.x) < F_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
				face_RIGHT_isTouching = true;
			}

			if (Math.abs((ent.bounds.x + ent.bounds.width) - bounds.x) < F_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
				face_LEFT_isTouching = true;
			}
		}
		
	}
	
	public Vector2 getVelocity() {
		return velocity;
	}
	
	@Override
	public void update(float delta) {
		if (velocity.x != 0) {
			if (moveByX(velocity.x * delta)) {
				velocity.x = 0;
			}
		}
		
		if (velocity.y != 0) {
			if (moveByY(velocity.y * delta)) {
				velocity.y = 0;
			}
		}
		
		updateTouchCollisions();
	}

	public boolean isTouching_TOP() {
		return face_TOP_isTouching;
	}

	public boolean isTouching_LEFT() {
		return face_LEFT_isTouching;
	}

	public boolean isTouching_BOTTOM() {
		return face_BOTTOM_isTouching;
	}

	public boolean isTouching_RIGHT() {
		return face_RIGHT_isTouching;
	}
}
