using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Tilemaps;

public class CharacterController2D : MonoBehaviour
{
    public static CharacterController2D instance;

    // Character Attributes
    [Header("Character Attributes")]
    [SerializeField] private float walkingSpeed = 6;
    [SerializeField] private int cur_phase = 0;
    [SerializeField] private float max_mana = 7.5f;
    [SerializeField] private float cur_mana = 0;
    [SerializeField] private float mana_regen = 2f;

    private enum Action { ROOTING, PLANTING, ATTACK, NONE };
    private Action action = Action.NONE;

    [SerializeField] private GameObject carriedItem = null;
    [SerializeField] private GameObject plant_spot;
    [SerializeField] private ParticleSystem energyParticles;

    [Serializable] public struct Plant { public GameObject plant; public float cost; }
    [SerializeField] private Plant[] plants;
    private int cur_plant = 0;

    [Header("Attack Attributes")]
    [SerializeField] private float atk_damage = 0.5f;
    [SerializeField] private float atk_slow_duration = 5.0f;
    [SerializeField] private float atk_slow_ratio = 0.2f;
    [SerializeField] private float atk_cooldown = 3.0f;
    [SerializeField] private float atk_cost = 0.0f;
    private float atk_cooldown_cur = 0f;

    float TimerToVoice = 0f;
    bool UsingVoice = false;

    // External refs
    private Bar bar;
    private Grid grid;
    private Tilemap tilemap;
    private UI ui;
    private Animator animator;

    // Movement - Visibility
    private bool flipped = false;
    private Vector2 movement = Vector2.zero;
    private Vector2Int direction = Vector2Int.zero;
    private int[] dirRange = { -1, 0, 1 };
    private Vector2Int[] dirTranslate = {new Vector2Int(-1, -1), new Vector2Int(-1, 0), new Vector2Int(-1, 1), new Vector2Int(0, 1), new Vector2Int(1, 1),
                                      new Vector2Int(1, 0), new Vector2Int(1, -1), new Vector2Int(0, -1)};

    private Rigidbody2D rb2D;
    private SpriteRenderer spriteRenderer;
    private Camera cam;

    bool usingMouse = false;
    public bool CanDoActions = true;

    private void Awake()
    {
        instance = this;
        CanDoActions = true;

        rb2D = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        cam = FindObjectOfType<Camera>();

        grid = FindObjectOfType<Grid>();
        animator = GetComponent<Animator>();
        ui = FindObjectOfType<UI>();
        ui.Init(plants, atk_cost);

        plant_spot.GetComponent<SpriteRenderer>().sprite = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().sprite;
        plant_spot.GetComponent<SpriteRenderer>().color = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().color;
    }

    private void FixedUpdate()
    {
        spriteRenderer.flipX = !flipped;
        atk_cooldown_cur = Mathf.Max(atk_cooldown_cur - Time.fixedDeltaTime, 0f);

        ui.SetMana(cur_mana, max_mana);
        ui.SetCooldown(atk_cooldown_cur, atk_cooldown);

        if (UsingVoice)
        {
            TimerToVoice -= Time.deltaTime;
            if (TimerToVoice <= 0)
                UsingVoice = false;
        }

        switch (action)
        {
            case Action.ROOTING:
                //Debug.Log("rooting: " + cur_mana);
                cur_mana += Time.deltaTime * mana_regen;
                cur_mana = Math.Min(max_mana, cur_mana);
                return;
            case Action.PLANTING:
                Vector2Int dir = Vector2Int.zero;
                if (UsingVoice) { }
                if (usingMouse) // Mouse
                {
                    Vector2 mouse_dir = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
                    dir = ComputeDirection(mouse_dir.normalized);
                }
                else // Controller
                {
                    dir = ComputeDirection(movement);
                }

                Vector3Int closestCell = grid.WorldToCell(transform.position) + new Vector3Int(dir.x, dir.y, 0);
                plant_spot.transform.position = grid.GetCellCenterWorld(closestCell);
                return;
        }

        animator.SetFloat("Velocity", Vector2.Distance(movement, Vector2.zero));
        rb2D.MovePosition(rb2D.position + movement * walkingSpeed * Time.fixedDeltaTime);
    }

    private Vector2Int ComputeDirection(Vector3 movement)
    {
        Vector2Int dir = Vector2Int.zero;
        dir.x = dirRange.Select(p => new { Value = p, Difference = Math.Abs(p - movement.normalized.x) })
            .OrderBy(p => p.Difference)
            .First().Value;
        dir.y = dirRange.Select(p => new { Value = p, Difference = Math.Abs(p - movement.normalized.y) })
            .OrderBy(p => p.Difference)
            .First().Value;
        dir = dirTranslate[(Array.IndexOf(dirTranslate, dir) + 1) % dirTranslate.Length];
        return dir;
    }

    #region playerActions
    public void OnMove(InputAction.CallbackContext context)
    {
        //if (action == Action.PLANTING && context.phase != InputActionPhase.Started) return;
        movement = context.ReadValue<Vector2>();

        // Sprite direction
        if (movement.x < 0) flipped = true;
        else if (movement.x > 0) flipped = false;
    }

