using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class SideCharacter2D : MonoBehaviour
{
    [SerializeField]
    private float _maxSpeed = 10f;                    // The fastest the player can travel in the x axis.
    [SerializeField]
    private float _jumpForce = 400f;                  // Amount of force added when the player jumps.
    [SerializeField]
    private bool _airControl = false;                 // Whether or not a player can steer while jumping;
    [SerializeField]
    private LayerMask _groundLayerMask;                  // A mask determining what is ground to the character

    private Transform _groundCheck;    // A position marking where to check if the player is grounded.
    const float GroundedRadius = .3f; // Radius of the overlap circle to determine if grounded
    private bool _isGrounded;            // Whether or not the player is grounded.
    public Transform WeaponNode;
    const float CeilingRadius = .01f; // Radius of the overlap circle to determine if the player can stand up
    private Animator _anim;            // Reference to the player's animator component.
    private Rigidbody2D _rigidbody2D;
    private bool _isFacingRight = true;  // For determining which way the player is currently facing.
    private bool _isJumping = false;
    private bool _canJump = true;
    private bool _ThrowInProgress = false;

    float _timeThrow;
    public float ThrowStandDuration = 0.12f;
    public float ThrowBendDuration = 1.7f;

    public Transform[] Weapons;
    public Text[] AmmoCounters;
    public Image WeaponSelector;
    private int _currentWeapon = 0;
    private int[] _ammos;

    public float ThrowForce = 100;

    private void Awake()
    {
        // Setting up references.
        _groundCheck = transform.Find("GroundCheck");

        _anim = GetComponent<Animator>();
        _rigidbody2D = GetComponent<Rigidbody2D>();

        _ammos = new int[Weapons.Length];
        for (int i = 0; i < _ammos.Length; i++)
        {
            _ammos[i] = 50;
            AmmoCounters[i].text = string.Format("{0}", _ammos[i]);
        }
    }

    public void Start()
    {
        TakeWeapon(_currentWeapon);
    }

    private void Update()
    {
        if (_isGrounded == true && _isJumping == false)
        {
            // Read the jump input in Update so button presses aren't missed.
            _isJumping = Input.GetButton("Jump"); //$$ GetButtonDown
        }

        if (_ThrowInProgress == false && Input.GetButtonDown("Fire1") && _ammos[_currentWeapon] > 0)
        {
            _ThrowInProgress = true;
            _timeThrow = Time.time;
            _anim.SetTrigger("Throw");
        }
        else if (_ThrowInProgress == true && Input.GetButtonUp("Fire1"))
        {
            _anim.SetTrigger("ThrowNow");
        }

        for (int i = 0; i < Weapons.Length; i++)
        {
            if (Input.GetKey(KeyCode.Alpha1 + i))
            {
                if (_ammos[i] > 0)
                {
                    _currentWeapon = i;
                    if (_ThrowInProgress == false)
                    {
                        TakeWeapon(_currentWeapon);
                    }
                }
            }
        }
    }

    private void FixedUpdate()
    {
        FixedUpdateInput();

        bool isGrounded = false;

        // The player is grounded if a circlecast to the groundcheck position hits anything designated as ground
        // This can be done using layers instead but Sample Assets will not overwrite your project settings.
        Collider2D[] colliders = Physics2D.OverlapCircleAll(_groundCheck.position, GroundedRadius, _groundLayerMask);
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].gameObject != gameObject)
                isGrounded = true;
        }

        if (_isGrounded != isGrounded)
        {
            _isGrounded = isGrounded;
            _anim.SetBool("Ground", _isGrounded);
            if (_isGrounded)
            {
                _anim.SetTrigger("HitGround");
                _canJump = true;
            }
        }

        // Set the vertical animation
        _anim.SetFloat("vSpeed", _rigidbody2D.velocity.y);
    }

    private void FixedUpdateInput()
    {
        // Read the inputs.
        float h = Input.GetAxis("Horizontal");
        // Pass all parameters to the character control script.
        Move(h, _isJumping);
        _isJumping = false;
    }

    public void Move(float move, bool jump)
    {
        //only control the player if grounded or airControl is turned on
        if (_isGrounded || _airControl)
        {
            // The Speed animator parameter is set to the absolute value of the horizontal input.
            _anim.SetFloat("Speed", Mathf.Abs(move));

            // Move the character
            _rigidbody2D.velocity = new Vector2(move * _maxSpeed, _rigidbody2D.velocity.y);

            // If the input is moving the player right and the player is facing left...
            if (move > 0 && !_isFacingRight)
            {
                // ... flip the player.
                Flip();
            }
            // Otherwise if the input is moving the player left and the player is facing right...
            else if (move < 0 && _isFacingRight)
            {
                // ... flip the player.
                Flip();
            }
        }
        // If the player should jump...
        if (_isGrounded && jump && _canJump)
        {
            _canJump = false;
            // Add a vertical force to the player.
            _anim.SetTrigger("Jump");
            _rigidbody2D.AddForce(new Vector2(0f, _jumpForce));
        }
    }

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

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

    private void ThrowEvent()
    {
        float throwDuration = Time.time - _timeThrow;
        float bendDuration = Mathf.Max(0, throwDuration - ThrowStandDuration);

        _ThrowInProgress = false;

        ThrowProjectile(ThrowForce * (1 + bendDuration));

        WeaponNode.GetComponent<SpriteRenderer>().sprite = null;

        Debug.LogFormat("ThrowEvent (throwDuration = {0:0.00}, bendDuration = {1:0.00})\n", throwDuration, bendDuration);
    }

    private void ReloadEvent()
    {
        Debug.LogFormat("ReloadEvent");

        if (_ammos[_currentWeapon] > 0)
        {
            _ammos[_currentWeapon] -= 1;
            AmmoCounters[_currentWeapon].text = string.Format("{0}", _ammos[_currentWeapon]);
            if (_ammos[_currentWeapon] > 0)
            {
                TakeWeapon(_currentWeapon);
            }
        }
    }

    private void TakeWeapon(int weapon)
    {
        WeaponNode.GetComponent<SpriteRenderer>().sprite = Weapons[weapon].GetComponent<SpriteRenderer>().sprite;
        WeaponSelector.rectTransform.position = AmmoCounters[weapon].rectTransform.position;
    }

    private void ThrowProjectile(float force)
    {
        Transform projectile = Instantiate(Weapons[_currentWeapon], WeaponNode.position, 
            _isFacingRight ? WeaponNode.rotation : Quaternion.Inverse(WeaponNode.rotation)) as Transform;
        
        Rigidbody2D rb = projectile.gameObject.GetComponent<Rigidbody2D>();

        rb.velocity = _rigidbody2D.velocity;
        rb.AddForce(new Vector2(_isFacingRight ? 1 : -1, 1) * force);
    }
}
