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

public enum WeaponType
{
	GUN,
	KNIFE
}

public class WeaponBehaviour : MonoBehaviour
{
	[SerializeField]
	private int knifeDamage = 50;
	[SerializeField]
	private int gunDamage = 1;
	[SerializeField]
	private float gunRange = 50;
	[SerializeField]
	private ParticleSystem bulletEffect;
	[SerializeField]
	private float knifeRange;
	[SerializeField]
	private ParticleSystem knifeEffect;
	[SerializeField]
	private LayerMask enemyMask, blockMask;
	private IWeapon weapon { get { return weaponList[weaponIndex]; } }
	private List<IWeapon> weaponList = new List<IWeapon>();
	private int weaponIndex = 0;
	private void Start()
	{
		IngameUIManager.inst.UpdateWeaponTypeUI(WeaponType.GUN);

		weaponList.Add(new Gun(gunDamage, gunRange, bulletEffect));
		weaponList.Add(new Knife(knifeDamage, knifeRange, bulletEffect, transform));
	}

	private void Update()
	{
		foreach (var weapon in weaponList)
		{
			weapon.UpdateWeapon();
		}

		Enemy[] hitEnemies = weapon.WeaponLockOn(enemyMask, blockMask);
		weapon.UseWeapon(hitEnemies);

		if (Input.GetKeyDown(KeyCode.Tab))
		{
			SwapWeapon();
		}
		if (Input.GetKeyDown(KeyCode.LeftShift))
		{
			weapon.UseSkill();
		}
	}

	private void SwapWeapon()
	{
		weaponIndex = (weaponIndex + 1) % 2;
		IngameUIManager.inst.UpdateWeaponTypeUI((WeaponType)weaponIndex);
		
	}
}

public interface IWeapon
{
	void UseWeapon(Enemy[] hitEnemies);
	void UseSkill();
	Enemy[] WeaponLockOn(LayerMask enemyMask, LayerMask blockMask);
	void UpdateWeapon();
}

public class Gun : IWeapon
{
	protected int weaponDamage;
	protected float weaponRange;

	private ParticleSystem effect;

	private const float gunShotInterval = 0.02f;
	private float timer;

	public Gun(int dmg, float range, ParticleSystem effect)
	{
		weaponDamage = dmg;
		weaponRange = range;
		this.effect = effect;
	}

	public void UseWeapon(Enemy[] hitEnemies)
	{
		if (timer > 0)
			return;
		if (Input.GetMouseButton(0))
		{
			effect.transform.rotation = Camera.main.transform.rotation;
			effect.Play();
			foreach (var enemy in hitEnemies)
			{
				enemy.GetDamaged(weaponDamage);
				IngameUIManager.inst.UpdateComboUI(weaponDamage);
			}
			timer = gunShotInterval;
		}
	}

	public Enemy[] WeaponLockOn(LayerMask enemyMask, LayerMask blockMask)
	{
		RaycastHit[] enemyHits = Physics.SphereCastAll(Camera.main.ScreenPointToRay(Input.mousePosition), 3, weaponRange, enemyMask);

		List<Enemy> hitEnemies = new List<Enemy>();
		List<Vector3> hitEnemyPositions = new List<Vector3>();

		foreach (var enemyHit in enemyHits)
		{
			Ray ray = new Ray(Camera.main.transform.position, (enemyHit.point - Camera.main.transform.position).normalized);
			if (!Physics.Raycast(ray, Vector3.Distance(Camera.main.transform.position, enemyHit.point), blockMask))
			{
				hitEnemies.Add(enemyHit.collider.GetComponent<Enemy>());
				hitEnemyPositions.Add(enemyHit.transform.position);
			}
		}

		IngameUIManager.inst.UpdateTargetLockedUIs(hitEnemyPositions.ToArray());
		return hitEnemies.ToArray();
	}

	public void UseSkill()
	{

	}

