using UnityEngine;
using ScrambleKit.FX;

public class JackMove : MonoBehaviour
{
	public FXTrigger BounceFX;
	public FXTrigger DigFX;

	public float BounceUpSpeedBase = 3f;
	public float BounceUpSpeedVariable = 3f;

	public float BounceSideSpeedBase = 0.5f;
	public float BounceSideSpeedVariable = 1f;

	public float SideAirControlAtHeight = 2.5f;
	public float SideAirControlAccel = 5f;

	//public float DigTime = 0.6f;
	public LayerMask PlayerLayer;
	public float playerProbeWidth = 0.4f;
	public float BlockProbeLen = 0.6f;
	public float PlayerCheckProbeLen = 0.6f;

	public float ArenaSize = 5f;

	public string DEBUG;

	public SpriteRenderer TouchDebugRend;
	public Transform Player;
	private PlayerMovement pmove;


	public enum JackState
	{
		Free,
		Digging
	}
	public JackState state = JackState.Free;
	private float digUntil;
	private float lastBounceTime;
	private Block blockDigging;
	private Vector2 digStartPos;

	private Rigidbody2D r2;
	private Animator anim;

	void Start()
	{
		r2 = GetComponent<Rigidbody2D>();
		anim = GetComponent<Animator>();
		pmove = Player.GetComponent<PlayerMovement>();
	}

	void FixedUpdate()
	{
		bool on = IsPlayerOnJack();
		if (TouchDebugRend != null)
			TouchDebugRend.enabled = on;


		switch (state)
		{
			case JackState.Free:
				Transform t = CheckForBlock();				

				if (t != null && DidntJustBounce())
				{
					Block block = t.GetComponent<Block>();
					if (ShouldDig(block))
					{
						blockDigging = block;
						block.StartDrilling();
						digStartPos = transform.position;
						EnterState(JackState.Digging);
					}
					else
					{
						Score.ComboLevel = 0;
						Bounce();
					}						
				}
				else if (IsAtAirControlHeight())
				{
					DoAirControl();
				}
				break;

			case JackState.Digging:
				if(Time.fixedTime > digUntil)
				{
					Score.ScoreBlock(blockDigging.ScoreValue);
					Score.ComboLevel++;

					blockDigging.Break();
					r2.isKinematic = false;

					Bounce();
					EnterState(JackState.Free);
				}
				else
				{
					DigIn();
				}
				break;

		}
	}

	private void EnterState(JackState newState)
	{
		JackState oldState = state;
		state = newState;

		switch (state)
		{
			case JackState.Free:
				anim.SetBool("Digging", false);
				break;

			case JackState.Digging:
				digUntil = Time.fixedTime + blockDigging.DigTime;
				anim.SetBool("Digging", true);
				DigFX.Trigger(transform);
				r2.isKinematic = true;
				r2.velocity = Vector2.zero;
				break;
		}
	}

	private bool ShouldDig(Block block)
	{
		return block != null && block.CanBreak() && IsPlayerOnJack();
	}

	private void DigIn()
	{
		Vector2 targetPos = (Vector2) blockDigging.transform.position + 0.5f*Vector2.up;
		Vector2 move = targetPos - digStartPos;
		float doneFactor = 1 - ((digUntil - Time.fixedTime) / blockDigging.DigTime);
		Vector2 newPos = digStartPos + move * doneFactor;

		DEBUG = "" + newPos + " -> " + targetPos;

		transform.position = newPos;
	}

	private bool DidntJustBounce()
	{
		return Time.fixedTime - lastBounceTime > 0.15f;
	}

	private void Bounce()
	{
		lastBounceTime = Time.fixedTime;
		//Vector2 deltapos = Player.position - transform.position;
		//if(deltapos.magnitude < 1.5f || Random.Range(0f, 1f) < 0.02f)
		//	RandomBounce();
		//else

		BounceFX.Trigger(transform);
		DirectedBounce();
	}

