﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;

public class Motor : MonoBehaviour
{
	[SerializeField] private Vector3 defaultDestination = Vector3.zero;
	[SerializeField] private float maxRadius = 10;
	[SerializeField] private float minRadius = 1;
	[SerializeField] private float speed = 1;

	public DecisionTypes decision = DecisionTypes.Advance;

	private Attack attack;
	private FactionInfo factionInfo;
	private List<Attackable> pointsOfInterest = new List<Attackable>();
	private Vector3 destination;

	private void Start()
	{
		attack = GetComponent<Attack>();
		factionInfo = GetComponent<FactionInfo>();
		destination = defaultDestination;
	}

	private void Update()
	{
		if (decision != DecisionTypes.Attack)
		{
			float distance = speed * Time.deltaTime;

			transform.LookAt(destination + Vector3.up * transform.localScale.y / 2f);
			transform.position += transform.forward * distance;
		}
	}

	private void FixedUpdate()
	{
		if (GameManager.IsRetreating)
		{
			decision = DecisionTypes.Retreat;
		}
		else
		{
			GetPointsOfInterest();

			if (pointsOfInterest.Count > 0)
			{
				if (attack != null && attack.CanAttack())
				{
					decision = DecisionTypes.Attack;
				}
				else
				{
					decision = DecisionTypes.Investigate;
				}
			}
			else
			{
				decision = DecisionTypes.Advance;
			}
		}

		switch (decision)
		{
			case DecisionTypes.Advance:
				destination = defaultDestination;
				break;
			case DecisionTypes.Retreat:
				destination = 2 * transform.position - defaultDestination;
				break;
			case DecisionTypes.Investigate:
				destination = pointsOfInterest[0].transform.position;
				break;
			case DecisionTypes.Attack:
				destination = pointsOfInterest[0].transform.position;
				break;
		}
	}

	private void GetPointsOfInterest()
	{
		pointsOfInterest.Clear();

		if (factionInfo == null)
		{
			throw new Exception("No faction info");
		}

		Collider[] hits = Physics.OverlapSphere(transform.position, maxRadius, GameManager.AttackableLayers);
		float minRadiusSquare = minRadius * minRadius;

		foreach (Collider hit in hits)
		{
			if (hit.gameObject == gameObject)
			{
				continue;
			}

			float squareDistance = Vector3.SqrMagnitude(transform.position - hit.transform.position);
			if (squareDistance < minRadiusSquare)
			{
				continue;
			}

			Attackable attackable = hit.GetComponent<Attackable>();
			FactionInfo attackableFaction = hit.GetComponent<FactionInfo>();

			if (attackable != null && attackableFaction != null && factionInfo.IsHostileTarget(attackableFaction))
			{
				pointsOfInterest.Add(attackable);
			}
		}

		pointsOfInterest.Sort(delegate(Attackable x, Attackable y)
		{
			Attack attackX = x.GetComponent<Attack>();
			Attack attackY = y.GetComponent<Attack>();
			float squareDistanceX = Vector3.SqrMagnitude(transform.position - x.transform.position);
			float squareDistanceY = Vector3.SqrMagnitude(transform.position - y.transform.position);
			
			if (x == y) return 0;
			else if (attackX != null && attackY == null) return -1;
			else if (attackX == null && attackY != null) return 1;
			else if (Mathf.Approximately(squareDistanceX, squareDistanceY)) return 0;
			else if (squareDistanceX < squareDistanceY) return -1;
			else if (squareDistanceX > squareDistanceY) return 1;
			else return 0;
		});
	}

	private void OnDrawGizmos()
	{
		Vector3 position = transform.position;

		Gizmos.color = Color.blue;
		Gizmos.DrawWireSphere(position, maxRadius);
		Gizmos.DrawWireSphere(position, minRadius);
		Gizmos.DrawLine(position, destination);
	}
}

public enum DecisionTypes
{
	Advance,
	Retreat,
	Investigate,
	Attack
}