	public void UpdateWeapon()
	{
		timer -= Time.deltaTime;
	}
}

public class Knife : IWeapon
{
	protected int weaponDamage;
	protected float weaponRange;
	ParticleSystem effect;
	Transform player;
	private const float skillTimeInterval = 5;
	private float skillTimer = 0;


	public Knife(int dmg, float range, ParticleSystem effect, Transform player)
	{
		weaponDamage = dmg;
		weaponRange = range;
		this.effect = effect;
		this.player = player;
	}

	public void UseWeapon(Enemy[] hitEnemies)
	{
		if (Input.GetMouseButtonDown(0))
		{
			effect.transform.rotation = Camera.main.transform.rotation;
			effect.Play();
			foreach (var enemy in hitEnemies)
			{
				enemy.GetDamaged(weaponDamage);
				IngameUIManager.inst.UpdateComboUI(weaponDamage);
				if (enemy.IsDead)
					skillTimer = 0;
			}
		}
	}

	public Enemy[] WeaponLockOn(LayerMask enemyMask, LayerMask blockMask)
	{
		RaycastHit[] enemyHits = Physics.SphereCastAll(Camera.main.ScreenPointToRay(Input.mousePosition), 3, weaponRange, enemyMask);

		List<Enemy> hitEnemies = new List<Enemy>();
		List<Vector3> hitEnemyPositions = new List<Vector3>();

		foreach (var enemyHit in enemyHits)
		{
			Ray ray = new Ray(Camera.main.transform.position, (enemyHit.point - Camera.main.transform.position).normalized);
			if (!Physics.Raycast(ray, Vector3.Distance(Camera.main.transform.position, enemyHit.point), blockMask))
			{
				hitEnemies.Add(enemyHit.collider.GetComponent<Enemy>());
				hitEnemyPositions.Add(enemyHit.transform.position);
			}
		}

		IngameUIManager.inst.UpdateTargetLockedUIs(hitEnemyPositions.ToArray());
		return hitEnemies.ToArray();
	}

	public void UseSkill()
	{
		if (skillTimer > 0)
			return;
		GameManager.inst.StartCoroutine(SkillRoutine());
		skillTimer = skillTimeInterval;
	}

	public void UpdateWeapon()
	{
		skillTimer -= Time.deltaTime;
		IngameUIManager.inst.UpdateSkillUI(skillTimer, skillTimeInterval);
	}

	private IEnumerator SkillRoutine()
	{
		const float dashDistance = 50;
		const float dashTime = 0.2f;
		Vector3 oriPos = player.position;

		Ray ray = new Ray(player.position, Camera.main.transform.forward);
		RaycastHit hit;
		Physics.Raycast(ray, out hit, dashDistance, player.GetComponent<PlayerController>().groundMask);
		Vector3 dest = hit.collider != null ? hit.point - Camera.main.transform.forward * 0.5f : player.position + Camera.main.transform.forward * dashDistance;

		//player.GetComponent<MeshRenderer>().enabled = false;
		for (float t = 0; t < dashTime; t += Time.fixedDeltaTime)
		{
			player.position = Vector3.Lerp(oriPos, dest, 1 - Mathf.Pow(1 - (t / dashTime),2));
			yield return new WaitForFixedUpdate();
		}
		player.GetComponent<Rigidbody>().velocity = Vector3.zero;
		//player.GetComponent<MeshRenderer>().enabled = true;

		foreach (var enemyHit in Physics.SphereCastAll(ray, 3, Vector3.Distance(oriPos,dest), 1 << LayerMask.NameToLayer("Enemy")))
		{
			Enemy enemy = enemyHit.collider.GetComponent<Enemy>();
			if (enemy == null)
				continue;
			enemy.GetDamaged(weaponDamage);
			IngameUIManager.inst.UpdateComboUI(weaponDamage);
			if (enemy.IsDead)
				skillTimer = 0;
		}
	}
}
