using UnityEngine;
using System.Collections;

public class Player_controller : MonoBehaviour
{
    private Rigidbody2D rb;
    private float xAxis;
    Animator anim;
    public static Player_controller Instance;
    PlayerStateList pState;
    private float gravity;

// --- Paramètre de la taille du personnage ---
    [Header("Scale Setting")]
    [SerializeField] private float xScale = 4;

// --- Paramètre de mouvement horizontaux ---
    [Header("Horizontal Movement Setting")]
    [SerializeField] private float walkSpeed = 1;

    [Header("Dash Setting")]
    [SerializeField] private float dashSpeed;
    [SerializeField] private float dashTime;
    [SerializeField] private float dashCooldown;
    private bool canDash = true;
    private bool dashed;

// --- Paramètre de saut ---
    [Header("Vertical Movement Setting")]
    [SerializeField] private float jumpForce = 45;
    private int jumpBufferCounter = 0;
    [SerializeField] private int  jumpBufferFrame;
    private float coyoteTimeCounter = 0;
    [SerializeField] private float CoyoteTime;
    private int airJumpCounter = 0;
    [SerializeField] private int MaxAirJumps;

// --- Paramètre du détecteur du sol ---
    [Header("GroundCheck Setting")]
    [SerializeField] private Transform GroundCheckPoint;
    [SerializeField] private float groundCheckY = 0.2f;
    [SerializeField] private float groundCheckX = 0.5f;
    [SerializeField] private LayerMask WhatIsGround;

// --- Permet le bon fonctionnement de la camèra ---
// --- (Supprime le joueur en double) ---
    private void Awake()
    {
        if(Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
        }
    }
    
// --- Mise en place des lien entre les composant unity et le programme --- 
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        pState = GetComponent<PlayerStateList>();
        gravity = rb.gravityScale;
    }

// --- Activation en boucle des fonctions ---
    void Update()
    {
        GetInputs();
        UpdateJumpVariables();

        if (pState.Dashing) return;
        Flip();
        Move();
        Jump();
        StartDash();
    }

// --- Récupérations des Inputs ---
    void GetInputs()
    {
        xAxis = Input.GetAxisRaw("Horizontal");
    }

// --- Permet de faire tourner le sprite en fonction de la direction ---
    void Flip()
    {
        if(xAxis < 0)
        {
            transform.localScale = new Vector2(-xScale, transform.localScale.y);
        }
        else if(xAxis > 0)
        {
            transform.localScale = new Vector2(xScale, transform.localScale.y);
        }
    }


// --- Fonctionnement du mouvement horizontal ---
    private void Move()
    {
        rb.linearVelocity = new Vector2(walkSpeed * xAxis, rb.linearVelocity.y);
        anim.SetBool("Walking", rb.linearVelocity.x != 0 && Grounded());
    }

// --- Détection du sol ---
    public bool Grounded()
    {
        if (Physics2D.Raycast(GroundCheckPoint.position, Vector2.down, groundCheckY, WhatIsGround) 
            || Physics2D.Raycast(GroundCheckPoint.position + new Vector3(groundCheckX, 0, 0) , Vector2.down, groundCheckY, WhatIsGround)
            || Physics2D.Raycast(GroundCheckPoint.position + new Vector3(-groundCheckX, 0, 0) , Vector2.down, groundCheckY, WhatIsGround))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

//--------------------------------------------------------------------
// --- Fonctionnement du saut ---
//--------------------------------------------------------------------
    void Jump()
    {
    // --- Saut de base ---
        if(Input.GetButtonUp("Jump") && rb.linearVelocity.y > 0)
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, 0);
            pState.Jumping = false;
        }

        if (!pState.Jumping)
        {
            if (jumpBufferCounter > 0 && coyoteTimeCounter > 0)
            {
                rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce);
                pState.Jumping = true;
            }
            // --- ajoute le double saut ---
            else if(!Grounded() && airJumpCounter < MaxAirJumps && Input.GetButtonDown("Jump"))
            {
                rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce);
                pState.Jumping = true;
                airJumpCounter ++;
            }
        }

        anim.SetBool("Jumping", !Grounded());
        
    }

// --- ajoute un temps pendant lequel l'input de saut est sauvegardé ---
// --- ajoute le coyote Time ---
    void UpdateJumpVariables()
    {
        if (Grounded())
        {
            pState.Jumping = false;
            coyoteTimeCounter = CoyoteTime;
            airJumpCounter = 0;
        }
        else
        {
            coyoteTimeCounter -= Time.deltaTime;
        }

        if (Input.GetButtonDown("Jump"))
        {
            jumpBufferCounter = jumpBufferFrame;
        }
        else
        {
            jumpBufferCounter --;
        }
    }

//--------------------------------------------------------------------
// --- Fonctionnement du dash ---
//--------------------------------------------------------------------
    void StartDash()
    {
        if(Input.GetButtonDown("DASH") && canDash && !dashed)
        {
            StartCoroutine(Dash());
            dashed = true;
        }

        if (Grounded())
        {
            dashed = false;
        }
    }

    IEnumerator Dash()
    {
        canDash = false;
        pState.Dashing = true;
        anim.SetTrigger("Dashing");
        rb.gravityScale = 0;
        rb.linearVelocity = new Vector2(transform.localScale.x * dashSpeed, 0);
        yield return new WaitForSeconds(dashTime);
        rb.gravityScale = gravity;
        pState.Dashing = false;
        yield return new WaitForSeconds(dashCooldown);
        canDash = true;
    }

    

}