using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

[RequireComponent (typeof (Controller2D))]
public class Player : Entity {

	public float maxJumpHeight = 4;
	public float minJumpHeight = 1;
	public float timeToJumpApex = .4f;
	float accelerationTimeAirborne = .2f;
	float accelerationTimeGrounded = .1f;
	float moveSpeed = 6;

	public Vector2 wallJumpClimb;
	public Vector2 wallJumpOff;
	public Vector2 wallLeap;

	public float wallSlideSpeedMax = 3;
	public float wallStickTime = .25f;
	float timeToWallUnstick;

	float gravity;
	float maxJumpVelocity;
	float minJumpVelocity;
	Vector3 velocity;
	float velocityXSmoothing;

	Controller2D controller;
    public Controller2D Controller
    {
        get
        {
            return controller; 
        }
    }

	Vector2 directionalInput;
	bool wallSliding;
	int wallDirX;

    public Transform rootBone; 
    public InventoryRenderer PlayerInventoryRenderer;
    public Transform HandBone_R;
    public Transform HandBone_L; 
    public Inventory Inventory;
    public float[] SlotCooldowns = new float[3] { 0, 0, 0 };
    public Item SelectedItem;
    private int selectedItemIndex;
    public static Player Instance;

    public void Awake()
    {
        Instance = this; 
        controller = GetComponent<Controller2D> ();
        Inventory = new Inventory(3);
    }

    public override void Start() {
        base.Start();

        PlayerInventoryRenderer.SetInventory(Inventory); 
        Inventory.SetItem(1, 1);
        Inventory.SetItem(2, 2);
        SelectItem(0); 

		gravity = -(2 * maxJumpHeight) / Mathf.Pow (timeToJumpApex, 2);
		maxJumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
		minJumpVelocity = Mathf.Sqrt (2 * Mathf.Abs (gravity) * minJumpHeight);
	}

	void Update() {
        if(IsDead)
        {
            return; 
        }
        CalculateVelocity ();
		HandleWallSliding ();

		controller.Move (velocity * Time.deltaTime, directionalInput);

		if (controller.collisions.above || controller.collisions.below) {
			if (controller.collisions.slidingDownMaxSlope) {
				velocity.y += controller.collisions.slopeNormal.y * -gravity * Time.deltaTime;
			} else {
				velocity.y = 0;
			}
		}

        HandleInteracables(); 
        
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll > 0)
        {
            selectedItemIndex = (selectedItemIndex + 1) % Inventory.Size;
            SelectItem(selectedItemIndex);
        }
        else if (scroll < 0)
        {
            selectedItemIndex = (selectedItemIndex + Inventory.Size - 1) % Inventory.Size;
            SelectItem(selectedItemIndex);
        }

        for(int n=0;n<Inventory.Size;n++)
        {
            if (Input.GetKey("" + (n + 1)))
            {
                SelectItem(n); 
            }
        }

        if (SelectedItem != null)
        {
            Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            Vector2 aimPos = new Vector2(mouseWorldPos.x, mouseWorldPos.y);
            SelectedItem.UpdatePosition(this.HandBone_R);
        }

        for (int n = 0; n < SlotCooldowns.Length; n++)
        {
            SlotCooldowns[n] = Mathf.Clamp(SlotCooldowns[n] - 1.0f * Time.deltaTime, 0, float.MaxValue); 
        }

        if (Input.GetMouseButton(0))
        {
            Attack(); 
        }

        this.Animator.SetFloat("Speed", controller.collisions.below ? Mathf.Abs(controller.collisions.moveAmountOld.x) : 0);
        this.Animator.SetBool("Falling", velocity.y < 0.5f && !controller.collisions.below);
        
