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

[RequireComponent(typeof(Rigidbody))]
public abstract class Enemy : MonoBehaviour
{
	[SerializeField]
	private int maxHealth;
	[SerializeField]
	private int curHealth;
	[SerializeField]
	protected int attackDamage;
	[SerializeField]
	private float detectRange;
	[SerializeField]
	protected float moveSpeed;
	[SerializeField]
	private LayerMask playerMask;
	[SerializeField]
	private LayerMask blockMask;
	[SerializeField]
	private GameObject projectilePrefab;

	[SerializeField]
	private ParticleSystem bloodEffect;

	private RectTransform healthUIRect;
	private Slider healthUI;

	private Collider col;

	protected StateMachine stateMachine = new StateMachine();

	protected PlayerController target;

	[SerializeField]
	private float shotSpeed;
	[SerializeField]
	protected float shotTimeInterval;
	protected float shotTimer;

	[SerializeField]
	protected Rigidbody rb;

	public bool IsDead { get { return curHealth <= 0; } }

#if UNITY_EDITOR
	private void OnDrawGizmos()
	{
		Gizmos.color = Color.cyan;
		Gizmos.DrawWireSphere(transform.position, detectRange);
	}
#endif

	protected virtual void Start()
	{
		col = GetComponent<Collider>();
		rb = GetComponent<Rigidbody>();
		curHealth = maxHealth;
		InitializeStateMachine();
	}

	private void Update()
	{
		stateMachine.curState?.StateUpdate();
		UpdateHealthUIRect();
	}

	public void GetDamaged(int dmg)
	{
		curHealth -= dmg;
		StartCoroutine(DamageRoutine());
		UpdateHealthUI();

		if (curHealth <= 0)
		{
			Dead();
		}
	}

	private void Dead()
	{
		Debug.Log(gameObject + " : DEAD!");
		Rigidbody rb = GetComponent<Rigidbody>();
		if (rb != null)
			rb.useGravity = false;
		GetComponent<Collider>().enabled = false;
		if (healthUI != null)
			Destroy(healthUI.gameObject);
		Destroy(gameObject);
	}

	private IEnumerator DamageRoutine()
	{
		if (bloodEffect != null)
			bloodEffect.Play();
		yield return null;
	}

	private void UpdateHealthUI()
	{
		if (healthUI == null)
		{
			healthUI = IngameUIManager.inst.CreateNewHealthUI();
			healthUIRect = healthUI.GetComponent<RectTransform>();
		}
		IngameUIManager.inst.UpdateEnemyHealthUI(healthUI, maxHealth, curHealth);
	}

	private void UpdateHealthUIRect()
	{
		if (healthUI == null)
			return;

		float distance = Vector3.Distance(Camera.main.transform.position, transform.position);
		healthUIRect.localScale = new Vector3(1,1,1) / distance;

		Vector2 viewportPoint = Camera.main.WorldToViewportPoint(transform.position + new Vector3(0,col.bounds.extents.y * 1.5f));
		healthUIRect.anchorMax = viewportPoint;
		healthUIRect.anchorMin = viewportPoint;
	}

	protected bool DetectPlayer()
	{
		var detectResults = Physics.OverlapSphere(transform.position, detectRange, playerMask);
		if (detectResults.Length > 0)
		{
			Ray ray = new Ray(transform.position, detectResults[0].transform.position - transform.position);
			if (Physics.Raycast(ray, detectRange, blockMask))
				return false;
			target = detectResults[0].GetComponent<PlayerController>();
			return true;
		}
		return false;
	}

	protected void GiveDamageToTarget()
	{
		target.GetDamaged(attackDamage);
	}

	protected virtual void ShotProjectile()
	{
		shotTimer -= Time.deltaTime;
		if (shotTimer <= 0)
		{
			shotTimer = shotTimeInterval;
			Vector3 shotDirection = (target.transform.position - transform.position).normalized;
			Instantiate(projectilePrefab, transform.position, transform.rotation)
				.GetComponent<Enemy>().SetTarget(target);
		}
	}

	protected virtual void AttackTarget()
	{
		shotTimer -= Time.deltaTime;
		if (shotTimer <= 0)
		{
			shotTimer = shotTimeInterval;
			GiveDamageToTarget();
		}
	}

	public void SetTarget(PlayerController pc)
	{
		target = pc;
	}

	protected virtual void Initialize()
	{
		InitializeStateMachine();
	}
	protected abstract void InitializeStateMachine();
}
