using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Player : MonoBehaviour, IHittable, ICharacter {

    public Rigidbody body;

    public float jumpPower = 2.0f;
    public float maxSpeed = 2.0f;
    public float acc = 1.0f;
    Vector3 moveVector = Vector3.zero;

    [SerializeField] Collider col;

    [Header("Controls")]
    KeyCode keyRight = KeyCode.D;
    KeyCode keyLeft = KeyCode.A;
    KeyCode keyUp = KeyCode.W;
    KeyCode keyDown = KeyCode.S;
    KeyCode keyJump = KeyCode.Space;
    KeyCode keyAttack = KeyCode.Mouse0;
    KeyCode keyUse = KeyCode.F;

    [Header("Ground cast")]
    bool isGrounded = true;
    bool shouldGroundcast = true;
    public Vector3 groundCastOffset = Vector3.zero;
    public float groundCastRadius = 0.1f;
    public float groundCastTimeoutOnJump = 0.5f;
    float groundCastTimeoutTime = 0.0f;

    [Header("Animation")]
    [SerializeField] Vector3 leftRotation = new Vector3(180.0f, 0.0f, 0.0f);
    [SerializeField] Vector3 rightRotation = new Vector3(0.0f, 0.0f, 0.0f);
    [SerializeField] ParticleSystem jumpParticleSystem;
    [SerializeField] Animator animator;
    [SerializeField] HitAnimator hitAnimator;
    
    public EntityStats stats;
    public EntityStatsData statsData;

    [Header("Sounds")]
    [SerializeField] GameSound jumpSound;
    [SerializeField] GameSound hitSound;

    [Header("Equipment")]
    [SerializeField] Equipment weaponPrefab;
    [SerializeField] Transform weaponHand;
    [SerializeField] Equipment weapon;

    bool isDead = false;

    //Events
    [HideInInspector] public UnityEvent OnPlayerDeathE;
    [HideInInspector] public UnityEvent OnCoinE;

    [Header("Settings")]
    [SerializeField] Transform _transfGFX;
    [SerializeField] Transform _transfPhys;

    [Header("Other")]
    public int coins = 0;

    public void AddCoins(int amount) {
        coins += amount;
        OnCoinE.Invoke();
    }

    private void Update() {
        InputUpdate();
        stats.Update(Time.deltaTime);
    }

    private void OnDrawGizmos() {
        Gizmos.DrawSphere(transform.position + groundCastOffset, groundCastRadius);
    }

    private void Awake() {
        stats = new EntityStats(statsData);

        if(weaponPrefab != null) {
            weapon = Instantiate(weaponPrefab, weaponHand);
            weapon.ownerChar = this;
            weapon.ownerStats = stats;
            weapon.ownerCol = col;
            weapon.Init();
        }
    }

    void InputUpdate() {
        if(isDead) return;

        if(Input.GetKey(keyLeft)) moveVector.x -= 1.0f;
        if(Input.GetKey(keyRight)) moveVector.x += 1.0f;
        if(Input.GetKey(keyUp)) moveVector.z += 1.0f;
        if(Input.GetKey(keyDown)) moveVector.z -= 1.0f;
        if(Input.GetKeyDown(keyJump)) Jump();
        if(Input.GetKeyDown(keyAttack)) Attack();
        if(Input.GetKeyDown(keyUse)) Use();
    }

    private void FixedUpdate() {
        GroundUpdate();
        MovementUpdate();
        AnimationUpdate();
    }

    Collider [] colBuf = new Collider[1];
    int colResBuf;

    void GroundUpdate() {
        if(groundCastTimeoutTime > 0.0f) {
            shouldGroundcast = false;
            groundCastTimeoutTime -= Time.deltaTime;

            if(groundCastTimeoutTime <= 0.0f) {
                shouldGroundcast = true;
            }
        }

        if(shouldGroundcast) {
            colResBuf = Physics.OverlapSphereNonAlloc(transform.position + groundCastOffset, groundCastRadius, colBuf);
            if(colResBuf > 0) {
                isGrounded = true;
            }
        }
    }

    Vector3 targetVel;
    Vector3 currentVel;
    float speedChange;

    public Transform transfPhys {
        get {
            return _transfPhys;
        }
    }

    public Transform transfGFX {
        get {
            return _transfGFX;
        }
    }

    void MovementUpdate() {
        animator.SetBool("IsMoving", moveVector.sqrMagnitude > 0.1f);

        targetVel = moveVector * maxSpeed;
        currentVel = body.velocity;
        
        speedChange = acc * Time.fixedDeltaTime;

        if(Mathf.Abs(targetVel.x) - Mathf.Abs(currentVel.x) <= speedChange) currentVel.x = targetVel.x;
        else {
            if(currentVel.x < targetVel.x) currentVel.x += speedChange;
            else currentVel.x -= speedChange;
        }

        if(Mathf.Abs(targetVel.z) - Mathf.Abs(currentVel.z) <= speedChange) currentVel.z = targetVel.z;
        else {
            if(currentVel.z < targetVel.z) currentVel.z += speedChange;
            else currentVel.z -= speedChange;
        }

        currentVel.y += targetVel.y;

        body.velocity = currentVel;

        moveVector.x = moveVector.y = moveVector.z = 0.0f;
    }

    void AnimationUpdate() {
        if(targetVel.x < -0.02f) transfGFX.eulerAngles = leftRotation;
        else if(targetVel.x > 0.02f)transfGFX.eulerAngles = rightRotation;
    }

    void Use() {
        //TODO
    }

    void Jump() {
        if(!isGrounded) return;

        moveVector.y += jumpPower;
        isGrounded = false;
        groundCastTimeoutTime = groundCastTimeoutOnJump;
        jumpParticleSystem.Play();
        jumpSound.Play();

    }

    void Attack() {
        if(weapon != null) {
            weapon.Attack();
            animator.SetInteger("AttackMode", 0);
            animator.SetTrigger("Attack");
        }
    }

    public void Hit(float damage) {
        if(isDead) return;

        hitAnimator.PlayHit();
        stats.Hurt(damage);
        hitSound.Play();

        if(stats.isDead) {
            OnPlayerDeathE.Invoke();
            isDead = true;
            animator.SetBool("IsDead", true);
            col.enabled = false;
            body.isKinematic = true;
        }
    }
}