        if (controller.collisions.faceDir > 0)
        {
            // this.SpriteRenderer.flipX = false;
            rootBone.transform.localScale = Vector3.one; 
        }
        else
        {
            // this.SpriteRenderer.flipX = true;
            rootBone.transform.localScale = Vector3.one - 2 * Vector3.up; 
        }

	}

    private IEnumerator loadGameOverScreenCoroutine()
    {
        yield return new WaitForSeconds(3.0f);
        SceneManager.LoadScene("GameOverScene"); 
    }

    public override void Die(bool destroyGameObject)
    {
        base.Die(false);
        this.SpriteRenderer.sprite = null; 
        StartCoroutine(loadGameOverScreenCoroutine()); 
        GameObject.Destroy(SelectedItem.gameObject); 
    }

    private void HandleInteracables()
    {
        if (IsDead)
        {
            return;
        }
        InteractableObject[] interactables = InteractibleManager.Instance.GetAvailableInteractables(this.transform.position);
        UserInterface.Instance.SetShowInteractible(interactables.Length > 0); 
        if (Input.GetKeyDown(KeyCode.E))
        {
            foreach(InteractableObject i in interactables)
            {
                i.OnInteract(this); 
            }
        }
    }

    public void Attack()
    {
        Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        Vector2 aimPos = new Vector2(mouseWorldPos.x, mouseWorldPos.y);

        bool usable = SlotCooldowns[selectedItemIndex] <= 0;
        if (SelectedItem is WeaponGun)
        {
            if (this.controller.collisions.faceDir > 0)
            {
                usable &= aimPos.x > this.transform.position.x + 0.1f;
            }
            else
            {
                usable &= aimPos.x < this.transform.position.x - 0.1f;
            }
        }
        if (usable)
        {
            SelectedItem.Use(aimPos);
            SlotCooldowns[selectedItemIndex] = SelectedItem.Cooldown;
        }
    }

    public void SelectItem(int slot)
    {
        if (SelectedItem != null)
        {
            GameObject.Destroy(SelectedItem.gameObject);
        }
        SelectedItem = ItemManager.Instance.SpawnWeapon(Inventory.GetItem(slot));
        SelectedItem.Owner = this;
        this.Animator.SetBool("Holding_Gun", SelectedItem is WeaponGun); 
    }

    public void EquipItem(int itemId, int slot)
    {
        Inventory.SetItem(slot, itemId);
        SelectItem(slot);
    }

    public bool EquipInFreeSlot(int itemId)
    {
        if (IsDead)
        {
            return false; 
        }

        for (int n = 0; n < Inventory.Size; n++)
        {
            if (Inventory.GetItem(n) == 0)
            {
                this.EquipItem(itemId, n);
                return true;
            }
        }

        return false;
    }

    public void SetDirectionalInput (Vector2 input) {
		directionalInput = input;
	}

	public void OnJumpInputDown() {
        if (IsDead)
        {
            return;
        }

        bool jumped = false; 
        if (wallSliding) {
			if (wallDirX == directionalInput.x) {
				velocity.x = -wallDirX * wallJumpClimb.x;
				velocity.y = wallJumpClimb.y;
			}
			else if (directionalInput.x == 0) {
				velocity.x = -wallDirX * wallJumpOff.x;
				velocity.y = wallJumpOff.y;
			}
			else {
				velocity.x = -wallDirX * wallLeap.x;
				velocity.y = wallLeap.y;
			}
            jumped = true; 
		}
		if (controller.collisions.below) {
			if (controller.collisions.slidingDownMaxSlope) {
				if (directionalInput.x != -Mathf.Sign (controller.collisions.slopeNormal.x)) { // not jumping against max slope
					velocity.y = maxJumpVelocity * controller.collisions.slopeNormal.y;
					velocity.x = maxJumpVelocity * controller.collisions.slopeNormal.x;
				}
			} else {
				velocity.y = maxJumpVelocity;
                jumped = true; 
			}
		}
        if(jumped)
        {
            this.Animator.SetTrigger("Jump"); 
        }
	}

	public void OnJumpInputUp() {
		if (velocity.y > minJumpVelocity) {
			velocity.y = minJumpVelocity;
		}
	}
		

	void HandleWallSliding() {
		wallDirX = (controller.collisions.left) ? -1 : 1;
		wallSliding = false;
		if ((controller.collisions.left || controller.collisions.right) && !controller.collisions.below && velocity.y < 0) {
			wallSliding = true;

			if (velocity.y < -wallSlideSpeedMax) {
				velocity.y = -wallSlideSpeedMax;
			}

			if (timeToWallUnstick > 0) {
				velocityXSmoothing = 0;
				velocity.x = 0;

				if (directionalInput.x != wallDirX && directionalInput.x != 0) {
					timeToWallUnstick -= Time.deltaTime;
				}
				else {
					timeToWallUnstick = wallStickTime;
				}
			}
			else {
				timeToWallUnstick = wallStickTime;
			}

		}

	}

	void CalculateVelocity() {
		float targetVelocityX = directionalInput.x * moveSpeed;
		velocity.x = Mathf.SmoothDamp (velocity.x, targetVelocityX, ref velocityXSmoothing, (controller.collisions.below)?accelerationTimeGrounded:accelerationTimeAirborne);
		velocity.y += gravity * Time.deltaTime;
	}
}
