package entities;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Animation.PlayMode;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.ld50.game.HF;

import Global.Global;
import levelGen.Level;

public class Furbal extends Sprite {
	public enum State {
		STANDLEFT, STANDRIGHT, STANDUP, STANDDOWN, WALKLEFT, WALKRIGHT, WALKUP, WALKDOWN, JUMPLEFT, JUMPRIGHT, JUMPUP, JUMPDOWN, PRESSEDLEFT, PRESSEDRIGHT,
		PRESSEDUP, PRESSEDDOWN, DYING
	}

	private final int SEEKTIME = MathUtils.random(70, 250);
	private final int REALSEEKTIME = MathUtils.random(270, 450);
	private final int CANHITTIMERMAX = MathUtils.random(500, 800);
	private final int REFRESHTIMERMAX = MathUtils.random(200, 300);

	public State currentState;
	public State previousState;
	private Animation<TextureRegion> walkRight;
	private Animation<TextureRegion> walkLeft;
	private Animation<TextureRegion> walkUp;
	private Animation<TextureRegion> walkDown;
	private Animation<TextureRegion> jumpRight;
	private Animation<TextureRegion> jumpLeft;
	private Animation<TextureRegion> jumpUp;
	private Animation<TextureRegion> jumpDown;
	private TextureRegion standRight;
	private TextureRegion standLeft;
	private TextureRegion standUp;
	private TextureRegion standDown;
	private TextureRegion pressedUp;
	private TextureRegion pressedDown;
	private TextureRegion pressedRight;
	private TextureRegion pressedLeft;
	private TextureRegion dying;
	private TextureAtlas atlas;
	public Color color;
	public Rectangle rect;
	public boolean isMain = true;// start with main
	public boolean isDead = true;// move off screen.
	public int _isDeadTimer;
	private int _sw;// sprite width
	private int _sh;// sprite height
	private float speed;
	public Vector2 sp;
	public Vector2 ep;
	private Vector2 dir;
	public boolean isWait = true;
	public boolean getDir;
	public boolean isCollidingWithGlass;
	public boolean isCollidingWithOffice;
	private boolean seeking;
	private float _stateTimer;
	private boolean isAnimationOver;
	public boolean isDying;
	private boolean l;
	private boolean r;
	private boolean u;
	private boolean d;
	private boolean dmove;
	private boolean umove;
	private boolean lmove;
	private boolean rmove;
	private boolean rjump;
	private boolean ljump;
	private boolean ujump;
	private boolean djump;
	private boolean rpressed;
	private boolean lpressed;
	private boolean dpressed;
	private boolean upressed;
	private boolean dirUp;
	private boolean dirRight;
	private boolean dirLeft;
	private boolean dirDown;
	private float aSpd;

	private int seekTimer;
	private int getRealDirectionTimer;
	private boolean getRealDir;
	private boolean realSeeking;
	public boolean canHit = true;
	public int canHitTimer = CANHITTIMERMAX;
	public boolean refreshed = true;
	public int refreshTimer = REFRESHTIMERMAX;
	private int fallUpTimer;
	private Sound death;// implement later
	private boolean one;
	private String initDir;
	private boolean isPeeringThroughGlass;
	private int randPeer = MathUtils.random(0, 3);
	public int isDyingTimer = 100;
	private boolean playOnce;

