using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
using TMPro;

public enum CurrentCommand { defend, moveForward, retreat }

// Left is Player, Enemy is right.
public enum Side { left = 1, right = -1 }

public class Unit : MonoBehaviour
{
    public string unitName;
    public CardData cardData;

    [SerializeField]
    private int basicAttack;
    private int finalAttack;

    [SerializeField]
    private int currentDefense;

    [SerializeField]
    private int basicMaxDefense;
    private int finalMaxDefense;

    [SerializeField]
    private CardTag[] cardTags;

    [SerializeField]
    private int attackRange = 1;
    [SerializeField]
    private int movement = 1;

    [SerializeField]
    private CurrentCommand currentCommand;

    public Cell standingCell;
    [SerializeField]
    private Cell destinationCell;
    private CellMap cellMap;

    public Side side;

    public Transform modelArt;
    public Transform attackText;
    public Transform defenseText;

    public bool walkingToDestination = false;


    // Start is called before the first frame update
    void Start()
    {
        cellMap = FindObjectOfType<CellMap>();
        transform.position = standingCell.transform.position;
        transform.position = new Vector3(transform.position.x, transform.position.y, -1);
        standingCell.standingUnit = this;

        basicAttack = cardData.attack;
        basicMaxDefense = cardData.defense;
        currentDefense = basicMaxDefense;
        cardTags = cardData.cardTags;

        foreach (var tag in cardData.cardTags)
        {
            if (tag.name == "Archer")
            {
                attackRange += 2;
            }
            else if (tag.name == "Cavalry")
            {
                movement += 1;
            }
        }

        modelArt.GetComponent<SpriteRenderer>().sprite = cardData.artwork;
    }

    // Update is called once per frame
    void Update()
    {
        if (walkingToDestination && destinationCell && transform.position != destinationCell.transform.position)
        {
            Vector3 moveVec = destinationCell.transform.position - transform.position;
            transform.position = new Vector3(transform.position.x + moveVec.x * 5.0f * Time.deltaTime, transform.position.y + moveVec.y * 5.0f * Time.deltaTime, -1);

            //Debug.Log("Manitude: " + moveVec.magnitude);

            if ((Mathf.Approximately(transform.position.x, destinationCell.transform.position.x)
                && Mathf.Approximately(transform.position.y, destinationCell.transform.position.y))
                || moveVec.magnitude <= 1)
            {
                walkingToDestination = false;
                transform.position = new Vector3(destinationCell.transform.position.x, destinationCell.transform.position.y, -1);
                destinationCell = null;
                //Debug.Log("Arrived.");
            }
        }

        finalAttack = basicAttack; // For now.

        attackText.GetComponent<TextMeshProUGUI>().text = finalAttack.ToString();
        defenseText.GetComponent<TextMeshProUGUI>().text = currentDefense.ToString();
    }

    public void MoveAndAttack()
    {
        MoveAction();
        AttackAction();
    }

    public void MoveAction()
    {
        if (walkingToDestination)
        {
            return;
        }
        foreach (var tag in cardData.cardTags)
        {
            if (tag.name == "Archer")
            {
                for (int i = 0; i < attackRange; ++i)
                {
                    int attackXPos = standingCell.xPos + (i + 1) * (int)side;
                    if (attackXPos >= cellMap.column || attackXPos < 0)
                    {
                        // Attack Camp
                        currentCommand = CurrentCommand.defend;
                        break;
                    }

                    Cell nextCellInRange = cellMap.GetCell(attackXPos, standingCell.yPos);
                    Unit targetUnit = nextCellInRange.standingUnit;
                    if (targetUnit)
                    {
                        if (targetUnit.side != side)
                        {
                            // Add units to attack list.
                            currentCommand = CurrentCommand.defend;
                            break;
                        }
                    }

                    currentCommand = CurrentCommand.moveForward;
                }
                break;
            }
        }

       
        //Debug.Log("Call MoveAction().");
        switch (currentCommand)
        {
            case CurrentCommand.defend:
                break;
            case CurrentCommand.moveForward:
                //Debug.Log("Try Move Forward.");

                for (int i = 0; i < movement; i++)
                {
                    if (checkDestinationCellEmpty())
                    {
                        // Attack()
                        //Debug.Log("Start Moving.");
                        MoveToCell(destinationCell);
                        walkingToDestination = true;
                    }
                    else
                    {
                        //Debug.Log("Can't Move.");
                        // Check the unit is facing the camp or unit.
                        if (destinationCell)
                        {
                            // Check the unit is Allie or Enemy.
                            if (destinationCell.standingUnit.side == side)
                            {
                                // Check the Allie is swappable or not.
                                if (!inDogFight(destinationCell.standingUnit))
                                {
                                    //standingCell = cell;
                                    //cell.standingUnit = this;
                                    destinationCell.standingUnit.walkingToDestination = true;
                                    standingCell.standingUnit = destinationCell.standingUnit;
                                    standingCell.standingUnit.standingCell = standingCell;
                                    standingCell.standingUnit.destinationCell = standingCell;

                                    standingCell = destinationCell;
                                    destinationCell.standingUnit = this;
                                    walkingToDestination = true;
                                }

                            }
                            else
                            {
                                // Can't move to cell this is standed by enemy.
                                break;
                            }
                        }
                        else
                        {
                            // Facing the camp.
                            break;
                        }
                        destinationCell = standingCell;
                    }
                }
                break;
            case CurrentCommand.retreat:
                break;
            default:
                break;
        }
    }

