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

[RequireComponent(typeof(AudioSource))]
public class CollisionManager : Singleton<CollisionManager>
{
	List<KeyValuePair<BoidAgent, BoidAgent>> _collisionPairs;
	List<BoidAgent> _cachedDestroyList;

	public float cohesionMultiplier = 1;
	public float speedMultiplier = 1;
	public float randomMultiplier = 1;

	private AudioSource _aSource;

	[SerializeField]
	private AudioClip _hitSound;
	private bool _hitHappened = false;

	[SerializeField]
	private AudioClip _missSound;
	private bool _missHappened = false;

	void Start()
	{
		_aSource = GetComponent<AudioSource>();
		_collisionPairs = new List<KeyValuePair<BoidAgent, BoidAgent>>();
		_cachedDestroyList = new List<BoidAgent>();
	}

	void Update()
	{
		if (BattleController.Instance.BattleStarted)
		{
			_hitHappened = _missHappened = false;

			//Handle all collisions between pairs
			foreach (KeyValuePair<BoidAgent, BoidAgent> pair in _collisionPairs)
			{
				Collide(pair.Key, pair.Value);
			}

			foreach (BoidAgent agent in _cachedDestroyList)
				agent.Die();

			_cachedDestroyList.Clear();
			_collisionPairs.Clear();

			if (_missHappened)
			{
				_aSource.pitch = Random.Range(.8f, 1.2f);
				_aSource.PlayOneShot(_missSound);
			}
			if (_hitHappened)
			{
				_aSource.pitch = Random.Range(.8f, 1.2f);
				_aSource.PlayOneShot(_hitSound);
			}
				
		}
	}

	void Collide(BoidAgent inA, BoidAgent inB)
	{
		BoidAgent agentToDestroy = null;

		// Same Type Collisions
		if (inA.CurrentBehaviourType == BoidController.BehaviourType.Defend && inB.CurrentBehaviourType == BoidController.BehaviourType.Defend)
		{
			agentToDestroy = DefendVDefend(inA, inB);
		}
		else if ((inA.CurrentBehaviourType == BoidController.BehaviourType.March && inB.CurrentBehaviourType == BoidController.BehaviourType.March)
			|| (inA.CurrentBehaviourType == BoidController.BehaviourType.Charge && inB.CurrentBehaviourType == BoidController.BehaviourType.Charge))
		{
			agentToDestroy = MarchVMarch(inA, inB);
		}

		//defend vs. march
		else if (inA.CurrentBehaviourType == BoidController.BehaviourType.Defend && inB.CurrentBehaviourType == BoidController.BehaviourType.March)
			agentToDestroy = DefendVMarch(inA, inB);
		else if (inB.CurrentBehaviourType == BoidController.BehaviourType.Defend && inA.CurrentBehaviourType == BoidController.BehaviourType.March)
			agentToDestroy = DefendVMarch(inB, inA);

		//defend vs. charge
		else if (inA.CurrentBehaviourType == BoidController.BehaviourType.Defend && inB.CurrentBehaviourType == BoidController.BehaviourType.Charge)
			agentToDestroy = DefendVCharge(inA, inB);
		else if (inB.CurrentBehaviourType == BoidController.BehaviourType.Defend && inA.CurrentBehaviourType == BoidController.BehaviourType.Charge)
			agentToDestroy = DefendVCharge(inB, inA);

		//march vs. charge
		else if (inA.CurrentBehaviourType == BoidController.BehaviourType.March && inB.CurrentBehaviourType == BoidController.BehaviourType.Charge)
			agentToDestroy = MarchVCharge(inA, inB);
		else if (inB.CurrentBehaviourType == BoidController.BehaviourType.March && inA.CurrentBehaviourType == BoidController.BehaviourType.Charge)
			agentToDestroy = MarchVCharge(inB, inA);

		//anything vs. flee
		else if (inA.CurrentBehaviourType == BoidController.BehaviourType.Flee)
			agentToDestroy = inA;
		else if (inB.CurrentBehaviourType == BoidController.BehaviourType.Flee)
			agentToDestroy = inB;
		else
			Debug.LogError("WE MISSED A CASE!!!!!");

		if (agentToDestroy != null && !_cachedDestroyList.Contains(agentToDestroy))
		{
			_hitHappened = true;
			_cachedDestroyList.Add(agentToDestroy);
		}
		else
		{
			_missHappened = true;
		}
	}

