using UnityEngine;
using UnityEngine.Events;
using TMPro;
using System;
using UnityUtils;
using System.Collections;

public class HealthComponent : MonoBehaviour, ISquezeDamageReceiver
{
    public bool bloodRegen = false;
    public float regen = 1000;

    [SerializeField] private bool onlyCallZeroHealthOnce = true;
    [SerializeField] float maxHealth = 100;

    [SerializeField] float damageTickDelay = 0.25f;
    private float currentDamageTick = 0f;
    private float accumulatedDamageInTick = 0f;

    public float currentHealth {  get; private set; }

    public static event Action<Vector3, float> OnHealthChangeAtPos;

    public UnityEvent OnHealthZero;
    public UnityEvent<float, float> OnHealthChange;

    [Header("Squeze Damage")]
    [SerializeField]
    float minThreshold = 1f;

    [SerializeField]
    float squezeDamageScalor = 1f;

    bool alreadyReachedZero = false;

    public void resetKillFlag() {
        alreadyReachedZero = false;
    }

    void Awake()
    {
        currentHealth = maxHealth;
        OnHealthChange.AddListener((prev, nex) => showRedTint = nex<prev);
        StartCoroutine(RedTintLoop());
    }

    bool showRedTint = false;
    private IEnumerator RedTintLoop()
    {
        SpriteRenderer sr = GetComponentInChildren<SpriteRenderer>();
        while (true)
        {
            if (!showRedTint)
            {
                yield return new WaitForSecondsRealtime(0.1f);
                continue;
            }

            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.red;
            yield return new WaitForSecondsRealtime(0.1f);
            sr.color = Color.white;
            yield return new WaitForSecondsRealtime(0.1f);

            showRedTint = false;
        }
    }

    void Update()
    {
        // blod regen
        if (bloodRegen)
        {
            PlayerInput playerInput = GetComponent<PlayerInput>();
            float bloodAccumalted = playerInput.PlayerNum == 0 ? BloodComputeShader.Instance.mop1Clean : BloodComputeShader.Instance.mop2Clean;
            TakeDamage(-bloodAccumalted / regen);
        }

        if (currentDamageTick < Time.time)
        {
            if (accumulatedDamageInTick < 1f) return;

            OnHealthChangeAtPos?.Invoke(transform.position.Add(y: 2f), accumulatedDamageInTick);
            currentDamageTick = Time.time + damageTickDelay;
            accumulatedDamageInTick = 0f;
        }
    }

    public float getMaxHealth() {
        return maxHealth;
    }

    public void setMaxHealth(float amount, bool heal = false) {
        maxHealth = amount;

        if (heal)
        {
            currentHealth = amount;
            alreadyReachedZero = false;
        }
    }

    public void TakeDamage(float damage)
    {
        if (damage == 0f) return;

        currentHealth -= damage;
        currentHealth = Mathf.Clamp(currentHealth, 0f, getMaxHealth());
        OnHealthChange?.Invoke(currentHealth + damage, currentHealth);

        accumulatedDamageInTick += damage;

        if (currentHealth <= 0) {
            if (alreadyReachedZero && onlyCallZeroHealthOnce) return;

            alreadyReachedZero = true;
            // Make sure to flush accumulated when dying
            if (accumulatedDamageInTick > 1f)
                OnHealthChangeAtPos?.Invoke(transform.position.Add(y: 2f), accumulatedDamageInTick);

            OnHealthZero?.Invoke();

            if (BloodComputeShader.Instance != null)
            {
                int blood = (int)(maxHealth * 100.0f * BloodComputeShader.Instance.scoreMult);
                float power = 10.0f + (maxHealth / 25.0f);

                BloodComputeShader.Instance.createBlood(transform.position, blood / 2, power);
                BloodComputeShader.Instance.createBlood(transform.position, blood / 2, power);
            }
        }
    }

    public void TakeSquezeDamage(float squezeDamage)
    {
        if (squezeDamage < minThreshold) return;

        TakeDamage((int) Mathf.Round(squezeDamage * squezeDamageScalor));
    }

    public void EnemyKill()
    {

        Destroy(gameObject);
    }
}