    public void AttackAction()
    {
        // Check Attack Range
        if (inDogFight(this))
        {
            foreach (var tag in cardData.cardTags)
            {
                if (tag.name == "Archer")
                {
                    // Archer can't dog fight.
                    return;
                }
            }
        }

        List<Unit> unitsInRange = new List<Unit>();
        for (int i = 0; i < attackRange; ++i)
        {
            int attackXPos = standingCell.xPos + (i+1) * (int)side;
            if (attackXPos >= cellMap.column || attackXPos < 0)
            {
                // Attack Camp
                return;
            }

            Cell nextCellInRange = cellMap.GetCell(attackXPos, standingCell.yPos);
            Unit targetUnit = nextCellInRange.standingUnit;
            if (targetUnit)
            {
                if (targetUnit.side != side)
                {
                    // Add units to attack list.
                    unitsInRange.Add(targetUnit);
                }
                else
                {
                    // It is allies. We don't attack it unless we are under some debuff.
                }
            }
            else
            {
                // Nothing to attack.
                //Debug.Log("No unit to attack.");
            }
        }


        if (side == Side.left)
        {
            unitsInRange = unitsInRange.OrderBy(x => x.standingCell.xPos).ToList<Unit>();
        }
        else
        {
            unitsInRange = unitsInRange.OrderByDescending(x => x.standingCell.xPos).ToList<Unit>();
        }

        for (int i = 0; i < unitsInRange.Count; i++)
        {
            Debug.Log(unitsInRange[i].unitName + ": " + unitsInRange[i].standingCell.xPos + " , " + unitsInRange[i].standingCell.yPos);
            // Do attack
            //Debug.Log("Attack!");
            unitsInRange[i].currentDefense -= finalAttack;
            if (unitsInRange[i].currentDefense <= 0)
            {
                unitsInRange[i].standingCell.standingUnit = null;
                unitsInRange[i].gameObject.SetActive(false); // For now
                Unit unit = unitsInRange[i];
                FindObjectOfType<UnitManager>().handleDiedUnit(unit);
                //Debug.Log("Defeat Enemy");
            }

            break; // Most of units can only attack one target.
        }
    }

    public bool isUnitAlive()
    {
        return currentDefense > 0;
    }

    bool checkDestinationCellEmpty(bool retreat = false)
    {
        int currentXPos = standingCell.xPos;
        int movePath = 0;

        if (retreat)
        {
            movePath = 1 * -(int)side;
        }
        else
        {
            movePath = 1 * (int)side;
        }

        int destinationXPos = standingCell.xPos + movePath;

        // Mean the destination cell is the Camp.
        if (!checkPosInMap(destinationXPos))
        {
            return false;
        }

        destinationCell = cellMap.GetCell(destinationXPos, standingCell.yPos);

        if (destinationCell.standingUnit == null)
        {
            return true;
        }
        else
        {
            // Check privous cell is standable if we have more than 1 movement ability.
            return false;
        }
    }

    void MoveToCell(Cell cell)
    {
        Assert.IsTrue(destinationCell.standingUnit == null, "Trying to move a cell that is already be standing by another unit.");

        standingCell.standingUnit = null;
        standingCell = cell;
        cell.standingUnit = this;
    }

    // Check the allies is near by an enemy or not. 
    bool inDogFight(Unit unit)
    {
        int checkPos = unit.standingCell.xPos + (int)side * 1;

        if (!checkPosInMap(checkPos))
        {
            return false;
        }

        var checkCell = cellMap.GetCell(checkPos, standingCell.yPos);
        if (checkCell.standingUnit && checkCell.standingUnit.side != side)
        {
            return true;
        }

        return false;
    }

    bool checkPosInMap(int pos)
    {
        return pos >= 0 && pos < cellMap.column;
    }
}
