using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {
    public AudioClip jumpSound;
    public LayerMask groundLayers;


    private const float JUMP_IMPULSE_Y_MULTIPLIER = 1.75f;
    private const float JUMP_IMPULSE_X_MULTIPLIER = 0.7f;
    private bool isFacingLeft = false;
    private float distToGround;
    private float halfWidth;
    private Vector3 lastPosition;
    private int deaths = 0;

    // Use this for initialization
    void Start() {
        distToGround = collider2D.bounds.extents.y;
        halfWidth = collider2D.bounds.extents.x;
    }

    bool DoGroundedRaycast(Vector3 direction) {
        Quaternion rotation = Quaternion.AngleAxis (45.0f, transform.right);
        Vector3 rayPosition = transform.position;
        if (direction.Equals(Vector3.down)) {
            return castRay(rayPosition, rotation);
        } else if (direction.Equals(Vector3.left)) {
            rayPosition.x = rayPosition.x - halfWidth;
            return castRay(rayPosition, rotation);
        } else if (direction.Equals(Vector3.right)) {
            rayPosition.x = rayPosition.x + halfWidth;
            return castRay(rayPosition, rotation);
        }
        return false;
    }

    private bool castRay(Vector3 start, Quaternion rotation) {
        Vector3 direction = rotation * transform.forward;
        Debug.DrawRay(start, direction, Color.red);
        return Physics2D.Raycast(start, direction, distToGround + 0.2f, groundLayers);
    }

    /**
     * Return grounded direction (left, right, down), or default Vector3 if not grounded
     */
    public Vector3 GroundedDirection() {
        Vector3[] directions = {
            Vector3.down,
            Vector3.left,
            Vector3.right,
        };

        foreach (var direction in directions) {
            if (DoGroundedRaycast(direction)) {
                return direction;
            }
        }
        return default(Vector3);
    }

    /***
     * Reset hero to last position and increment death count
     */
    void Die() {
        deaths++;

        // Reset multiplier
        GameObject.Find("Main Camera").GetComponent<MultiplierBar>().resetMultiplier();

        transform.position = lastPosition;
        if (isFacingLeft) {
            // Hero faces right by default
            Flip();
        }
        transform.rotation = default(Quaternion);
        rigidbody2D.velocity = default(Vector3);
    }

    // Update is called once per frame
    void Update() {
        if (Time.timeScale == 0) {
            // Do nothing on pause
            return;
        }

        if (rigidbody2D.position.y < -2) {
            // Reset after falling too low
            Die();
            return;
        }

        Vector3 groundedDirection = GroundedDirection();
        for (int i = 0; i < Input.touchCount; ++i) {
            Touch touch = Input.GetTouch(i);
            if (touch.phase == TouchPhase.Began) {
                HandleJump(touch.position, groundedDirection);
            }
        }

        if (Input.GetMouseButtonDown(0)) {
            HandleJump(Input.mousePosition, groundedDirection);
        }
    }
    
    // Physics things go here
    void FixedUpdate () {
        Vector3 groundedDirection = GroundedDirection();
        if (!groundedDirection.Equals(Vector3.down)) {
            transform.rotation = Quaternion.Euler(transform.rotation.z, transform.rotation.y, caluateAngle(Physics2D.gravity));
        }
    }

    void HandleJump(Vector2 inputLocation, Vector3 groundedDirection) {
        bool canJumpLeft = false;
        bool canJumpRight = false;

        if (groundedDirection.Equals(Vector3.left)) {
            canJumpRight = true;
        } else if (groundedDirection.Equals(Vector3.right)) {
            canJumpLeft = true;
        } else if (groundedDirection.Equals(Vector3.down)) {
            canJumpLeft = true;
            canJumpRight = true;

            // Store current position in case jump causes super death
            lastPosition = transform.position;
        } else {
            // Can't jump if not grounded
            return;
        }

        int screenCenter = Screen.width / 2;

        bool isLeftTouch = (inputLocation.x < screenCenter);

        if ((isLeftTouch && !canJumpLeft) || (!isLeftTouch && !canJumpRight)) {
            // Can't jump
            return;
        }

        bool directionChanged = ((isLeftTouch && !isFacingLeft) || (!isLeftTouch && isFacingLeft));
        if (directionChanged) {
            Flip();
        }
        Jump(isLeftTouch);
    }

    void Jump(bool isLeft) {
        if (!AudioSingleton.instance.IsSFXMuted()) {
            AudioSource.PlayClipAtPoint(jumpSound, transform.position);
        }
        // zero the velocity before applying the jump
        rigidbody2D.velocity = new Vector2(0, 0);

        rigidbody2D.AddForce(Vector3.up * JUMP_IMPULSE_Y_MULTIPLIER, ForceMode2D.Impulse);

        Vector3 direction = (isLeft ? Vector3.left : Vector3.right);
        rigidbody2D.AddForce(direction * JUMP_IMPULSE_X_MULTIPLIER, ForceMode2D.Impulse);
    }

    void Flip() {
        // Switch the way the player is labelled as facing
        isFacingLeft = !isFacingLeft;

        // Multiply the player's x local scale by -1
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }

    void OnTriggerEnter2D(Collider2D otherCollider) {
        CoinScript coin = otherCollider.gameObject.GetComponent<CoinScript>();
        if (coin != null && !coin.getIsCollected()) {
            // Collect points and destroy coin
            int scoreCollected = GameObject.Find("LevelController").GetComponent<LevelController>().collectPoints(coin.points);
            coin.collected(scoreCollected);
        }
    }

    public static float caluateAngle(Vector2 vector)
    {
        vector = vector.normalized;
        if (vector.y < 0) {
            return 360 - (Mathf.Atan2(vector.x, -vector.y) * Mathf.Rad2Deg * -1);
        } else {
            return Mathf.Atan2(vector.x, -vector.y) * Mathf.Rad2Deg;
        }
    }
}