    public void OnRoot(InputAction.CallbackContext context)
    {
        if (!CanDoActions)
            return;

        // Plant cancel
        if (action == Action.PLANTING)
        {
            action = Action.NONE;
            plant_spot.SetActive(false);
            return;
        }
        if (action == Action.ATTACK) return;
        switch (context.phase)
        {
            case InputActionPhase.Started:
                FMODUnity.RuntimeManager.PlayOneShot("event:/Foley/ToRoot");
                action = Action.ROOTING;
                animator.SetBool("Root", true);
                energyParticles.enableEmission = true;
                break;
            case InputActionPhase.Performed:
                break;
            case InputActionPhase.Canceled:
                if (action != Action.ROOTING) return;
                action = Action.NONE;
                animator.SetBool("Root", false);
                energyParticles.enableEmission = false;
                break;
        }
    }

    public void OnPlant(InputAction.CallbackContext context)
    {
        if (!CanDoActions)
            return;

        if (action == Action.ROOTING || action == Action.ATTACK) return;
        switch (context.phase)
        {
            case InputActionPhase.Started:
                usingMouse = context.control.device.description.deviceClass == "Mouse";
                action = Action.PLANTING;
                animator.SetFloat("Velocity", 0f);
                plant_spot.SetActive(true);
                break;
            case InputActionPhase.Performed:
                break;
            case InputActionPhase.Canceled:
                if ((action != Action.PLANTING) || (plants.Length <= 0)) return;
                plant_spot.SetActive(false);

                // Check tile contents
                Collider2D [] cols = Physics2D.OverlapCircleAll(plant_spot.transform.position, 0.2f);
                foreach(Collider2D c in cols)
                {
                    if (c.CompareTag("Plant") || c.CompareTag("Enemy") || c.CompareTag("Tower") || c.CompareTag("Borders"))
                    {
                        //Debug.Log(c.tag);
                        action = Action.NONE;
                        return;
                    }
                }

                if (cur_mana >= plants[cur_plant].cost)
                {
                    cur_mana -= plants[cur_plant].cost;
                    GameObject.Instantiate(plants[cur_plant].plant, plant_spot.transform.position, Quaternion.identity).SetActive(true);
                }
                else
                {
                    if(!UsingVoice)
                    {
                        FMODUnity.RuntimeManager.PlayOneShot("event:/Voice/NoMana");
                        UsingVoice = true;
                        TimerToVoice = 1f;
                    }
                }
                action = Action.NONE;
                break;
        }

    }

    public void OnSwitchRight(InputAction.CallbackContext context)
    {
        if (action == Action.ROOTING || context.phase != InputActionPhase.Started) return;
        cur_plant = (cur_plant + 1) % plants.Length;

        plant_spot.GetComponent<SpriteRenderer>().sprite = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().sprite;
        plant_spot.GetComponent<SpriteRenderer>().color = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().color;

        ui.SelectPlant(cur_plant);
    }

    public void OnSwitchLeft(InputAction.CallbackContext context)
    {
        if (action == Action.ROOTING || context.phase != InputActionPhase.Started) return;
        cur_plant = (cur_plant - 1 + plants.Length) % plants.Length;

        plant_spot.GetComponent<SpriteRenderer>().sprite = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().sprite;
        plant_spot.GetComponent<SpriteRenderer>().color = plants[cur_plant].plant.GetComponentInChildren<SpriteRenderer>().color;

        ui.SelectPlant(cur_plant);
    }

    public void OnPick(InputAction.CallbackContext context)
    {
        if (context.phase != InputActionPhase.Started) return;
        if (carriedItem)
        {
            // Drop Item

            carriedItem = null;
        }
        else
        {
            // Pick Item if Nearby
        }

    }

    public void OnAttack(InputAction.CallbackContext context)
    {
        if (context.phase != InputActionPhase.Started || action == Action.PLANTING || action == Action.ROOTING || atk_cooldown_cur > 0f) return;
        if (cur_mana < atk_cost) 
        {
            if (!UsingVoice)
            {
                FMODUnity.RuntimeManager.PlayOneShot("event:/Voice/NoMana");
                UsingVoice = true;
                TimerToVoice = 1f;
            }
            return;
        }
        

        animator.SetBool("Attack", true);
        FMODUnity.RuntimeManager.PlayOneShot("event:/Voice/Jaah");


        action = Action.ATTACK;
        atk_cooldown_cur = atk_cooldown;
        cur_mana -= atk_cost;

        Vector3 lootAt = grid.GetCellCenterWorld(grid.WorldToCell(transform.position) + new Vector3Int(direction.x, direction.y, 0));

        Collider2D [] colliders = Physics2D.OverlapCircleAll(lootAt, grid.cellSize.x * 2);
        foreach(Collider2D col in colliders)
        {
            if (col.CompareTag("Enemy"))
            {
                col.GetComponent<Enemy>().SlowMovement(atk_slow_ratio, atk_slow_duration);
                col.GetComponent<TargetBase>().NotifySwing(atk_damage);
            }
        }

        action = Action.NONE;
    }
    #endregion

    void Footstep()
    {
        FMODUnity.RuntimeManager.PlayOneShot("event:/Foley/Footstep");
    }
}
