using UnityEngine;
using System.Collections;
using System;

public abstract class IInteractible : MonoBehaviour
{
		public abstract void Interact (Player p);
}

public class NeutralEnemy : BaseEnemy
{
		public GameObject chicken;
		public GameObject fox;
		
		public Material regularFur;
		public Material evilFur;
	
		protected override void Start ()
		{
				base.Start ();
				animation.Play ("Walk");
		}
	
		protected override void BeginChasing ()
		{
				fox.renderer.material = evilFur;
				animation.Play ("Run");
		}
		protected override void EndChasing ()
		{
				fox.renderer.material = regularFur;
				animation.Play ("Walk");
		}
	
		protected override void Update ()
		{
				if (!_moving)
						return;
		
				var pl = GetPlayer ();
		
				if (pl.Hungry) {
						fox.SetActive (false);
						chicken.SetActive (true);
				} else {
						fox.SetActive (true);
						chicken.SetActive (false);
				}
		
				base.Update ();
		}
}
		
public class BaseEnemy : IInteractible
{
		private Vector2 direction;
		// KEEP THIS NORMALIZED
		private float roamingLength;
		private const float ROAMING_SPEED = 4f;
		protected bool _moving;
		protected bool _chasing;
		private float lastHitTime;
		private const float HIT_INTERVAL = 1f;
		public float roamingScale;
		public float aggressiveDistance;

		public bool moving {
				get {
						return _moving;
				}
				set {
						_moving = value;
				}
		}

		protected virtual void Start ()
		{
				roamingLength = 0F;
				direction = new Vector2 (1f, 0f);
				_moving = true;
		}

		public override void Interact (Player p)
		{
				Hit (p);
		}

		protected virtual void BeginChasing ()
		{
		}
		protected virtual void EndChasing ()
		{
		}

		private void Hit (Player p)
		{
				if ((Time.time - lastHitTime) < HIT_INTERVAL)
						return;
						
				p.Hurt ();
				lastHitTime = Time.time;
		}

		protected Player GetPlayer ()
		{
				return GameObject.FindGameObjectWithTag ("Player").GetComponent ("Player") as Player;
		}

		protected float WhereIs (GameObject go)
		{
				Vector3 direction = (go.transform.position - transform.position);
				return (float)(Math.Atan2 (direction.z, direction.x) * 180f / Math.PI);
		}

		protected void ChasePlayer (Player pl)
		{
				var playerDirection = WhereIs (pl.gameObject);
				var playerDistance = (pl.gameObject.transform.position - transform.position).magnitude;
				bool changeDirection = false;

				if (playerDistance > aggressiveDistance) {
						RoamRandomly (pl);
						_chasing = false;
						return;
				}
				
				if (!_chasing) {
						BeginChasing ();
						changeDirection = true;
						roamingLength = 2f;
				}
				_chasing = true;
				pl.Chase (this.gameObject);

				if (playerDistance < 5f) {
						Hit (pl);
						return;
				} else if (playerDistance < 10f) {
						changeDirection = true;
						roamingLength = 1.0f;
				} else if (playerDistance < 30f && roamingLength <= 0) {
						playerDirection += -5f + 10f * UnityEngine.Random.value;
						roamingLength = 3f + 2f * UnityEngine.Random.value;
						changeDirection = true;
				} else if (roamingLength <= 0) {
						playerDirection += -15f + 30f * UnityEngine.Random.value;
						roamingLength = 3f + 2f * UnityEngine.Random.value;
						changeDirection = true;
				}

				if (changeDirection) {
						direction = new Vector2 ((float)Math.Cos (playerDirection * Math.PI / 180f),
								(float)Math.Sin (playerDirection * Math.PI / 180f));
				}

				var ctrl = (CharacterController)this.GetComponent ("CharacterController");
				var length = ROAMING_SPEED * (1f + 0.3f / playerDistance) * Time.deltaTime;
				ctrl.Move (length * new Vector3 (direction.x, 0, direction.y) + new Vector3 (0f, -1f, 0f));
				transform.LookAt (transform.position + new Vector3 (direction.x, 0, direction.y));
				roamingLength -= length / roamingScale;
		}

		protected void RunAway (Player pl)
		{
				if (_chasing)
						EndChasing ();
				_chasing = false;
		
				var playerDirection = WhereIs (pl.gameObject);
				var playerDistance = (pl.gameObject.transform.position - transform.position).magnitude;
				bool changeDirection = false;
				
				if (playerDistance > 20f) {
						RoamRandomly (pl);
						return;
				}

				if (playerDistance < 10f) {
						changeDirection = true;
						roamingLength = 5f;
				} else if (roamingLength <= 0) {
						playerDirection += -20f + 40f * UnityEngine.Random.value;
						roamingLength = 5f + 2f * UnityEngine.Random.value;
						changeDirection = true;
				}

				if (changeDirection) {
						direction = new Vector2 ((float)-Math.Cos (playerDirection * Math.PI / 180f),
								(float)-Math.Sin (playerDirection * Math.PI / 180f));
				}

				var ctrl = (CharacterController)this.GetComponent ("CharacterController");
				var length = ROAMING_SPEED * (1f + 0.1f / playerDistance) * Time.deltaTime;
				ctrl.Move (length * new Vector3 (direction.x, 0, direction.y) + new Vector3 (0f, -1f, 0f));
				transform.LookAt (transform.position + new Vector3 (direction.x, 0, direction.y));
				roamingLength -= length / roamingScale;
		}

		protected void RoamRandomly (Player pl)
		{
				if (_chasing)
						EndChasing ();
				_chasing = false;
				
				if (roamingLength <= 0) {
						float angle = (float)(Math.Atan2 (direction.y, direction.x) * 180f / Math.PI);
						angle += 0f * UnityEngine.Random.value * (UnityEngine.Random.value > .5f ? 1 : -1);

						direction = new Vector2 ((float)Math.Cos (angle * Math.PI / 180f),
								(float)Math.Sin (angle * Math.PI / 180f));
								
						roamingLength = 20f + 10f * UnityEngine.Random.value;
				}

				var ctrl = (CharacterController)this.GetComponent ("CharacterController");
				var length = ROAMING_SPEED * Time.deltaTime;
				ctrl.Move (length * new Vector3 (direction.x, 0, direction.y) + new Vector3 (0f, -1f, 0f));
				transform.LookAt (transform.position + new Vector3 (direction.x, 0, direction.y));
				roamingLength -= length / roamingScale;
		}
		
		protected virtual void Update ()
		{
				if (!_moving)
						return;
	
				var pl = GetPlayer ();
				
				/*
				if ((pl.transform.position - transform.position).magnitude < 1.0f) {
						Hit (pl);
						if (Player.States.Scared)
								pl.Chase (this);
						return;
				}
				*/

				if (pl.Scared)
						ChasePlayer (pl);
				else if (pl.Hungry)
						RunAway (pl);
				else
						RoamRandomly (pl);
		}
}