	public Furbal(int _x, int _y, String _dir) {
		rect = new Rectangle();
		rect.x = _x;
		rect.y = _y;
		rect.width = 50;
		rect.height = 50;

		_sw = 100;
		_sh = 100;
		// this is for the animations
		currentState = State.STANDRIGHT;
		previousState = State.STANDRIGHT;
		_stateTimer = 0f;
		// declare all the static frames (static states (no animation))
		death = Gdx.audio.newSound(Gdx.files.internal("sfx/bongdong.ogg"));
		// region hello
		speed = MathUtils.random(50f, 200f);// for now
		if (speed <= 50f)
			aSpd = 1f;
		else if (speed < 60f)
			aSpd = .95f;
		else if (speed < 70f)
			aSpd = .9f;
		else if (speed < 80f)
			aSpd = .85f;
		else if (speed < 90f)
			aSpd = .8f;
		else if (speed < 100f)
			aSpd = .75f;
		else if (speed < 110f)
			aSpd = .7f;
		else if (speed < 120f)
			aSpd = .65f;
		else if (speed < 130f)
			aSpd = .6f;
		else if (speed < 140f)
			aSpd = .55f;
		else if (speed < 150f)
			aSpd = .5f;
		else if (speed < 160f)
			aSpd = .45f;
		else if (speed < 170f)
			aSpd = .4f;
		else if (speed < 180f)
			aSpd = .35f;
		else if (speed < 190f)
			aSpd = .3f;
		else if (speed <= 200f)
			aSpd = .25f;
		else if (speed <= 250f)
			aSpd = .2f;
		else if (speed <= 300f)
			aSpd = .15f;
		else if (speed <= 350f)
			aSpd = .1f;
		else if (speed <= 400f)
			aSpd = .05f;
		else if (speed <= 450f)
			aSpd = .01f;
		// endregion
		// ok when a growl is created i need it to go from where it is at the mouse, is that possible?
		atlas = new TextureAtlas(Gdx.files.internal("furbal.txt"));
		standLeft = new TextureRegion(atlas.findRegion("furbal"), 0, 0, _sw, _sh);
		standRight = new TextureRegion(atlas.findRegion("furbal"), 0, 0, _sw, _sh);
		standRight.flip(true, false);
		standUp = new TextureRegion(atlas.findRegion("furbal"), 4 * _sw, 0, _sw, _sh);
		standDown = new TextureRegion(atlas.findRegion("furbal"), 0, 0, _sw, _sh);
		pressedDown = new TextureRegion(atlas.findRegion("furbal"), 8 * _sw, 0, _sw, _sh);
		pressedLeft = new TextureRegion(atlas.findRegion("furbal"), 10 * _sw, 0, _sw, _sh);
		pressedRight = new TextureRegion(atlas.findRegion("furbal"), 10 * _sw, 0, _sw, _sh);
		pressedRight.flip(true, false);
		pressedUp = new TextureRegion(atlas.findRegion("furbal"), 9 * _sw, 0, _sw, _sh);
		dying = new TextureRegion(atlas.findRegion("furbal"), 11 * _sw, 0, _sw, _sh);
		// animated frames
		Array<TextureRegion> frames = new Array<TextureRegion>();
		for (int i = 0; i <= 1; i++) {
			frames.add(new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh));
		}
		walkLeft = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		walkDown = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();
		for (int i = 0; i <= 1; i++) {
			TextureRegion r = new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh);
			r.flip(true, false);
			frames.add(r);
		}
		walkRight = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();
		for (int i = 4; i <= 5; i++) {
			frames.add(new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh));
		}
		walkUp = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();
		for (int i = 0; i <= 3; i++) {
			if (i != 1)// skip this frame
				frames.add(new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh));
		}
		jumpLeft = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		jumpDown = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();
		for (int i = 0; i <= 3; i++) {
			if (i != 1) {// skip this frame
				TextureRegion r = new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh);
				r.flip(true, false);
				frames.add(r);
			}
		}
		jumpRight = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();
		for (int i = 4; i <= 7; i++) {
			if (i != 5)// skip this frame
				frames.add(new TextureRegion(atlas.findRegion("furbal"), i * _sw, 0, _sw, _sh));
		}
		jumpUp = new Animation<TextureRegion>(aSpd, frames, PlayMode.LOOP);
		frames.clear();

		// assign random color
		color = new Color(new Color(MathUtils.random(.1f, .2f), MathUtils.random(.1f, .2f), MathUtils.random(.6f, 1f), 1));
		setBounds(0, 0, _sw, _sh);
		setOriginCenter();
		setRegion(walkRight.getKeyFrame(_stateTimer, true));
		initDir = _dir;
		// SpawnFurbal(_x, _y, _dir);
	}

	public Vector2 GetDirection(Vector2 sp, Vector2 ep) {
		rect.x = sp.x;
		rect.y = sp.y;
		return ep.sub(sp).nor();
	}

	public void Update(float dt) {
		if (isDying && isDyingTimer > 0) {
			if (!Global.SetVolumeToZero && !playOnce) {
				playOnce = true;
				death.play(.5f);
			}
			isDyingTimer--;
		} else {
			if (isDying && isDyingTimer <= 0)
				isDead = true;
			if (isDead) {
				rect.x = 9000;
				rect.y = 9000;
			} else if (seeking) {
				if (realSeeking) {
					rect.x += dir.x * speed * dt;
					rect.y += dir.y * speed * dt;
				} else {
					rect.x += dir.x * speed * dt / 2;
					rect.y += dir.y * speed * dt / 2;
				}
			}
			if (getRealDir) {
				getRealDir = false;
				realSeeking = true;
				sp = new Vector2(rect.x, rect.y);
				ep = new Vector2(Global.P.x, Global.P.y);
				dir = GetDirection(sp, ep);
			} else if (getDir) {
				getDir = false;
				sp = new Vector2(rect.x, rect.y);
				ep = new Vector2(Global.P.x + MathUtils.random(-250f, 250f), Global.P.y + MathUtils.random(-250f, 250f));
				dir = GetDirection(sp, ep);
			}
			if (seekTimer <= 0) {
				seekTimer = SEEKTIME;
				getDir = true;
				realSeeking = false;
			}
			if (seekTimer > 0)
				seekTimer--;
			if (getRealDirectionTimer <= 0) {
				getRealDirectionTimer = REALSEEKTIME;
				getRealDir = true;
				getDir = false;
				seekTimer = SEEKTIME;
			}
			if (getRealDirectionTimer > 0)
				getRealDirectionTimer--;
			if (isCollidingWithGlass && canHitTimer > 0) {
				canHitTimer--;
				isPeeringThroughGlass = true;
				// System.out.println("waiting just a bit here, peering through the glass");
			} else
				isPeeringThroughGlass = false;
			if (isCollidingWithGlass && canHitTimer <= 0) {
				refreshTimer--;
				secretMovement(dt, getReverseDir(initDir));
			} else
				handleMovement(dt);
			if (isCollidingWithGlass && refreshTimer <= 0) {
				canHit = true;
				isCollidingWithGlass = false;
				refreshTimer = REFRESHTIMERMAX;
				canHitTimer = MathUtils.random(400, 800);
			}
			// if (isCollidingWithGlass && refreshTimer < REFRESHTIMERMAX)
			// secretMovement(dt, getReverseDir(initDir));
			// else
			// handleMovement(dt);
		}
		getCorrectWalkingAnimation(dt);
		// System.out.println("getDir: " + getDir);
		// System.out.println("SEEKTIME: " + SEEKTIME);
		// System.out.println("seeking: " + seekTimer);
		// System.out.println("REALSEEKTIME: " + REALSEEKTIME);
		// System.out.println("getRealDirectionTimer: " + getRealDirectionTimer);
		// System.out.println("REFRESHTIMERMAX: " + REFRESHTIMERMAX);
		// System.out.println("refreshTimer: " + refreshTimer);
		// System.out.println("CANHITTIMERMAX: " + CANHITTIMERMAX);
		// System.out.println("canHitTimer: " + canHitTimer);
		// System.out.println("canHit: " + canHit);
		// System.out.println("isCollidingWithGlass: " + isCollidingWithGlass);

	}

	private String getReverseDir(String initDir2) {
		if (initDir2 == "right")
			return "left";
		else if (initDir2 == "left")
			return "right";
		else if (initDir2 == "up")
			return "down";
		else
			return "up";
	}

	private void getCorrectWalkingAnimation(float dt) {
		if (!isCollidingWithOffice) {
			rjump = false;
			ljump = false;
			djump = false;
			ujump = false;
			if (dirRight) {
				rjump = true;
			} else if (dirLeft) {
				ljump = true;
			} else if (dirUp) {
				ujump = true;
			} else if (dirDown) {
				djump = true;
			}
		}
		if (isPeeringThroughGlass && !isCollidingWithOffice) {
			rmove = false;
			lmove = false;
			dmove = false;
			umove = false;
			r = false;
			l = false;
			u = false;
			d = false;
			rjump = false;
			ljump = false;
			djump = false;
			ujump = false;
			rpressed = false;
			lpressed = false;
			upressed = false;
			dpressed = false;
			if (randPeer == 0) {
				if (initDir == "right")
					rjump = true;
				else if (initDir == "up")
					ujump = true;
				else if (initDir == "left")
					ljump = true;
				else if (initDir == "down")
					djump = true;
			} else {
				if (initDir == "right")
					rpressed = true;
				else if (initDir == "up")
					upressed = true;
				else if (initDir == "left")
					lpressed = true;
				else if (initDir == "down")
					dpressed = true;
			}
		} else {
			rmove = false;
			lmove = false;
			dmove = false;
			umove = false;
			r = false;
			l = false;
			u = false;
			d = false;
			rjump = false;
			ljump = false;
			djump = false;
			ujump = false;
			rpressed = false;
			lpressed = false;
			upressed = false;
			dpressed = false;
			if (realSeeking && Global.P.x > rect.x) {
				rjump = true;
			} else if (realSeeking && Global.P.x <= rect.x) {
				ljump = true;
			} else if (Global.P.x > rect.x) {
				rmove = true;
			} else if (Global.P.x <= rect.x) {
				lmove = true;
			}
		}
	}

	// for all the animations: //this isn't even updated unless i call it from the other screen oh bother. *facepalm*
	public TextureRegion getFrame(float dt) {
		Update(dt);

		currentState = getState();
		// System.out.println("currentState: " + currentState);
		TextureRegion region;
		switch (currentState) {
		case WALKRIGHT:
			region = walkRight.getKeyFrame(_stateTimer, true);
			break;
		case WALKLEFT:
			region = walkLeft.getKeyFrame(_stateTimer, true);
			break;
		case WALKUP:
			region = walkUp.getKeyFrame(_stateTimer, true);
			break;
		case WALKDOWN:
			region = walkDown.getKeyFrame(_stateTimer, true);
			break;
		case JUMPRIGHT:
			region = jumpRight.getKeyFrame(_stateTimer, true);
			break;
		case JUMPLEFT:
			region = jumpLeft.getKeyFrame(_stateTimer, true);
			break;
		case JUMPUP:
			region = jumpUp.getKeyFrame(_stateTimer, true);
			break;
		case JUMPDOWN:
			region = jumpDown.getKeyFrame(_stateTimer, true);
			break;
		case STANDLEFT:
			region = standLeft;
			break;
		case STANDUP:
			region = standUp;
			break;
		case STANDDOWN:
			region = standDown;
			break;
		case PRESSEDRIGHT:
			region = pressedRight;
			break;
		case PRESSEDLEFT:
			region = pressedLeft;
			break;
		case PRESSEDUP:
			region = pressedUp;
			break;
		case PRESSEDDOWN:
			region = pressedDown;
			break;
		case DYING:
			region = dying;
			break;
		case STANDRIGHT:
		default:
			region = standRight;
			break;
		}
		if (currentState != previousState)
			isAnimationOver = false;

		if (!isAnimationOver && currentState == previousState) {
			_stateTimer += dt;
		} else
			_stateTimer = 0;
		previousState = currentState;

		return region;
	}

	public void SpawnFurbal(float _x, float _y, String directionToTravel) {
		// initial spawn will be outside the level, and then directed right at the building. it will given a direction and a random speed.
		if (directionToTravel == "right")
			dirRight = true;
		if (directionToTravel == "left")
			dirLeft = true;
		if (directionToTravel == "up")
			dirUp = true;
		if (directionToTravel == "down")
			dirDown = true;
		rect.x = _x;
		rect.y = _y;
	}

	public void SeekPlayer() {
		sp = new Vector2(rect.x, rect.y);
		ep = new Vector2(Global.P.x, Global.P.y);
		dir = GetDirection(sp, ep);
		seeking = true;
	}

	private void secretMovement(float dt, String dir) {
		if (dir == "right")
			rect.x += speed * dt;
		if (dir == "left")
			rect.x -= speed * dt;
		if (dir == "up")
			rect.y += speed * dt;
		if (dir == "down")
			rect.y -= speed * dt;
	}

	private void handleMovement(float dt) {
		boolean isCT = false;// collision tile
		// System.out.println("collding with office: " + isCollidingWithOffice);
		if (isCollidingWithOffice && !one) {
			one = true;// this is supposed to only trigger once.
			dirRight = false;
			dirLeft = false;
			dirUp = false;
			dirDown = false;
			realSeeking = true;
			SeekPlayer();
		}
		if (dirRight) {
			// isCT = false;
			isCT = (GetPaneYouWillCrashInto("right", dt)) ? true : false;
			isCollidingWithOffice = (GetTileYouWillBeOn("right", dt)) ? true : false;// right
			MoveRight(speed, isCT, dt);
			l = false;
			r = true;
			u = false;
			d = false;
			rjump = true;
		} else
			rjump = false;
		if (dirLeft) {
			// isCT = false;
			isCT = (GetPaneYouWillCrashInto("left", dt)) ? true : false;
			isCollidingWithOffice = (GetTileYouWillBeOn("left", dt)) ? true : false;// left
			MoveLeft(speed, isCT, dt);
			l = true;
			r = false;
			u = false;
			d = false;
			ljump = true;
		} else
			ljump = false;
		if (dirUp) {
			// isCT = false;
			isCT = (GetPaneYouWillCrashInto("up", dt)) ? true : false;
			isCollidingWithOffice = (GetTileYouWillBeOn("up", dt)) ? true : false;
			MoveUp(speed, isCT, dt);
			u = true;
			d = false;
			l = false;
			r = false;
			ujump = true;
		} else
			ujump = false;
		if (dirDown) {
			// isCT = false;
			isCT = (GetPaneYouWillCrashInto("down", dt)) ? true : false;
			isCollidingWithOffice = (GetTileYouWillBeOn("down", dt)) ? true : false;// down
			MoveDown(speed, isCT, dt);
			u = false;
			d = true;
			l = false;
			r = false;
			djump = true;
		} else
			djump = false;
	}

	public void MoveFalling(float speed, float dt) {
		if (fallUpTimer > 0) {
			rect.y += speed * 2 * dt;
			fallUpTimer--;
			if (!Global.SetVolumeToZero)
				death.play();
		} else
			rect.y -= 2 * speed * dt;
	}

	public boolean GetPaneYouWillCrashInto(String dr, float dt) {
		// test to see if you are running into a glass pane
		// System.out.println("dir: " + dr);
		if (dr == "right") {// this is checked for outside in the glass panes and furball things in the level
			if (isCollidingWithGlass)
				return true;
			else
				return false;
		}
		if (dr == "left") {
			if (isCollidingWithGlass)
				return true;
			else
				return false;
		}
		if (dr == "up") {
			if (isCollidingWithGlass)
				return true;
			else
				return false;
		}
		if (dr == "down") {
			if (isCollidingWithGlass)
				return true;
			else
				return false;
		} else
			return false;
	}

	public void MoveRight(float speed, boolean isColl, float dt) {
		if (isColl) {
			rect.x -= 0;
			rpressed = true;
		} else
			rect.x += speed * dt;
	}

	public void MoveLeft(float speed, boolean isColl, float dt) {
		if (isColl) {
			rect.x += 0;
			lpressed = true;
		} else
			rect.x -= speed * dt;
	}

	public void MoveUp(float speed, boolean isColl, float dt) {
		if (isColl) {
			rect.y -= 0;
			upressed = true;
		} else
			rect.y += speed * dt;
	}

	public void MoveDown(float speed, boolean isColl, float dt) {
		if (isColl) {
			rect.y += 0;
			dpressed = true;
		} else
			rect.y -= speed * dt;
	}

	public boolean GetTileYouWillBeOn(String dr, float dt) {
		// test to see what tile we will be standing on.
		if (dr == "right") {
			String _k = ("" + Math.abs(((int) rect.x + (int) (speed * dt)) / HF.TW) + "," + Math.abs((int) rect.y / HF.TH));
			String c = getMapId(_k);
			if (c != "null")
				return true;// collision tile
			else
				return false;
		}
		if (dr == "left") {
			String _k = ("" + Math.abs(((int) rect.x - (int) (speed * dt)) / HF.TW) + "," + Math.abs((int) rect.y / HF.TH));
			String c = getMapId(_k);
			if (c != "null")
				return true;// collision tile
			else
				return false;
		}
		if (dr == "up") {
			String _k = ("" + Math.abs((int) rect.x / HF.TW) + "," + Math.abs((int) (rect.y + (int) (speed * dt)) / HF.TH));
			String c = getMapId(_k);
			if (c != "null")
				return true;// collision tile
			else
				return false;
		}
		if (dr == "down") {
			String _k = ("" + Math.abs((int) rect.x / HF.TW) + "," + Math.abs((int) (rect.y - (int) (speed * dt)) / HF.TH));
			String c = getMapId(_k);
			if (c != "null")
				return true;// collision tile
			else
				return false;
		} else
			return false;
	}

	public String getMapId(String _k) {
		Integer _o = Level.Map.get(_k);
		String s;
		if (_o == null)// translate the integers into actual, um, tile types
			s = "null";
		else if (_o == 0)
			s = "water";
		else if (_o == 1)
			s = "rock";
		else if (_o == 2)
			s = "grass";
		else if (_o == 3)
			s = "deep water";
		else if (_o == 4)
			s = "deepest water";
		else if (_o == 5)
			s = "dark rock";
		else if (_o == 6)
			s = "snow";
		else if (_o == 7)
			s = "sand";
		else if (_o == 8)
			s = "lava";
		else
			s = "something else";
		// System.out.println("tileType: " + s);
		return s;
	}

	public State getState() {
		// System.out.println("l: " + l + " r: " + r + " u: " + u + " d: " + d);
		// System.out.println("lmove: " + lmove + " rmove: " + rmove + " umove: " + umove + " dmove: " + dmove);
		if (isDying)
			return State.DYING;
		else if (rpressed)
			return State.PRESSEDRIGHT;
		else if (lpressed)
			return State.PRESSEDLEFT;
		else if (dpressed)
			return State.PRESSEDDOWN;
		else if (upressed)
			return State.PRESSEDUP;
		else if (rmove)
			return State.WALKRIGHT;
		else if (lmove)
			return State.WALKLEFT;
		else if (dmove)
			return State.WALKDOWN;
		else if (umove)
			return State.WALKUP;
		else if (rjump)
			return State.JUMPRIGHT;
		else if (ljump)
			return State.JUMPLEFT;
		else if (djump)
			return State.JUMPDOWN;
		else if (ujump)
			return State.JUMPUP;
		else if (r)
			return State.STANDRIGHT;
		else if (l)
			return State.STANDLEFT;
		else if (u)
			return State.STANDUP;
		else if (d)
			return State.STANDDOWN;
		else
			return State.JUMPRIGHT;
	}

	public void dispose() {
		atlas.dispose();
		death.dispose();
	}

	public void render(HF game, float dt) {
		game.batch.draw(getFrame(dt), rect.x, rect.y);
	}

}
