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

namespace TBTK{
	
	
	public enum _GridType{ SquareGrid, HexGrid, }
	public enum _GridColliderType{ Master, Individual, }
	
	
	[RequireComponent(typeof(GridGenerator))] [ExecuteInEditMode]
	public class GridManager : MonoBehaviour {
		
		#if UNITY_EDITOR
		public static bool inspector=false;
		#endif
		
		public bool generateGridOnStart=false;
		public bool generateUnitOnStart=false;
		public bool generateCollectibleOnStart=false;
		
		
		[Space(8)]
		public _GridType gridType;
		public static _GridType GetGridType(){ return instance.cacheGridType; }
		
		public static bool IsSquareGrid(){ return GetGridType()==_GridType.SquareGrid; }
		public static bool IsHexGrid(){ return GetGridType()==_GridType.HexGrid; }
		
		public _GridColliderType colliderType;
		public static bool UseMasterCollider(){ return instance.cacheColliderType==_GridColliderType.Master; }
		public static bool UseIndividualCollider(){ return instance.cacheColliderType==_GridColliderType.Individual; }
		
		
		public enum _RangeCalculation{ ByNode, ByDistance }
		public _RangeCalculation rangeCalculation;
		
		
		public bool enableDiagonalNeighbour=false;
		public static bool EnableDiagonalNeighbour(){ return instance.cacheDiagonalNeighbour; }
		
		
		public float nodeSize=1;
		//public static float GetBaseNodeSize(){ return instance.cacheNodeSize; }
		
		public float nodeSpacing=0.1f;
		public static float GetNodeSpacing(){ return instance.cacheNodeSpacing; }
		
		public static float GetNodeSize(bool addSpacing=true){ return instance._GetNodeSize(addSpacing); }
		public float _GetNodeSize(bool addSpacing=true){ 
			return cacheNodeSize * (IsHexGrid() ? 1.15f : 1) + (addSpacing ? cacheNodeSpacing : 0);
		}
		
		
		public static void ResetGrid(){
			for(int x=0; x<instance.grid.Count; x++){
				for(int z=0; z<instance.grid[x].Count; z++){
					instance.grid[x][z].ResetListState();
				}
			}
		}
		
		[Space(8)]
		public int dimensionX=50;		public static int DimensionX(){ return instance.cacheDimensionX; }
		public int dimensionZ=50;		public static int DimensionZ(){ return instance.cacheDimensionZ; }
		public float unwalkableRate=0.1f;
		public float obstacleRate=0.1f;
		public float wallRate=0.2f;
		
		[Space(8)]	//we need ListWrapper because unity will not serialize a list of list (grid)
		public List<ListWrapper> gridT =new List<ListWrapper>();
		public List<List<Node>> grid=new List<List<Node>>();		
		[HideInInspector] public float offsetX;
		[HideInInspector] public float offsetZ;
		
		public float GetOffsetX(){ 
			if(IsSquareGrid()) offsetX=DimensionX()*GetNodeSize()*.5f-GetNodeSize()*.5f;	
			if(IsHexGrid()) offsetX=DimensionX()*GetNodeSize()*GridGenerator.spaceXHex/2f-0.5f*GetNodeSize()*GridGenerator.spaceXHex;	
			return offsetX;
		}
		public float GetOffsetZ(){ 
			if(IsSquareGrid()) offsetZ=DimensionZ()*GetNodeSize()*.5f-nodeSize*.5f;	
			if(IsHexGrid()) offsetZ=DimensionZ()*GetNodeSize()*GridGenerator.spaceZHex/2f-0.5f*GetNodeSize()*GridGenerator.spaceZHex;	
			return offsetZ;
		}
		
		
		
		
		
		[HideInInspector] public _GridType cacheGridType;
		[HideInInspector] public _GridColliderType cacheColliderType;
		[HideInInspector] public bool cacheDiagonalNeighbour;
		[HideInInspector] public float cacheNodeSize;
		[HideInInspector] public float cacheNodeSpacing;
		[HideInInspector] public int cacheDimensionX;
		[HideInInspector] public int cacheDimensionZ;
		
