using System.Collections;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using UnityEngine;

public class LDAIManager : MonoBehaviour
{
    private List<LDBaseCreatureAI> registeredAI;
    private List<LDChangeTileWorkerComponent> registeredBuilders;

    private Dictionary<EFaction, List<LDCombatComponent>> combatantLists;

    private List<LDGameActor> listLandmarks;

	// Use this for initialization
	void Start () 
    {
		
	}
	
	// Update is called once per frame
	void Update () 
    {
		
	}

    public void RegisterAI(LDBaseCreatureAI newAI)
    {
        if (registeredAI == null)
        {
            registeredAI = new List<LDBaseCreatureAI>();
        }
        if (!registeredAI.Contains(newAI))
        {
            registeredAI.Add(newAI);
        }
        if (combatantLists == null)
        {
            combatantLists = new Dictionary<EFaction, List<LDCombatComponent>>();
            for (EFaction t = 0; t < EFaction.MAX; t++)
            {
                combatantLists[t] = new List<LDCombatComponent>();
            }
        }

        LDCombatComponent cc = newAI.GetComponentInChildren<LDCombatComponent>();
        if (cc != null)
        {
            combatantLists[newAI.Faction].Add(cc);
        }

    }

    public void RegisterPlayer(LDGameActor p)
    {
        if (combatantLists == null)
        {
            combatantLists = new Dictionary<EFaction, List<LDCombatComponent>>();
            for (EFaction t = 0; t < EFaction.MAX; t++)
            {
                combatantLists[t] = new List<LDCombatComponent>();
            }
        }
        LDCombatComponent cc = p.GetComponentInChildren<LDCombatComponent>();
        if (cc != null)
        {
            combatantLists[p.Faction].Add(cc);
        }
        
    }

    public void RegisterAsBuilder(LDChangeTileWorkerComponent newAI)
    {
        if (registeredBuilders == null)
        {
            registeredBuilders = new List<LDChangeTileWorkerComponent>();
        }
        if (!registeredBuilders.Contains(newAI))
        {
            registeredBuilders.Add(newAI);
        }
    }

    public void RegisterLandmark(LDGameActor newLandmark)
    {
        if (listLandmarks == null)
        {
            listLandmarks = new List<LDGameActor>();
        }

        listLandmarks.Add(newLandmark);
    }

    public void RemoveFromLists(GameObject o)
    {
        if (registeredAI != null)
        {
            registeredAI.RemoveAll(a => a.gameObject == o);
        }

        if (registeredBuilders != null)
        {
            registeredBuilders.RemoveAll(a => a.gameObject == o);
        }

        if (combatantLists != null)
        {
            for (EFaction t = 0; t < EFaction.MAX; t++)
            {
                combatantLists[t].RemoveAll(a => a.gameObject == o);
            }
        }

        if (listLandmarks != null)
        {
            listLandmarks.RemoveAll(a => a.gameObject == o);
        }
    }

    public bool TileIsBusyForBuilding(int iTileID)
    {
        if (registeredBuilders == null)
        {
            return false;
        }

        for (int t = 0; t < registeredBuilders.Count; t++)
        {
            if (registeredBuilders[t].IsWorkingOnTile(iTileID))
            {
                return true;
            }
        }

        return false;
    }

    public void PrepareNewGame()
    {
        registeredBuilders = null;
        registeredAI = null;
        combatantLists = null;
        listLandmarks = null;
    }

    public LDCombatComponent GetClosestTarget(EFaction myFaction, float fRange, Vector2 vGamePosOrigin, bool bAllowNeutral)
    {
        List<LDCombatComponent> targets = GetTargetsInArea(50, myFaction, fRange, vGamePosOrigin, bAllowNeutral);
        if (targets == null || targets.Count == 0)
        {
            return null;
        }

        //find the closest
        LDCombatComponent closest = null;
        float fBestDist = 9999f;
        for (int t = 0; t < targets.Count; t++)
        {
            float fDistSquared = ((Vector2) (targets[t].myOwningActor.vWorldPos) - vGamePosOrigin).sqrMagnitude;
            if (fDistSquared < fBestDist)
            {
                fBestDist = fDistSquared;
                closest = targets[t];
            }
        }

        return closest;
    }

    public List<LDCombatComponent> GetTargetsInArea(int iMaxTargets, EFaction myFaction, float fRange, Vector2 vGamePosOrigin, bool bAllowNeutral)
    {
        return GetTargetsInArea(iMaxTargets, myFaction, fRange, vGamePosOrigin,bAllowNeutral, Vector2.zero, 0f);        
    }

