using System.Collections;
using UnityEngine;

// PlayerController는 플레이어 캐릭터로서 Player 게임 오브젝트를 제어한다.
public class PlayerController : MonoBehaviour {
    public AudioClip deathClip; // 사망시 재생할 오디오 클립
    public float jumpForce = 700f; // 점프 힘
    public int maxJumpCount = 2; // 최대 점프 횟수
    public float invincibleDuration = 1.5f; // 피해를 입은 뒤 무적 시간

    private int jumpCount = 0; // 누적 점프 횟수
    private bool isGrounded = false; // 바닥에 닿았는지 나타냄
    private bool isDead = false; // 사망 상태
    private bool isSliding = false; // 슬라이딩 상태
    private bool isInvincible = false; // 피해 후 무적 상태

    private Rigidbody2D playerRigidbody; // 사용할 리지드바디 컴포넌트
    private Animator animator; // 사용할 애니메이터 컴포넌트
    private AudioSource playerAudio; // 사용할 오디오 소스 컴포넌트
    private SpriteRenderer spriteRenderer; // 플레이어 스프라이트 표시 컴포넌트

    private CapsuleCollider2D playerCollider; // 플레이어의 콜라이더 충돌 범위
    private Vector2 originalColliderSize; // 기본 자세일 때 콜라이더 크기
    private Vector2 originalColliderOffset; // 기본 자세일 때 콜라이더 중심 위치
    private CapsuleDirection2D originalColliderDirection; // 기본 자세일 때 콜라이더 방향
    public Vector2 slidingColliderSize; // 슬라이딩 자세일 때 콜라이더 크기
    public Vector2 slidingColliderOffset; // 슬라이딩 자세일 때 콜라이더 중심 위치
    public CapsuleDirection2D slidingColliderDirection = CapsuleDirection2D.Horizontal; // 슬라이딩 자세일 때 콜라이더 방향

    private void Start() {
        // 게임 오브젝트로부터 사용할 컴포넌트들을 가져와 변수에 할당
        playerRigidbody = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        playerAudio = GetComponent<AudioSource>();
        spriteRenderer = GetComponent<SpriteRenderer>();

        // 게임 콜라이더를 가져와서 현 상태를 저장
        playerCollider = GetComponent<CapsuleCollider2D>();
        originalColliderSize = playerCollider.size;
        originalColliderOffset = playerCollider.offset;
        originalColliderDirection = playerCollider.direction;
    }

    private void Update() {
        if(isDead)
        {
            return;
        }

        // 슬라이딩 중이 아니라면 최대 점프 횟수까지 점프할 수 있다.
        if(Input.GetMouseButtonDown(0) && jumpCount < maxJumpCount && !isSliding)
        {
            isGrounded = false;
            jumpCount++;

            // 점프 직전에 속도를 순간적으로 제로(0, 0)으로 변경
            playerRigidbody.linearVelocity = Vector2.zero;

            // 리지드바디에 위쪽으로 힘을 주기
            playerRigidbody.AddForce(new Vector2(0, jumpForce));
            playerAudio.Play();
        }
        else if(Input.GetMouseButtonUp(0) && playerRigidbody.linearVelocityY > 0)
        {
            // 마우스 왼쪽 버튼에서 손을 떼는 순간 && 속도의 y값이 양수라면(위로 상승중)
            // 현재 속도를 절반으로 변경
            playerRigidbody.linearVelocity = playerRigidbody.linearVelocity * 0.5f;
        }

        animator.SetBool("Grounded", isGrounded);

        // 달리는 중에 마우스 오른쪽 버튼을 누르고 있는 동안 슬라이딩 유지
        if(Input.GetMouseButton(1) && isGrounded && !isSliding)
        {
            StartSlide();
        }
        else if((!Input.GetMouseButton(1) || !isGrounded) && isSliding)
        {
            EndSlide();
        }
    }

    private void Die() {
        if(isDead)
        {
            return;
        }

        if(isSliding)
        {
            EndSlide();
        }

        StopAllCoroutines();
        spriteRenderer.color = Color.white;

        // 애니메이터의 Die 트리거 파라미터를 셋
        animator.SetTrigger("Die");

        playerAudio.clip = deathClip;
        playerAudio.Play();

        playerRigidbody.linearVelocity = Vector2.zero;

        isDead = true;

        GameManager.instance.OnPlayerDead();
    }

    private void OnTriggerEnter2D(Collider2D other) {
        if(other.CompareTag("Dead") && !isDead)
        {
            if(other.gameObject.name == "DeadZone")
            {
                Die();
                return;
            }

            TakeDamage();
        }
    }

    private void OnCollisionEnter2D(Collision2D collision) {
        // 어떤 콜라이더와 닿았으며, 충돌 표면이 위쪽을 보고 있으면
        if(collision.contacts[0].normal.y > 0.7f)
        {
            isGrounded = true;
            jumpCount = 0;
        }
    }

    private void OnCollisionExit2D(Collision2D collision) {
        // 바닥에서 벗어났음을 감지하는 처리
        isGrounded = false;

        if(isSliding)
        {
            EndSlide();
        }
    }

    private void StartSlide() {
        isSliding = true;
        animator.SetBool("Sliding", true);

        playerCollider.size = slidingColliderSize;
        playerCollider.offset = slidingColliderOffset;
        playerCollider.direction = slidingColliderDirection;
    }

    private void EndSlide() {
        isSliding = false;
        animator.SetBool("Sliding", false);

        playerCollider.size = originalColliderSize;
        playerCollider.offset = originalColliderOffset;
        playerCollider.direction = originalColliderDirection;
    }

    private void TakeDamage() {
        if(isInvincible)
        {
            return;
        }

        bool shouldDie = GameManager.instance.TakeDamage();

        if(shouldDie)
        {
            Die();
            return;
        }

        StartCoroutine(InvincibleRoutine());
    }

    private IEnumerator InvincibleRoutine() {
        isInvincible = true;

        float elapsedTime = 0f;
        float blinkInterval = 0.1f;

        while(elapsedTime < invincibleDuration)
        {
            spriteRenderer.color = new Color(1f, 1f, 1f, 0.35f);
            yield return new WaitForSeconds(blinkInterval);
            elapsedTime += blinkInterval;

            spriteRenderer.color = Color.white;
            yield return new WaitForSeconds(blinkInterval);
            elapsedTime += blinkInterval;
        }

        spriteRenderer.color = Color.white;
        isInvincible = false;
    }
}