		public void CacheGridSetting(){
			cacheGridType=gridType;
			cacheColliderType=colliderType;
			cacheDiagonalNeighbour=enableDiagonalNeighbour;
			cacheNodeSize=nodeSize;
			cacheNodeSpacing=nodeSpacing;
			cacheDimensionX=dimensionX;
			cacheDimensionZ=dimensionZ;
		}
		
		
		[Space(8)]	public GameObject masterColliderObj;
		public void SetupMasterCollider(){
			if(!UseMasterCollider()){
				if(masterColliderObj!=null) masterColliderObj.SetActive(false);
				return;
			}
			
			if(masterColliderObj==null){
				masterColliderObj=GameObject.CreatePrimitive(PrimitiveType.Quad);
				masterColliderObj.transform.parent=transform;
				masterColliderObj.transform.position=Vector3.zero;
				masterColliderObj.transform.rotation=Quaternion.Euler(90, 0, 0);
				masterColliderObj.name="Collider";	masterColliderObj.layer=30;
				masterColliderObj.GetComponent<MeshRenderer>().enabled=false;
			}
			
			masterColliderObj.transform.localScale=new Vector3(DimensionX()*GetNodeSize(),  DimensionZ()*GetNodeSize(), 1);
			masterColliderObj.SetActive(true);
		}
		
		
		public static List<Vector3> nodeDirList=new List<Vector3>();
		public static void InitNodeDirList(){
			nodeDirList=new List<Vector3>();
			if(GridManager.IsHexGrid()){
				nodeDirList.Add(new Vector3(.866f, 0, .5f));
				nodeDirList.Add(new Vector3(0, 0, 1));
				nodeDirList.Add(new Vector3(-.866f, 0, .5f));
				nodeDirList.Add(new Vector3(-.866f, 0, -.5f));
				nodeDirList.Add(new Vector3(0, 0, -1));
				nodeDirList.Add(new Vector3(.866f, 0, -.5f));
			}
			else if(GridManager.IsSquareGrid()){
				nodeDirList.Add(new Vector3(1, 0, 0));
				nodeDirList.Add(new Vector3(0, 0, 1));
				nodeDirList.Add(new Vector3(-1, 0, 0));
				nodeDirList.Add(new Vector3(0, 0, -1));
			}
		}
		
		
		
		public static void SetupFogOfWar(){ instance._SetupFogOfWar(); }
		public void _SetupFogOfWar(){
			if(!GameControl.EnableFogOfWar()) return;
			
			List<Unit> unitList=new List<Unit>( UnitManager.GetAllUnits() );
			for(int i=0; i<unitList.Count; i++){
				if(unitList[i].playableUnit) continue;
				unitList.RemoveAt(i);	i-=1;
			}
			
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					bool visible=false;
					for(int i=0; i<unitList.Count; i++){
						visible=CheckLOS(unitList[i].node, grid[x][z], unitList[i].GetSight());
						if(visible) break;
					}
					grid[x][z].SetVisible(visible);
				}
			}
			
