using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KnightOnHorse : Ally
{
    [SerializeField]
    private int _chargeAttackDamage;
    [SerializeField]
    private int _chargeCooldown;
    [SerializeField]
    private int _maxChargeHitCount;
    [SerializeField]
    private int _maxChargeDistance;
    [SerializeField]
    private float _fadeDistance;
    [SerializeField]
    private GameObject _detection;
    [SerializeField]
    private GameObject _chargeDetection;
    [SerializeField]
    private GameObject _root;

    // scripts
    private Root _rootScript;
    private Detection _detectionScript;

    // attack variables
    private int _originalAttackDamage;

    // movement variables
    private Vector3 _originalPos;
    private Vector2 _movementVector = Vector2.zero;

    // charge variables
    private bool _isCharging;
    private float _timeSinceLastCharge;
    private List<Entity> _opponentsHit = new List<Entity>();

    // fading variables
    private float _fadeProgress;
    private bool _isFading;
    private int _fadeState;

    public override void Start()
    {
        base.Start();

        _rootScript = _root.GetComponent<Root>();
        _detectionScript = _detection.GetComponent<Detection>();

        // keep originals to go back to them eventually
        _originalAttackDamage = AttackDamage;
        _originalPos = transform.position;

        // makes sure the entity charges on creation
        _timeSinceLastCharge = _chargeCooldown + 1;
    }

    public override void Update()
    {
        // fade if charge done
        if (_isFading)
        {
            HandleFading();
            return;
        }

        // check if charge is ready
        if (_timeSinceLastCharge > _chargeCooldown && !_isCharging)
        {
            AttackDamage = _chargeAttackDamage;

            SweepAttack();

            _isCharging = true;
            Animation.ToggleChargeAnim(true);
        }

        if (_isCharging)
        {
            // sweep attack if an enemy slips behind the tip's hitbox
            SweepAttack();

            // toggle charge detection
            _chargeDetection.SetActive(true);

            // movement
            HandleMovement();

            // attack
            if (IsEnemyDetected && !_opponentsHit.Contains(Enemy))
            {
                AttackEnemyRiding();
            }

            // reset
            if (transform.position.x - _originalPos.x >= _maxChargeDistance || _opponentsHit.Count >= _maxChargeHitCount)
            {
                // charge state
                _isCharging = false;
                _timeSinceLastCharge = 0;
                _opponentsHit.Clear();

                // detection state
                IsEnemyDetected = false;
                Enemy = null;
                _chargeDetection.SetActive(false);

                // attack damage
                AttackDamage = _originalAttackDamage;

                // start fading
                _isFading = true;
            }
        }
        else
        {
            // charge cooldown
            _timeSinceLastCharge += Time.deltaTime;

            // regular attack
            if (IsEnemyDetected)
            {
                AttackEnemy();
            }
        }
    }

    private void HandleMovement()
    {
        _movementVector.x = Time.deltaTime * Speed;
        transform.position += (Vector3)_movementVector;
    }

    private void AttackEnemy()
    {
        //Attack Cooldown
        if (AttackSpeedWait > AttackInterval)
        {
            Animation.PlayAttackAnim();

            AttackSpeedWait = 0f;
        }

        AttackSpeedWait += Time.deltaTime;
    }

    private void AttackEnemyRiding()
    {
        // call root attack cause no specific animation
        _rootScript.Attack();
        _opponentsHit.Add(Enemy);
    }

    /* 
     * Attacks all enemies already in the default detection box during charge.
     * Charge uses a different detection (at the tip) due to Enemy being the first opponent to enter the hitbox.
     * Therefore enemies would only get hit when the previous Enemy exits the hitbox, which is at the handle instead of the tip.
     * To cover for the enemies behind the charge detection which is at the tip, we hit them all as the charge starts 
     * and during in case something goes wrong and a monster slips through the charge hitbox.
    */
    private void SweepAttack()
    {
        foreach (Entity entity in _detectionScript.DetectedEntities)
        {
            if (!_opponentsHit.Contains(entity))
            {
                Enemy = entity;
                AttackEnemyRiding();
            }
        }
    }

    private void HandleFading()
    {
        if (_fadeState == 0 && _fadeProgress < _fadeDistance)
        {
            HandleMovement();

            //fade out
            _fadeProgress += _movementVector.x / _fadeDistance;
            foreach (SpriteRenderer spriteRenderer in SpriteRenderers)
            {
                spriteRenderer.color = new Color(1.0f, 1.0f, 1.0f, 1.0f - _fadeProgress);
            }
        }
        else if (_fadeState == 0 && _fadeProgress > _fadeDistance)
        {
            // teleport behind original position to fade in
            transform.position = new Vector3(_originalPos.x - _fadeDistance, _originalPos.y, _originalPos.z);
            _fadeState = 1;
        }
        else if (_fadeState == 1 && _fadeProgress < _fadeDistance * 2)
        {
            HandleMovement();

            // fade in
            _fadeProgress += _movementVector.x / _fadeDistance;
            foreach (SpriteRenderer spriteRenderer in SpriteRenderers)
            {
                spriteRenderer.color = new Color(1.0f, 1.0f, 1.0f, _fadeProgress - _fadeDistance);
            }
        }
        else
        {
            // disable fading
            _isFading = false;
            _fadeProgress = 0;
            _fadeState = 0;
            foreach (SpriteRenderer spriteRenderer in SpriteRenderers)
            {
                spriteRenderer.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            }

            // reset position
            _movementVector.x = 0;
            transform.position = _originalPos;

            // toggle detection
            _detection.SetActive(true);
            _chargeDetection.SetActive(false);

            // toggle animation
            Animation.ToggleChargeAnim(false);
        }
    }
}