	//private void RandomBounce()
	//{
	//	float vert = BounceUpSpeedBase + 0.5f * Random.Range(0, BounceUpSpeedVariable);
	//	float y = vert - r2.velocity.y;  // cancel any existing y vel

	//	float sign = Mathf.Sign(Random.Range(-1f, 1f));
	//	float horiz = sign * (BounceSideSpeedBase + Random.Range(0, BounceSideSpeedVariable));
	//	float x = horiz;  // we don't cancel size velocity for undirected, but this is questionable?

	//	Vector2 accel = new Vector2(x, y);
	//	Vector2 force = r2.mass * accel / Time.fixedDeltaTime;
	//	r2.AddForce(force);
	//}

	private void DirectedBounce()
	{
		float blockHeight = Block.HighestBlockHeight + 1;
		Block.HighestBlockHeight = -100;

		float horizDistFactor = Mathf.Min(1, Mathf.Abs(Player.position.x - transform.position.x) / ArenaSize);
		float vertDistFactor = Mathf.Clamp((blockHeight - transform.position.y) / ArenaSize, 0f, 1f);   // used to use: pmove.LastStandingHeight
		horizDistFactor *= Random.Range(0.8f, 1.15f);
		vertDistFactor *= Random.Range(0.8f, 1.15f);

		float vert = BounceUpSpeedBase + vertDistFactor * BounceUpSpeedVariable;
		float y = vert - r2.velocity.y;  // cancel any existing y vel

		float sign = Mathf.Sign(Player.position.x - transform.position.x);
		float horiz = sign * (BounceSideSpeedBase + horizDistFactor * BounceSideSpeedVariable);
		float x = horiz - r2.velocity.x;  // cancel horiz velocity for directed bounce

		Vector2 accel = new Vector2(x, y);
		Vector2 force = r2.mass * accel / Time.fixedDeltaTime;
		r2.AddForce(force);
	}

	private void DoAirControl()
	{
		float dir = Mathf.Sign(Player.position.x - transform.position.x);
		Vector2 accel = new Vector2(dir * SideAirControlAccel, 0);

		DEBUG = "" + accel + "  @" + Time.fixedTime;

		Vector2 force = r2.mass * accel;
		r2.AddForce(force);
	}

	private Transform CheckForBlock()
	{
		float len = BlockProbeLen;

		Vector2 ctr = transform.position;

		RaycastHit2D res = Physics2D.Raycast(ctr, -Vector2.up, len);

		Debug.DrawLine(ctr, ctr - Vector2.up * len);

		if (res.collider == null)
			return null;
		else
			return res.collider.transform;
	}

	private bool IsAtAirControlHeight()
	{
		float len = SideAirControlAtHeight;

		Vector2 ctr = transform.position;

		RaycastHit2D res = Physics2D.Raycast(ctr, -Vector2.up, len);

		Debug.DrawLine(ctr + Vector2.right * 0.05f, ctr - Vector2.up * len, Color.cyan);

		return (res.collider == null);
	}

	private bool IsPlayerOnJack()
	{
		float len = PlayerCheckProbeLen;

		Vector2 ctr = (Vector2)transform.position + Vector2.up * 0.35f;
		Vector2 left = ctr + new Vector2(-playerProbeWidth, 0);
		Vector2 right = ctr + new Vector2(playerProbeWidth, 0);

		RaycastHit2D res1 = Physics2D.Raycast(left, Vector2.up, len, PlayerLayer);
		RaycastHit2D res2 = Physics2D.Raycast(ctr, Vector2.up, len, PlayerLayer);
		RaycastHit2D res3 = Physics2D.Raycast(right, Vector2.up, len, PlayerLayer);

		Debug.DrawLine(left, left + Vector2.up * len);
		Debug.DrawLine(ctr, ctr + Vector2.up * len);
		Debug.DrawLine(right, right + Vector2.up * len);

		return (res1.collider != null) || (res2.collider != null) || (res3.collider != null);
	}

}

