using UnityEngine;
using System.Collections;

public class PlayerMovementScript : MonoBehaviour 
{
	public float acceleration;
	public float deceleration;
	public float maxRunSpeed;
	public float jumpSpeed;
	public float jumpTime;
	public LayerMask groundCheckMask;

	public Transform leftSensorTransform;
	public Transform rightSensorTransform;
	public Transform groundSensorTransform;
	public Transform topSensorTransform;
	public Transform bottomSensorTransform;

	public Animator playerAnim;

	private bool isHitR;
	private bool isHitL;
	private bool isHitG;
	private bool isHitForward;
	
	private Vector2 hitPointR = Vector2.zero;
	private Vector2 hitPointL = Vector2.zero;
	private Vector2 hitPointG = Vector2.zero;
	private Vector2 hitPointForward = Vector2.zero;
	private float angle;

	private Rigidbody2D myRigidbody;
	private Transform myTransform;
	private float currentJumpTime;

	private bool canJump;

	// Use this for initialization
	void Start () 
	{
		myRigidbody = rigidbody2D;
		myTransform = transform;
		currentJumpTime = 0;
		canJump = true;
	}
	
	// Update is called once per frame
	void Update () 
	{
		//---------------------------------------------------------------------
		// Use raycasting to find where the ground is in relation to the player
		// and what angle the ground is they are on.
		//---------------------------------------------------------------------
		
		// Set the direction and distance of the ground check
		Vector2 downDirection = new Vector2(0, -1);// -myTransform.up;
		Vector2 forwardDirection = new Vector2(myTransform.localScale.x, 0);
		float distanceSide = PixelPerfect.ToUnityUnits(Constants.SIDE_SENSOR_LENGTH, Camera.main);
		float distanceGround = PixelPerfect.ToUnityUnits(Constants.GROUND_SENSOR_LENGTH, Camera.main);
		float distanceFront = PixelPerfect.ToUnityUnits(Constants.FRONT_SENSOR_LENGTH, Camera.main);
		
		// Create debug visualizations of the rays being used
		Debug.DrawRay (rightSensorTransform.position, downDirection * distanceSide, Color.green);
		Debug.DrawRay (leftSensorTransform.position, downDirection * distanceSide, Color.green);
		Debug.DrawRay (groundSensorTransform.position, downDirection * distanceGround, Color.red);

		Debug.DrawRay (topSensorTransform.position, forwardDirection * distanceFront, Color.yellow);
		Debug.DrawRay (bottomSensorTransform.position, forwardDirection * distanceFront, Color.yellow);
		
		// Create a raycast for each of the 3 sensor points
		RaycastHit2D hitR = Physics2D.Raycast(rightSensorTransform.position, downDirection, distanceSide, groundCheckMask);
		RaycastHit2D hitL = Physics2D.Raycast(leftSensorTransform.position, downDirection, distanceSide, groundCheckMask);
		RaycastHit2D hitG = Physics2D.Raycast(groundSensorTransform.position, downDirection, distanceGround, groundCheckMask);
		RaycastHit2D hitT = Physics2D.Raycast(topSensorTransform.position, forwardDirection, distanceFront, groundCheckMask);
		RaycastHit2D hitB = Physics2D.Raycast(bottomSensorTransform.position, forwardDirection, distanceFront, groundCheckMask);
		
		isHitR = false;
		isHitL = false;
		isHitG = false;
		
		hitPointR = Vector2.zero;
		hitPointL = Vector2.zero;
		hitPointG = Vector2.zero;
		hitPointForward = Vector2.zero;
		
		// The right sensor hit something
		if (hitR != null && hitR.collider != null)
		{
			isHitR = true;
			hitPointR = hitR.point;
		}
		
		// The left sensor hit something
		if (hitL != null && hitL.collider != null)
		{
			isHitL = true;
			hitPointL = hitL.point;
		}
		
		// The middle sensor hit something
		if (hitG != null && hitG.collider != null)
		{
			isHitG = true;
			hitPointG = hitG.point;
		}
		
		// The player is still on the ground
		if (isHitG)
		{
			// Both sides of the player hit the ground, find the angle of the ground
			if (isHitR && isHitL)
			{
				angle = Vector2.Angle(hitPointL, hitPointR);
				//				Debug.Log("Angle is: " + angle);
			}
			// If one of the sensors is off the ground assume the slope hasn't changed and just keep using the same angle
		}

		// Check to see if we hit a wall
		isHitForward = false;
		
		// The top sensor hit something
		if (hitT != null && hitT.collider != null)
		{
			isHitForward = true;
			hitPointForward = hitT.point;
		}
		
		// The bottom sensor hit something
		if (hitB != null && hitB.collider != null)
		{
			isHitForward = true;
			hitPointForward = hitB.point;
		}

		//---------------------------------
		// Horizontal Movement
		//---------------------------------

		// Feed the input to the animator
		playerAnim.SetFloat("speed", Mathf.Abs(Input.GetAxis("Horizontal")));

		// Flip the orientation if necessary

		// Facing right
		if (myTransform.localScale.x > 0)
		{
			// We're moving left, turn around;
			if (Input.GetAxis("Horizontal") < 0)
			{
				Flip();
			}
		}

		// Facing left
		if (myTransform.localScale.x < 0)
		{
			// We're moving right, turn around;
			if (Input.GetAxis("Horizontal") > 0)
			{
				Flip();
			}
		}

		// Make sure we're not hitting a wall
		Vector2 newVelocity = Vector3.zero;
//		if (!isHitForward)
//		{
			// Move based on the input axis
			newVelocity = myRigidbody.velocity;
			switch (PlayerStateManager.instance.currentPlayerState)
			{
				case Enums.PlayerState.Default:
				{
					newVelocity.x = maxRunSpeed * Input.GetAxis("Horizontal") * Time.deltaTime;
					break;
				}
				case Enums.PlayerState.InAir:
				{
					newVelocity.x = maxRunSpeed * Input.GetAxis("Horizontal") * Time.deltaTime;
					break;
				}
				case Enums.PlayerState.Grabbing:
				{
					newVelocity.x = (maxRunSpeed /2) * Input.GetAxis("Horizontal") * Time.deltaTime;
					break;
				}
			}
//		}

		myRigidbody.velocity = newVelocity;

		// Check to see if we've hit a wall, if we have move us back a bit.

		/*if (isHitForward)
		{
			float hitDistance = hitPointForward.x - topSensorTransform.position.x;

			// The ray hit closer than the distance of the raycast, set to max distance
			if (Mathf.Abs(hitDistance) < Constants.FRONT_SENSOR_LENGTH)
			{
				Vector2 newPos = myTransform.position;
				newPos.x -= hitDistance;
				myTransform.position = newPos;
			}

		}*/

		//-----------------------------
		// Jumping
		//-----------------------------

		// We pressed the jump key
		if (Input.GetAxis("Jump") > 0)
		{
			/*Debug.Log("trying to jump. canJump: " + canJump);
			Debug.Log("currentJumpTime: " + currentJumpTime);
			Debug.Log("currentPlayerState: " + PlayerStateManager.instance.currentPlayerState);*/
			if (canJump)
			{
				// We have not jumped yet since hitting the ground last
				if (currentJumpTime <= 0 && PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.Default)
				{
					currentJumpTime = jumpTime;
					PlayerStateManager.instance.currentPlayerState = Enums.PlayerState.InAir;
				}

				// The player is still affecting the jump height
				if (PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.InAir)
				{
					if (currentJumpTime > 0)
					{
						currentJumpTime -= Time.deltaTime;

						Vector2 newJumpVelocity = myRigidbody.velocity;
						newJumpVelocity.y = jumpSpeed;

						myRigidbody.velocity = newJumpVelocity;
					}
					else
					{
						canJump = false;
					}
				}
			}
		}
		// We are not pressing the jump key
		else
		{
			// We are still in the air
			if (PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.InAir)
			{
				canJump = false;
				currentJumpTime = 0;
			}
		}

		// We are still in the air, check to see if we hit the ground
		if (PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.InAir)
		{
			// Check to see if we are falling (moving down)
			if (myRigidbody.velocity.y <= 0)
			{
				if (isHitL || isHitR || isHitG)
				{
					canJump = true;
					currentJumpTime = 0;
					PlayerStateManager.instance.currentPlayerState = Enums.PlayerState.Default;
				}
			}
		}

		// Check to see if we were on the ground and fell off of something
		if (PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.Default)
		{
			if (!isHitL && !isHitR && !isHitG)
			{
				Debug.Log("all sensors failed");
				PlayerStateManager.instance.currentPlayerState = Enums.PlayerState.InAir;
			}
		}

		// Set jump variables in player animator
		playerAnim.SetBool("inAir", PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.InAir);
		playerAnim.SetFloat("ySpeed", myRigidbody.velocity.y);

		// We morphed into a new shape, reset jump variables so we can jump when we get out
		if (PlayerStateManager.instance.currentPlayerState == Enums.PlayerState.Morphing)
		{
			canJump = true;
			currentJumpTime = 0;
		}
	}

	private void Flip()
	{
		Vector2 newScale = myTransform.localScale;
		newScale.x *= -1;
		myTransform.localScale = newScale;
		
		Vector2 turnVelocity = myRigidbody.velocity;
		turnVelocity.x = 0;
		myRigidbody.velocity = turnVelocity;
	}
}
