using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class BoidController : MonoBehaviour
{
	public enum BehaviourType
	{
		Defend,
		March,
		Charge,
		Flee
	}

	[SerializeField]
	private int _playerIndex;
	public int PlayerIndex { get { return _playerIndex; } }

	[SerializeField]
	private BoidAgent _agentPrefab;
	[SerializeField]
	private int _agentCount;
	[SerializeField]
	private UI_PlayerHUD _hud;
	[SerializeField]
	private SquadBehaviourAudio _squadAudio;

	public float DeltaTime { get { return Time.deltaTime * SquadBehaviour.TimeScale; } }

	public BehaviourType CurBehaviourType;

	private List<BoidAgent> _agents = new List<BoidAgent>();
	public List<BoidAgent> Agents { get { return _agents; } }

	[SerializeField]
	private List<Transform> _obstacleAgents = new List<Transform>();
	public List<Transform> ObstacleAgents { get { return _obstacleAgents; } }

	public SquadBehaviour SquadBehaviour;

	public SquadBehaviour DefendBehaviour;
	public SquadBehaviour MarchBehaviour;
	public SquadBehaviour ChargeBehaviour;
	public SquadBehaviour FleeBehaviour;

	public Color teamColor;
	public Color teamDisabledColor;

	//public float BoundMagnitude = 50;

	[Header("Soft Bounds")]
	public Vector3 softBoundsMin = new Vector3(-50f, -1f, -50f);
	public Vector3 softBoundsMax = new Vector3(50f, 1f, 50f);
	public bool showSoftBoundsInSceneView = true;

	[Header("Hard Bounds")]
	public Vector3 hardBoundsMin = new Vector3(-50f, -1f, -50f);
	public Vector3 hardBoundsMax = new Vector3(50f, 1f, 50f);
	public bool showHardBoundsInSceneView = true;

	[Header("Reenforcements")]
	public float reinformcementDuration = 10f;
	public int reinforcementCount = 25;

	[Header("Spawning Bounds")]
	public Vector3 spawnMin = new Vector3(-10f, -1f, -10f);
	public Vector3 spawnMax = new Vector3( 10f,  1f,  10f);
	public bool showSpawnBoundsInSceneView = true;

	private int _currentReinforcements;
	private float reinformcementTimer;

	private Vector3 _flockPositionSum;
	public Vector3 FlockPositionSum { get { return _flockPositionSum; } }

	private Vector3 _flockVelocitySum;
	public Vector3 FlockVelocitySum { get { return _flockVelocitySum; } }

	[SerializeField]
	private float _cohesionBonus = 0;
	public float CohesionBonus { get { return _cohesionBonus; } }
	public Vector2 commandInputAxis;// { get; private set; }

	[SerializeField]
	private float _energyDecayRate = 50;
	[SerializeField]
	private float _energyChargeRate = 10;
	[SerializeField]
	private float _energy = 100;
	public float Energy { get { return _energy; } }

	private float _behaviourCooldown = 0;

	private BoidController _enemyController;

	// Use this for initialization
	void Start ()
	{
		BattleController.Instance.OnPrepareBattle.AddListener(SpawnAgents);
		BattleController.Instance.OnBattleStarted.AddListener(OnBattleStarted);
	}
	
	// Update is called once per frame
	void Update ()
	{
		if (BattleController.Instance.BattleStarted)
		{
			if (InputManager.GetButtonDown(_playerIndex, ControllerButton.B) && _behaviourCooldown <= 0 && CurBehaviourType != BehaviourType.Flee)
			{
				if (CurBehaviourType == BehaviourType.Charge)
					_behaviourCooldown = 2;
				else
					_behaviourCooldown = 1;

				SquadBehaviour = DefendBehaviour;
				CurBehaviourType = BehaviourType.Defend;
				_behaviourCooldown = 1;
				if (_hud)
					_hud.OnBehaviourChanged(BehaviourType.Defend);
				if (_squadAudio)
					_squadAudio.DoBehaviourAudio(this);
			}
			else if (InputManager.GetButtonDown(_playerIndex, ControllerButton.A) && _behaviourCooldown <= 0 && CurBehaviourType != BehaviourType.Flee)
			{
				if (CurBehaviourType == BehaviourType.Charge)
					_behaviourCooldown = 2;
				else
					_behaviourCooldown = 1;

				SquadBehaviour = MarchBehaviour;
				CurBehaviourType = BehaviourType.March;
				_behaviourCooldown = 1;
				if (_hud)
					_hud.OnBehaviourChanged(BehaviourType.March);
				if (_squadAudio)
					_squadAudio.DoBehaviourAudio(this);
			}
			else if (InputManager.GetButtonDown(_playerIndex, ControllerButton.X) && _behaviourCooldown <= 0 && CurBehaviourType != BehaviourType.Flee)
			{
				SquadBehaviour = ChargeBehaviour;
				CurBehaviourType = BehaviourType.Charge;
				_behaviourCooldown = 1;
				if (_hud)
					_hud.OnBehaviourChanged(BehaviourType.Charge);
				if (_squadAudio)
					_squadAudio.DoBehaviourAudio(this);
			}

			commandInputAxis = InputManager.GetJoystick(_playerIndex);

			if (InputManager.GetButtonDown(_playerIndex, ControllerButton.Y) && reinformcementTimer <= 0)
				SpawnReinforcements();
		}
		if (_agents.Count > 0)
		{
			_flockPositionSum = Vector3.zero;
			_flockVelocitySum = Vector3.zero;
			foreach(BoidAgent agent in _agents)
			{
				_flockPositionSum += agent.CachedPosition;
				_flockVelocitySum += agent.Velocity;
			}

			/*_cohesionBonus = CalculateConhesionBonus();
			Debug.Log(_cohesionBonus);*/

			foreach (BoidAgent agent in _agents)
			{
				agent.DoBehaviours(_agents);
			}
			foreach (BoidAgent agent in _agents)
			{
				agent.DoMovement();
			}
		}

		if (BattleController.Instance.BattleStarted)
		{
			if (reinformcementTimer > 0f)
			{
				reinformcementTimer -= Time.deltaTime;
				if (reinformcementTimer <= 0f)
				{
					_currentReinforcements += reinforcementCount;
					//reinformcementTimer += reinformcementDuration;
				}
			}
			if (CurBehaviourType == BehaviourType.Charge)
			{
				_energy -= _energyDecayRate * Time.deltaTime;
				if (_energy <= 0)
				{
					SquadBehaviour = MarchBehaviour;
					CurBehaviourType = BehaviourType.March;
					_behaviourCooldown = 2;
					if (_hud)
						_hud.OnBehaviourChanged(BehaviourType.March);
					/*if (_squadAudio)
						_squadAudio.DoBehaviourAudio(this);*/
				}
			}
			else if (CurBehaviourType == BehaviourType.Defend)
			{
				_energy += _energyChargeRate * Time.deltaTime;
			}
			else if (CurBehaviourType == BehaviourType.March)
			{
				_energy += _energyChargeRate * Time.deltaTime * .25f;
			}
			_energy = Mathf.Clamp(_energy, 0, 100);

			if (_behaviourCooldown > 0)
			{
				_behaviourCooldown -= Time.deltaTime;
			}

			if (_hud)
				_hud.UpdateProgressIndicators(_energy / 100, 1 - (reinformcementTimer / reinformcementDuration), _behaviourCooldown <= 0, CurBehaviourType);
		}
	}

	private float CalculateConhesionBonus()
	{
		if (_agents.Count == 1)
			return .25f;

		Vector3 com = _flockPositionSum / _agents.Count;
		float largestDistSq = 0;
		foreach(BoidAgent agent in _agents)
		{
			float distSq = (com - agent.CachedPosition).sqrMagnitude;
			if (distSq > largestDistSq)
				largestDistSq = distSq;
		}
		float area = largestDistSq * Mathf.PI;
		return _agents.Count / area;
	}
	private void SpawnAgents(BoidController inControllerA, BoidController inControllerB)
	{
		_enemyController = inControllerA == this ? inControllerB : inControllerA;
		foreach (BoidAgent agent in _agents)
		{
			Destroy(agent.gameObject);
		}
		_agents.Clear();

		for(int i = 0; i < _agentCount; i++)
		{
			BoidAgent spawnedAgent = Instantiate<BoidAgent>(_agentPrefab, transform);
			spawnedAgent.transform.localPosition = new Vector3(Random.Range(spawnMin.x, spawnMax.x), 0f, Random.Range(spawnMin.z, spawnMax.z));
			spawnedAgent.SetController(this, _enemyController);
			_agents.Add(spawnedAgent);
			spawnedAgent.transform.DOScale(Vector3.zero, Random.Range(.25f, .75f)).From();
		}

		reinforcementCount = 25;

		SquadBehaviour = DefendBehaviour;
		CurBehaviourType = BehaviourType.Defend;
		_behaviourCooldown = 1;
		_energy = 100;
		if (_hud)
			_hud.OnBehaviourChanged(BehaviourType.Defend);
		if (_squadAudio)
			_squadAudio.DoBehaviourAudio(this);
	}
	private void OnBattleStarted()
	{
		reinformcementTimer = reinformcementDuration;
		SquadBehaviour = MarchBehaviour;
		CurBehaviourType = BehaviourType.March;
		_behaviourCooldown = 1;
		if (_hud)
			_hud.OnBehaviourChanged(BehaviourType.March);
		if (_squadAudio)
			_squadAudio.DoBehaviourAudio(this);
	}

	private void SpawnReinforcements()
	{
		for(int i = 0; i < _currentReinforcements; i++)
		{
			BoidAgent spawnedAgent = Instantiate<BoidAgent>(_agentPrefab, transform);
			spawnedAgent.transform.localPosition = new Vector3(Random.Range(spawnMin.x, spawnMax.x), 0f, Random.Range(spawnMin.z, spawnMax.z));
			spawnedAgent.SetController(this, _enemyController);
			_agents.Add(spawnedAgent);
			spawnedAgent.transform.DOScale(Vector3.zero, Random.Range(.25f, .75f)).From();
		}

		_currentReinforcements = 0;
		reinformcementTimer = reinformcementDuration;
		_hud.OnCallReinforcements();
		_squadAudio.DoBehaviourAudio(this);
		_squadAudio.DoReinforcementAudio();

		if(CurBehaviourType == BehaviourType.Flee)
		{
			SquadBehaviour = DefendBehaviour;
			CurBehaviourType = BehaviourType.Defend;
			if (_hud)
				_hud.OnBehaviourChanged(BehaviourType.Defend);
			if (_squadAudio)
				_squadAudio.DoBehaviourAudio(this);
		}

		reinforcementCount -= 5;
		if(reinforcementCount < 5)
			reinforcementCount = 5;
	}

	public void DespawnAgent(BoidAgent inAgent)
	{
		if(_agents.Contains(inAgent))
			_agents.Remove(inAgent);

		if(_agents.Count == 0)
			BattleController.Instance.OnArmyDestroyed(this);
		/*else if(_agents.Count <= _agentCount * .1f && _enemyController.CurBehaviourType != BehaviourType.Flee)
		{
			SquadBehaviour = FleeBehaviour;
			CurBehaviourType = BehaviourType.Flee;
			_hud.OnBehaviourChanged(CurBehaviourType);
		}*/
		//Destroy(inAgent.gameObject);
	}

//	void OnGUI()
//	{
//		GUILayout.Label("Reinforcements: " + _currentReinforcements + ".  Additional reinforcements in " + reinformcementTimer + " seconds.");
//	}

	void OnDrawGizmosSelected()
	{
		if(showSpawnBoundsInSceneView)
		{
			Vector3 center = Vector3.Lerp(spawnMin, spawnMax, 0.5f);
			Vector3 size = spawnMax - center;
			size *= 2f;
			Gizmos.color = new Color(0f, 1f, 0f, 0.5f);
			Gizmos.DrawCube(transform.TransformPoint(center), size);
		}
		if (showSoftBoundsInSceneView)
		{
			Vector3 center = Vector3.Lerp(softBoundsMin, softBoundsMax, 0.5f);
			Vector3 size = softBoundsMax - center;
			size *= 2f;
			Gizmos.color = new Color(0f, 1f, 0f, 0.5f);
			Gizmos.DrawCube(transform.TransformPoint(center), size);
		}
		if (showHardBoundsInSceneView)
		{
			Vector3 center = Vector3.Lerp(hardBoundsMin, hardBoundsMax, 0.5f);
			Vector3 size = hardBoundsMax - center;
			size *= 2f;
			Gizmos.color = new Color(0f, 0f, 1f, 0.2f);
			Gizmos.DrawCube(transform.TransformPoint(center), size);
		}
	}
}