			GridIndicator.ShowFog();
		}
		public static bool CheckLOS(Node node1, Node node2, int sight, bool debugging=false){
			if(debugging) Debug.Log("CheckLOS  "+node1.GetPos()+"  "+node2.GetPos()+"   "+GridManager.GetDistance(node1, node2));
			
			if(GridManager.GetDistance(node1, node2)>sight) return false;
			
			if(debugging) Debug.Log("  ___ CheckLOS  ");
			
			List<Node> nList1=new List<Node>{ node1 };
			List<Node> nList2=new List<Node>{ node2 };

			if(GameControl.EnableSideStepping()){
				if(GridManager.IsSquareGrid()){
					nList1.AddRange(GetSideStepNeighbours(node1, node2));
					nList2.AddRange(GetSideStepNeighbours(node2, node1));
				}
				else{
					nList1.AddRange(node1.GetNeighbourList(true));		//n1 is the origin node, 
					//nList2.AddRange(node2.GetNeighbourList(true));
				}
			}

			if(debugging) Debug.Log("  ___ CheckLOS  "+nList1.Count+"   "+nList2.Count);
			
			for(int i=0; i<nList1.Count; i++){
				for(int n=0; n<nList2.Count; n++){
					if(GridManager.GetDistance(nList1[i], nList2[n])>sight) continue;
					bool visible=!LOSCast(nList1[i].GetPos(), nList2[n].GetPos());
					if(debugging) Debug.DrawLine(nList1[i].GetPos(), nList2[n].GetPos(), visible ? Color.white : Color.red, 1);
					if(visible) return true;
				}
			}
			
			if(debugging) Debug.Log("  ___ CheckLOS   pass all     false");
			
			return false;
		}
		public static bool LOSCast(Vector3 pos1, Vector3 pos2, bool debugging=false){
			InitMask();
			return Physics.Linecast(pos1+new Vector3(0, .1f, 0), pos2+new Vector3(0, .1f, 0), losMask);
		}
		private static LayerMask losMask;		public static bool initMask;
		public static void InitMask(){
			if(initMask) return;
			initMask=true;
			losMask=1<<TBTK.GetLayerObsFullCover();
		}
		
		public static List<Node> GetSideStepNeighbours(Node srcNode, Node tgtNode){
			List<Node> list=new List<Node>();
			Node n1=GetSideStepNeighbour(srcNode, tgtNode, 1);		if(n1!=null) list.Add(n1);
			Node n2=GetSideStepNeighbour(srcNode, tgtNode, -1);		if(n2!=null) list.Add(n2);
			return list;
		}
		public static Node GetSideStepNeighbour(Node srcNode, Node tgtNode, float mul){
			Vector3 dir=(tgtNode.GetPos()-srcNode.GetPos()).normalized;
			Vector3 dirP=new Vector3(dir.z, 0, -dir.x);
			
			Node neighbour=srcNode.GetNeighbourFromPos(srcNode.GetPos()+(mul*dirP)*GridManager.GetNodeSize());
			if(neighbour!=null && !neighbour.IsBlocked(srcNode)) return neighbour;
			
			return null;
		}
		
		public static void SetupFogOfWarForDeployment(List<Node> dNodeList, int factionID){
			if(!GameControl.EnableFogOfWar()) return;
			
			//Faction fac=UnitManager.GetFaction(UnitManager.GetDeployingFacIdx());
			//List<Node> dNodeList=GetDeploymentNode(deployFacID);
			
			List<Node> visibleList=new List<Node>();
			
			for(int x=0; x<instance.grid.Count; x++){
				for(int z=0; z<instance.grid[x].Count; z++){
					Node node=instance.grid[x][z];
					
					//Node node=instance.grid[x][z];
					//node.SetVisible((node.unit!=null && node.unit.facID==factionID) || dNodeList.Contains(node));
					
					if(dNodeList.Contains(node) || (node.unit!=null && node.unit.facID==factionID)){
						List<Node> neighbours=node.GetNeighbourList();
						for(int n=0; n<neighbours.Count; n++){
							neighbours[n].SetVisible(true);
							if(!visibleList.Contains(neighbours[n])) visibleList.Add(neighbours[n]);
						}
						
						node.SetVisible(true);
						continue;
					}
					else node.SetVisible(visibleList.Contains(node));
				}
			}
		}
		
		private List<Node> scannedNodeList=new List<Node>();		//list of node under the effect of scan-fog-of-war ability
		public static void AddScannedNode(Node node){ instance.scannedNodeList.Add(node); }
		public static void EndTurn(){ 
			for(int i=0; i<instance.scannedNodeList.Count; i++){
				if(instance.scannedNodeList[i].IterateFogOfWarCD()){
					instance.scannedNodeList.RemoveAt(i);	i-=1;
				}
			}
		}
		
		//public bool combineGridMesh;
		//public static bool CombineGridMesh(){ return instance.combineGridMesh; }
		
		private GridGenerator gridGenerator;
		
		private static GridManager instance;
		public static GridManager GetInstance(){ return instance; }
		public void SetupInstance(){ instance=this; }	//called by GridGenerator
		
		public static void Init(){
			if(instance==null) instance=(GridManager)FindObjectOfType(typeof(GridManager));
			
			instance.gridGenerator=instance.transform.GetComponent<GridGenerator>();
			instance.gridGenerator.Init();
			instance.InitGrid();
			
			if(Application.isPlaying){// && instance.combineGridMesh){
				CombineMeshes cm=instance.gridGenerator.gridParent.GetComponent<CombineMeshes>();
				if(cm!=null) cm.Combine();
			}
			
			GridIndicator indicator=(GridIndicator)FindObjectOfType(typeof(GridIndicator));
			indicator.Awake();
		}
		
		void InitGrid(){
			InitNodeDirList();
			
			for(int i=0; i<gridT.Count; i++) grid.Add(gridT[i].list);
			if(Application.isPlaying) gridT.Clear();
			
			if(Application.isPlaying && generateGridOnStart) gridGenerator.Generate();
			
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					if(grid[x][z].unit!=null) grid[x][z].unit.node=grid[x][z];
					//if(grid[x][z].collectible!=null) grid[x][z].collectible.node=grid[x][z];
				}
			}
			
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					grid[x][z].SetupNeighbour();
				}
			}
			
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					grid[x][z].SetupWall();
				}
			}
			
			if(Application.isPlaying){
				if(generateUnitOnStart) gameObject.GetComponent<GridGenerator>().GenerateUnit();
				if(generateCollectibleOnStart) gameObject.GetComponent<GridGenerator>().GenerateCollectible();
			}
			
			if(Application.isPlaying && GameControl.EnableCoverSystem()) instance.StartCoroutine(InitGrid_Delayed());
		}
		private IEnumerator InitGrid_Delayed(){
			yield return null;
			
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					grid[x][z].InitCover();
				}
			}
		}
		
		
		
		void Update(){
			if(instance==null && !Application.isPlaying){
				instance=this;
				InitGrid();
			}
		}
		
		
		
		public static List<List<Node>> GetGrid(){ return instance.grid; }
		public static int GetNodeCount(){ return instance.dimensionX * instance.dimensionZ ; }
		
		public static Node GetNode(int x, int z){ 
			if(x<0 || x>=instance.grid.Count) return null;
			if(z<0 || z>=instance.grid[x].Count) return null;
			
			return instance.grid[x][z];
		}
		public static Node GetNode(Vector3 point, GameObject obj){
			if(UseIndividualCollider()){
				for(int x=0; x<instance.grid.Count; x++){
					for(int z=0; z<instance.grid[x].Count; z++){
						if(instance.grid[x][z].GetObjT().gameObject==obj) return instance.grid[x][z];
					}
				}
			}
			
			if(IsSquareGrid()){
				Vector2 coor=new Vector2(point.x+instance.offsetX, point.z+instance.offsetZ)/GetNodeSize();
				return GetNode((int)Mathf.Round(coor.x), (int)Mathf.Round(coor.y));
			}
			else{
				Vector2 coor=new Vector2(point.x+instance.offsetX, point.z+instance.offsetZ)/GetNodeSize();
				int x=(int)Mathf.Round(coor.x/GridGenerator.spaceXHex);
				coor.x/=GridGenerator.spaceXHex;
				
				float offset=(x%2==1 ? 0 : GridGenerator.spaceZHex*GetNodeSize()*0.5f);
				coor.y=(coor.y)/GridGenerator.spaceZHex-offset*0.5f;		
				//Debug.Log(coor.x.ToString("f2")+"   "+coor.y.ToString("f2")+"    "+offset);
				
				return GetNode((int)Mathf.Round(coor.x), (int)Mathf.Round(coor.y));
			}
		}
		
		
		public static int GetDistance(Node n1, Node n2, bool searchAStar=false){
			if(!searchAStar){
				if(IsSquareGrid()){
					if(EnableDiagonalNeighbour()){
						float deltaX=Mathf.Abs(n1.idxX-n2.idxX);
						float deltaZ=Mathf.Abs(n1.idxZ-n2.idxZ);
						return (int)Mathf.Max(deltaX, deltaZ);
					}
					return (int)Mathf.Abs(n1.idxX-n2.idxX)+(int)Mathf.Abs(n1.idxZ-n2.idxZ);
				}
				else{
					float x=Mathf.Abs(n1.x-n2.x);
					float y=Mathf.Abs(n1.y-n2.y);
					float z=Mathf.Abs(n1.z-n2.z);
					return (int)((x + y + z)/2);
				}
			}
			else return AStar.SearchWalkableNode(n1, n2).Count;
		}
		
		public static float GetAngle(Node n1, Node n2, bool round){ return GetAngle(n1.GetPos(), n2.GetPos(), round); }
		public static float GetAngle(Vector3 p1, Vector3 p2, bool round){
			float angle=Utility.Vector2ToAngle(new Vector2(p2.x-p1.x, p2.z-p1.z).normalized);
			if(round){
				if(IsSquareGrid()) angle=Utility.RoundAngleTo90(angle);
				else if(IsHexGrid()) angle=Utility.RoundAngleTo60(angle);
			}
			return angle;
		}
		
		
		public static List<Node> GetDeploymentNode(int facID){ return instance._GetDeploymentNode(facID); }
		public List<Node> _GetDeploymentNode(int facID){
			List<Node> list=new List<Node>();
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					if(!grid[x][z].walkable || !grid[x][z].IsEmpty()) continue;
					if(grid[x][z].deployFacID==facID) list.Add(grid[x][z]);
				}
			}
			return list;
		}
		
		
		public List<Node> walkableList=new List<Node>();
		public List<Node> attackableList=new List<Node>();
		public static List<Node> GetWalkableList(){ return instance.walkableList; }
		public static List<Node> GetAttackableList(){ return instance.attackableList; }
		
		public static void SelectUnit(Unit unit){ SetupWalkableList(unit);	SetupAttackableList(unit);	}
		public static List<Node> SetupWalkableList(Unit unit){
			instance.walkableList.Clear();
			if(!unit.CanMove()) return null;
			
			//instance.walkableList=GetNodesWithinDistance(unit.node, unit.GetMoveRange(), true, unit.canMovePastUnit, unit.canMovePastObs);
			instance.walkableList=GetNodesWithinDistance(unit.node, unit.GetMoveRange(), true, AStar.BypassUnitCode(unit), unit.canMovePastObs);
			
			for(int i=0; i<instance.walkableList.Count; i++){
				if(instance.walkableList[i].unit!=null || instance.walkableList[i].HasObstacle()){
					instance.walkableList.RemoveAt(i); i-=1;
				}
			}
			
			return instance.walkableList;
		}
		public static List<Node> SetupAttackableList(Unit unit){
			instance.attackableList=GetAttackableList(unit, unit.node);
			return instance.attackableList;
			//instance.attackableList.Clear();
			//List<Node> list=GetNodesWithinDistance(unit.node, unit.GetAttackRange(), false);
			//for(int i=0; i<list.Count; i++){
			//	if(list[i].unit!=null && list[i].unit.facID!=unit.facID) instance.attackableList.Add(list[i]);
			//}
		}
		public static void ClearSelectUnit(){
			GridIndicator.HideAll();
			instance.walkableList=new List<Node>();	instance.attackableList=new List<Node>();
		}
		
		public static List<Node> GetAttackableList(Unit unit, Node tgtNode){
			if(!unit.CanAttack()) return new List<Node>();
			
			List<Node> list=GetNodesWithinDistance(tgtNode, unit.GetAttackRange(), false);
			List<Node> tgtList=new List<Node>();
			
			float minAttackRange=unit.GetAttackRangeMin();
			float meleeAttackRange=unit.hasMeleeAttack ? unit.GetAttackRangeMelee() : 0;
			
			for(int i=0; i<list.Count; i++){
				if(list[i].unit==null) continue;
				if(!list[i].IsVisible()) continue; 
				if(OnSameFac(list[i].unit, unit)) continue;
				
				if(minAttackRange>0){
					int dist=GridManager.GetDistance(list[i].unit.node, unit.node);
					if(meleeAttackRange<=0){
						if(minAttackRange>0 && dist<minAttackRange) continue;
					}
					else{
						if(dist>meleeAttackRange && dist<minAttackRange) continue;
					}
				}
				
				if(unit.requireLOSToAttack && !CheckLOS(tgtNode, list[i], unit.GetSight())) continue;
				
				tgtList.Add(list[i]);
			}
			return tgtList;
		}
		
		
		public static bool CanMoveTo(Node node){ return instance.walkableList!=null && instance.walkableList.Contains(node); }
		public static bool CanAttack(Node node){ return node.unit!=null && instance.attackableList!=null && instance.attackableList.Contains(node); }
		
		public static void PreviewAttackableNode(Node node){ instance._PreviewAttackableNode(node); }
		public void _PreviewAttackableNode(Node node){
			bool preview=node!=null && GridManager.CanMoveTo(node);
			preview&=(UnitManager.GetSelectedUnit()!=null && UnitManager.GetSelectedUnit().CanAttack());
			
			if(preview) GridIndicator.PreviewHostile(GetAttackableList(UnitManager.GetSelectedUnit(), node));
			else GridIndicator.ClearPreviewHostile();
		}
		
		
		
		public List<Node> abilityTargetList=new List<Node>();
		public static bool InAbilityTargetList(Node node){ return instance.abilityTargetList.Contains(node); }
		
		public static void SetupAbilityTargetList(Faction fac, Ability ability){
			List<Node> list=new List<Node>();
			for(int x=0; x<instance.grid.Count; x++) list.AddRange(instance.grid[x]);
			SetupAbilityTargetList(ability, list, fac.factionID, null);
		}
		public static void SetupAbilityTargetList(Unit unit, Ability ability){
			//Debug.Log("SetupAbilityTargetList   "+ability.TargetStraightLineOnly());
			
			if(ability.TargetCone()){
				return;
			}
			if(ability.TargetStraightLineOnly()){
				instance.abilityTargetList.Clear();
				
				for(int i=0; i<(IsHexGrid() ? 6 : 4); i++){
					float angle=IsHexGrid() ? (i*60)+30 : i*90 ;
					List<Node> list=GetNodesInALine(unit.node, angle, ability.GetRange());	
					
					for(int n=0; n<list.Count; n++) list[n].abLineParent=list[list.Count-1];
					
					if(ability.type==Ability._AbilityType.Charge){
						for(int n=0; n<list.Count; n++){
							Node prevNode=n==0 ? unit.node : list[n-1] ;
							
							if(ability.targetType==Ability._TargetType.EmptyNode){
								if(list[n].IsBlocked(prevNode)) break;
								instance.abilityTargetList.Add(list[n]);
							}
							else if(ability.targetType==Ability._TargetType.AllNode){
								if(list[n].IsBlocked(prevNode, AStar.BypassUnitCode(true))) break;
								instance.abilityTargetList.Add(list[n]);
								if(list[n].unit!=null) break;
							}
							else{
								if(list[n].IsBlocked(prevNode, AStar.BypassUnitCode(true))) break;
								if(list[n].unit==null) continue;
								if(ability.targetType==Ability._TargetType.AllUnit) instance.abilityTargetList.Add(list[n]);
								else if(ability.targetType==Ability._TargetType.HostileUnit && !OnSameFac(list[n].unit, unit)) instance.abilityTargetList.Add(list[n]);
								else if(ability.targetType==Ability._TargetType.FriendlyUnit && OnSameFac(list[n].unit, unit)) instance.abilityTargetList.Add(list[n]);
								break;
							}
						}
					}
					else instance.abilityTargetList.AddRange(list);
				}
				
				GridIndicator.ShowAbility(instance.abilityTargetList);
				return;
			}
			
			List<Node> nodeList=GetNodesWithinDistance(unit.node, ability.GetRange());
			SetupAbilityTargetList(ability, nodeList, unit.GetFacID(), unit.node);
		}
		public static void SetupAbilityTargetList(Ability ability, List<Node> list, int facID, Node origin){
			instance.abilityTargetList.Clear();
			
			int rangeMin=ability.GetRangeMin();
			if(ability.isUnitAbility && rangeMin>0){
				for(int i=0; i<list.Count; i++){
					if(GetDistance(origin, list[i])<rangeMin){ list.RemoveAt(i); i-=1; }
				}
			}
			
			if(ability.isUnitAbility && ability.requireLos && origin.unit!=null){
				for(int i=0; i<list.Count; i++){
					if(!CheckLOS(origin, list[i], origin.unit.GetSight())){ list.RemoveAt(i); i-=1; }
				}
			}
			
			if(ability.targetType==Ability._TargetType.AllNode){
				instance.abilityTargetList=list;
			}
			else if(ability.targetType==Ability._TargetType.AllUnit){
				for(int i=0; i<list.Count; i++){
					if(list[i].unit!=null) instance.abilityTargetList.Add(list[i]);
				}
			}
			else if(ability.targetType==Ability._TargetType.HostileUnit){
				for(int i=0; i<list.Count; i++){
					if(list[i].unit!=null && list[i].unit.GetFacID()!=facID) instance.abilityTargetList.Add(list[i]);
				}
			}
			else if(ability.targetType==Ability._TargetType.FriendlyUnit){
				for(int i=0; i<list.Count; i++){
					if(list[i].unit!=null && list[i].unit.GetFacID()==facID) instance.abilityTargetList.Add(list[i]);
				}
			}
			else if(ability.targetType==Ability._TargetType.EmptyNode){
				for(int i=0; i<list.Count; i++){
					if(list[i].unit==null) instance.abilityTargetList.Add(list[i]);
				}
			}
			
			if(!ability.isUnitAbility && ability.targetType==Ability._TargetType.AllNode){
				GridIndicator.ShowAbility(new List<Node>());
			}
			else GridIndicator.ShowAbility(instance.abilityTargetList);
		}
		public static void ClearAbilityTargetList(bool resetIndicator){ 
			GridIndicator.HideAbility();
			instance.abilityTargetList.Clear();
			
			if(resetIndicator){
				GridIndicator.ShowMovable(GetWalkableList());
				GridIndicator.ShowHostile(GetAttackableList());
			}
		}
		
		public static List<Node> GetTargetNodeForNonTargetingFAbility(Ability ability){
			List<Node> list=new List<Node>();
			List<Unit> uList=UnitManager.GetAllUnitList();
			
			//if(ability.targetType==Ability._TargetType.AllNode){
			//	for(int x=0; x<instance.grid.Count; x++) list.AddRange(instance.grid[x]);
			//}
			if(ability.targetType==Ability._TargetType.AllUnit){
				for(int i=0; i<uList.Count; i++) list.Add(uList[i].node);
			}
			else if(ability.targetType==Ability._TargetType.HostileUnit){
				for(int i=0; i<uList.Count; i++){ if(uList[i].GetFacID()!=ability.facID) list.Add(uList[i].node); }
			}
			else if(ability.targetType==Ability._TargetType.FriendlyUnit){
				for(int i=0; i<uList.Count; i++){ if(uList[i].GetFacID()==ability.facID) list.Add(uList[i].node); }
			}
			//else if(ability.targetType==Ability._TargetType.EmptyNode){
			//	for(int x=0; x<instance.grid.Count; x++){
			//		for(int z=0; z<instance.grid[x].Count; z++){
			//			if(instance.grid[x][z].unit==null) list.Add(instance.grid[x][z]);
			//		}
			//	}
			//}
			
			return list;
		}
		
		
		
		
		private List<Node> testNodeList=new List<Node>();
		void OnDrawGizmos22(){
			Gizmos.color=Color.grey;
			for(int i=0; i<testNodeList.Count; i++) Gizmos.DrawSphere(testNodeList[i].GetPos(), 0.4f);
		}
		
		public static List<Node> GetSurroundingNodes(Node srcNode, int range, bool walkableOnly=true){
			List<Node> tgtList=GetNodesWithinDistance(srcNode, range, walkableOnly);
			List<Node> exemptList=GetNodesWithinDistance(srcNode, range-1, walkableOnly);
			
			for(int i=0; i<exemptList.Count; i++) tgtList.Remove(exemptList[i]);
			
			return tgtList;
		}
		public static List<Node> GetNodesInACircle(Node srcNode, int range, bool walkableOnly=true){
			//int range=(int)Mathf.Ceil(dist/GetNodeSize());
			float dist=range*GetNodeSize();
			List<Node> tgtList=GetNodesWithinDistance(srcNode, range, walkableOnly);
			
			//instance.testNodeList=new List<Node>( tgtList );
			
			for(int i=0; i<tgtList.Count; i++){
				if(Vector3.Distance(srcNode.GetPos(), tgtList[i].GetPos())>dist){ tgtList.RemoveAt(i); i-=1; }
			}
			return tgtList;
		}
		public static List<Node> GetNodesInACone(Node srcNode, Node tgtNode, int range, int fov, bool walkableOnly=false){
			List<Node> tgtList=new List<Node>();
			
			Vector3 v=( tgtNode.GetPos() - srcNode.GetPos() ).normalized;
			float baseAngle=Utility.Vector2ToAngle(new Vector2(v.x, v.z));
			
			//~ int range=(int)Mathf.Ceil(dist/GetNodeSize());
			List<Node> nodeList=GetNodesWithinDistance(srcNode, range, true, true, true);
			//~ List<Node> nodeList=GetNodesInACircle(srcNode, range, walkableOnly);
			
			//instance.testNodeList=nodeList;
			
			for(int i=0; i<nodeList.Count; i++){
				//if(Vector3.Distance(srcNode.GetPos(), nodeList[i].GetPos())>dist) continue;
				if(!nodeList[i].walkable) continue;
				if(nodeList[i].obstacleT!=null) continue;
				if(walkableOnly && nodeList[i].unit!=null) continue;
				
				float angle=GetAngle(srcNode, nodeList[i], false);//Utility.Vector2ToAngle(new Vector2(vv.x, vv.z));
				
				float angleDiff=Mathf.Abs(angle-baseAngle);
				if(angleDiff>180) angleDiff=Mathf.Abs(angleDiff-360);
				
				if(angleDiff<=fov*0.5f){
					tgtList.Add(nodeList[i]);
					//Debug.DrawLine(srcNode.GetPos(), nodeList[i].GetPos(), Color.green, 0.2f);
				}
				//else Debug.DrawLine(srcNode.GetPos(), nodeList[i].GetPos(), Color.red, 0.2f);
			}
			
			return tgtList;
		}
		
		public static List<Node> GetNodesInALine(Node srcNode, float angle, int dist, bool walkableOnly=false){
			List<Node> list=new List<Node>();
			Node curNode=srcNode;
			for(int i=0; i<dist; i++){
				List<Node> neighbourList=curNode.GetNeighbourList(walkableOnly);
				for(int n=0; n<neighbourList.Count; n++){
					if(Mathf.Abs(GetAngle(neighbourList[n], curNode, true)-angle)>1) continue;
					curNode=neighbourList[n];
					list.Add(curNode);
					break;
				}
			}
			//Debug.Log("GetNodesInALine  "+list.Count);
			//if(list.Count>3) Debug.Log(list[0].objT+" "+list[1].objT+" "+list[2].objT+" "+list[3].objT+" ");
			return list;
		}
		
		public static List<Node> GetNodesWithinDistance(Node srcNode, int dist, bool walkableOnly=false, bool allowUnit=false, bool allowObs=false){
			return GetNodesWithinDistance(srcNode, dist, walkableOnly, AStar.BypassUnitCode(allowUnit), allowObs);
		}
		public static List<Node> GetNodesWithinDistance(Node srcNode, int dist, bool walkableOnly, int allowUnit, bool allowObs){
			List<Node> neighbourList=srcNode.GetNeighbourList(walkableOnly, allowUnit, allowObs);
			
			List<Node> closeList=new List<Node>();
			List<Node> openList=new List<Node>();
			List<Node> newOpenList=new List<Node>();
			
			for(int m=0; m<neighbourList.Count; m++){
				Node neighbour=neighbourList[m];
				if(!newOpenList.Contains(neighbour)) newOpenList.Add(neighbour);
			}
			
			for(int i=0; i<dist; i++){
				openList=newOpenList;
				newOpenList=new List<Node>();
				
				for(int n=0; n<openList.Count; n++){
					neighbourList=openList[n].GetNeighbourList(walkableOnly, allowUnit, allowObs);
					for(int m=0; m<neighbourList.Count; m++){
						Node neighbour=neighbourList[m];
						if(!closeList.Contains(neighbour) && !openList.Contains(neighbour) && !newOpenList.Contains(neighbour)){
							newOpenList.Add(neighbour);
						}
					}
				}
				
				for(int n=0; n<openList.Count; n++){
					Node tile=openList[n];
					if(tile!=srcNode && !closeList.Contains(tile)){
						closeList.Add(tile);
					}
				}
			}
			
			
			if(IsSquareGrid() && EnableDiagonalNeighbour() && instance.rangeCalculation==_RangeCalculation.ByDistance){
				float convertedDist=dist*GetNodeSize()+GetNodeSize()*0.5f;
				for(int i=0; i<closeList.Count; i++){
					if(Vector3.Distance(closeList[i].GetPos(), srcNode.GetPos())>=convertedDist){
						closeList.RemoveAt(i); i-=1;
					}
				}
			}
			
			return closeList;
		}
		
		
		
		public static List<Node> GetSpawnGroup(int facID, int areaID){
			List<Node> list=new List<Node>();
			for(int x=0; x<instance.grid.Count; x++){
				for(int z=0; z<instance.grid[x].Count; z++){
					if(!instance.grid[x][z].walkable || instance.grid[x][z].HasObstacle()) continue;
					if(instance.grid[x][z].spawnGroupFacID==facID && instance.grid[x][z].spawnGroupID==areaID) list.Add(instance.grid[x][z]);
				}
			}
			return list;
		}
		
		
		public static bool OnSameFac(Unit unit1, Unit unit2){ return unit1.GetFacID()==unit2.GetFacID(); }
		
		
		void OnDrawGizmos(){
			for(int x=0; x<grid.Count; x++){
				for(int z=0; z<grid[x].Count; z++){
					//Gizmos.color=grid[x][z].visible ? Color.grey : Color.white;
					if(!grid[x][z].walkable){
						Gizmos.color=new Color(0.4f, 0.4f, 0.4f, 1f);
						Gizmos.DrawSphere(grid[x][z].GetPos(), nodeSize*0.125f);
					}
					else if(!grid[x][z].IsVisible()){
						Gizmos.color=new Color(0.5f, 0.5f, 0.5f, .6f);
						Gizmos.DrawSphere(grid[x][z].GetPos(), nodeSize*0.125f);
					}
					
					if(grid[x][z].deployFacID>=0){
						Gizmos.color=UnitManager.GetFacColor(grid[x][z].deployFacID);
						Gizmos.DrawSphere(grid[x][z].GetPos(), nodeSize*0.175f);
					}
					if(grid[x][z].spawnGroupFacID>=0){
						//Gizmos.DrawSphere(grid[x][z].GetPos(), nodeSize*0.25f);
						//Utility.GizmosDrawCross(grid[x][z].GetPos(), nodeSize*0.25f, UnitManager.GetFacColor(grid[x][z].spawnGroupFacID));
						
						Gizmos.color=UnitManager.GetFacColor(grid[x][z].spawnGroupFacID);
						
						float size=nodeSize*0.25f;		Vector3 pos=grid[x][z].GetPos()+new Vector3(0, .1f, 0);
						Gizmos.DrawLine(pos+new Vector3(1, 0, 1).normalized*size, pos-new Vector3(1, 0, 1).normalized*size);
						Gizmos.DrawLine(pos+new Vector3(-1, 0, 1).normalized*size, pos-new Vector3(-1, 0, 1).normalized*size);
					}
				}
			}
			
			
			if(abilityTargetList.Count>0){
				Gizmos.color=Color.yellow;
				for(int i=0; i<abilityTargetList.Count; i++){
					Gizmos.DrawSphere(abilityTargetList[i].GetPos(), 0.4f);
				}
			}
			else{
				Gizmos.color=Color.green;
				for(int i=0; i<walkableList.Count; i++){
					Gizmos.DrawSphere(walkableList[i].GetPos(), 0.3f);
				}
				
				Gizmos.color=Color.red;
				for(int i=0; i<attackableList.Count; i++){
					Gizmos.DrawSphere(attackableList[i].GetPos(), 0.4f);
				}
			}
			
			OnDrawGizmos22();
		}
		
		
	}

}