	private BoidAgent DefendVDefend(BoidAgent inA, BoidAgent inB)
	{
		float rVal = Random.Range(0, 600);
		if(rVal < 50)
		{
			return inA;
		}
		else if(rVal < 100)
		{
			return inB;
		}
		else
		{
			return null;
		}
	}
	private BoidAgent DefendVMarch(BoidAgent inD, BoidAgent inM)
	{
		float rVal = Random.Range(0, 200);

		if (rVal < 25)
			return inD;
		else if (rVal < 100)
			return inM;
		else
			return null;
	}
	private BoidAgent DefendVCharge(BoidAgent inD, BoidAgent inC)
	{
		float rVal = Random.Range(0, 600);

		if (rVal < 60)
			return inD;
		else if (rVal < 100)
			return inC;
		else
			return null;
	}

	private BoidAgent MarchVMarch(BoidAgent inA, BoidAgent inB)
	{
		bool aFlanking = false;
		bool bFlanking = false;
		/*bool aFlanking = isFlanking(inA, inB);
		bool bFlanking = isFlanking(inB, inA);
		if (aFlanking && bFlanking)
			return null;*/

		float rVal;

		if (aFlanking || bFlanking)
		{
			//special flanking case probability
			rVal = Random.Range(0, 130);
			if(rVal < 20)
			{
				return aFlanking ? inA : inB;
			}
			else if(rVal < 100)
			{
				return aFlanking ? inB : inA;
			}
			else
			{
				return null;
			}
		}
		else
		{
			// regular 50/50/30 probability
			rVal = Random.Range(0, 130);
			if (rVal < 50)
			{
				return inA;
			}
			else if (rVal < 100)
			{
				return inB;
			}
			else
			{
				return null;
			}
		}
	}
	private BoidAgent MarchVCharge(BoidAgent inM, BoidAgent inC)
	{
		bool marchIsFlanking = false;
		bool chargeIsFlanking = false;
		/*bool marchIsFlanking = isFlanking(inM, inC);
		bool chargeIsFlanking = isFlanking(inC, inM);
		if (marchIsFlanking && chargeIsFlanking)
			return null;*/
		float rVal;

		//marchIsFlanking = chargeIsFlanking = false;

		if(marchIsFlanking)
		{
			rVal = Random.Range(0, 130);
			if (rVal < 30)
				return inM;
			else if (rVal < 100)
				return inC;
			else
				return null;
		}
		else if(chargeIsFlanking)
		{
			rVal = Random.Range(0, 120);
			if (rVal < 10)
				return inC;
			else if (rVal < 100)
				return inM;
			else
				return null;
		}
		else
		{
			rVal = Random.Range(0, 130);
			if (rVal < 30)
				return inC;
			else if (rVal < 100)
				return inM;
			else
				return null;
		}
	}

	private bool isFlanking(BoidAgent inAttacker, BoidAgent inVictim)
	{
		return Vector3.Dot(inVictim.transform.forward, inAttacker.CachedPosition) < 0;
	}
	public void AddPair(BoidAgent inA, BoidAgent inB)
	{
		if(!enabled)
			return;

		bool found = false;
		foreach(KeyValuePair<BoidAgent, BoidAgent> pair in _collisionPairs)
		{
			if((pair.Key == inA && pair.Value == inB) || (pair.Key == inB && pair.Value == inA))
			{
				found = true;
				break;
			}
		}

		if(!found)
			_collisionPairs.Add(new KeyValuePair<BoidAgent, BoidAgent>(inA, inB));
	}
}