    public List<LDCombatComponent> GetTargetsInArea(int iMaxTargets, EFaction myFaction, float fRange,  Vector2 vGamePosOrigin , bool bAllowNeutral, Vector2 vMyFacing, float fConeDegrees )
    {
        //look through opposite list first
        List<LDCombatComponent> oppositeList = myFaction == EFaction.player ? combatantLists[EFaction.enemy] : combatantLists[EFaction.player];
        //then look through neutral list
        List<LDCombatComponent> neutralList = combatantLists[EFaction.neutral];

        //Who's up first
        List<LDCombatComponent> firstTargets = GetTargetsFromList(oppositeList, iMaxTargets, fRange, vGamePosOrigin, vMyFacing, fConeDegrees);

        if (!bAllowNeutral || firstTargets.Count >= iMaxTargets)
        {
            return firstTargets;
        }

        //otherwise, check neutral too if we can
        List<LDCombatComponent> neutralTargets = GetTargetsFromList(neutralList, iMaxTargets - firstTargets.Count, fRange, vGamePosOrigin, vMyFacing, fConeDegrees);

        int iNeutralIdx = 0;
        for (int t = firstTargets.Count; t < iMaxTargets; t++)
        {
            if (iNeutralIdx < neutralTargets.Count)
            {
                firstTargets.Add(neutralTargets[iNeutralIdx]);
                iNeutralIdx++;
            }
        }

        return firstTargets;
    }

    List<LDCombatComponent> GetTargetsFromList(List<LDCombatComponent> targetList, int iMaxTargets,float fRange, Vector2 vWorldPosOrigin, Vector2 vMyFacing, float fConeDegrees)
    {
        List<LDCombatComponent> retList = new List<LDCombatComponent>();

        for (int t = 0; t < targetList.Count; t++)
        {
            LDCombatComponent cc = targetList[t];
            Vector2 vToTarget = ((Vector2) cc.myOwningActor.GetTargetingPosition() - vWorldPosOrigin);
            float fDistSq = vToTarget.sqrMagnitude;

            //Is it in range?
            if (fDistSq > fRange * fRange)
            {
                continue;
            }

            //is it ALREADY DEAD
            if (!cc.myOwningActor.CanBeTargeted())
            {
                continue;
            }

            //If we need to be looking at it, are we looking at it?
            if (vMyFacing != Vector2.zero)
            {
                Vector2 vToTargetNormalized = vToTarget.normalized;
                float fDot = Vector2.Dot(vMyFacing, vToTargetNormalized);

                //if we're not even within 180 degrees, no.
                if (fDot < 0)
                {
                    continue;
                }

                //Cosine of adjacent over hypotenuse should give angle? 
                float fAngle = Vector2.Angle(vMyFacing, vToTargetNormalized);

                //if outside the cone, no target
                if (fAngle > fConeDegrees)
                {
                    continue;
                }
            }

            //it's good!
            retList.Add(cc);
            if (retList.Count >= iMaxTargets)
            {
                return retList;
            }
        }

        return retList;
    }


    public bool IsPositionNearLandmark(Vector2 vCheckPos, float fMaxRange)
    {
        if (listLandmarks == null)
        {
            return true;
        }

        float fSqRange = fMaxRange * fMaxRange;

        for (int t = 0; t < listLandmarks.Count; t++)
        {
            LDGameActor ga = listLandmarks[t];
            if (ga == null)
            {
                continue;
            }

            if (((Vector2)ga.vWorldPos - vCheckPos).sqrMagnitude < fSqRange)
            {
                return true;
            }
        }

        return false;
    }

    public Vector2 GetRandomLandmarkLocation()
    {
        if (listLandmarks == null || listLandmarks.Count == 0)
        {
            return Vector2.zero;
        }

        LDGameActor r = listLandmarks[Random.Range(0, listLandmarks.Count)];
        return r.vWorldPos;
    }

    public LDGameActor GetNearestLandmark(Vector2 vOrigin)
    {
        if (listLandmarks == null || listLandmarks.Count == 0)
        {
            return null;
        }

        LDGameActor closest = null;
        float fBestDist = 9999f;

        for (int t = 0; t < listLandmarks.Count; t++)
        {
            LDGameActor r = listLandmarks[t];
            float fDelta = (r.GetTargetingPosition() - vOrigin).sqrMagnitude;
            if( fDelta < fBestDist )
            {
                fBestDist = fDelta;
                closest = r;
            }
        }
        return closest;
    }
}
