using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Assets.Script.Game.Character;
using Framework.Utility;
using FrameworkEngine.Extensions;
using FrameworkEngine.Initialize;
using FrameworkEngine.Locate;
using FrameworkEngine.StateMachine;
using GameEngine.Shared;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;


namespace GameEngine.Main
{
	public class GameController : StateBehaviour<GameController, GameController.Signals, GameControllerEventBus>, 
		IConstructBehaviour, 
		ILocateBehaviour
	{
		[SerializeField] private LocateContainer m_Container = default(LocateContainer);
		[SerializeField] private float m_PowerBarTime = 0.5f;
		[SerializeField, Range(0f, 1f)] private float m_StartPowerNormalized = 0.6f;
		[SerializeField] private Material m_Material = default(Material);
		[SerializeField] private int m_RenderQueue = default(int);
		[SerializeField] private float m_DeerSlowTime = default(float);
		[SerializeField] private Sprite m_DeadBuckFrame = default(Sprite);
		[SerializeField] private Transform m_LeftSpawn = default(Transform);
		[SerializeField] private Transform m_RightSpawn = default(Transform);
		[SerializeField] private DiffcultyMover[] m_BuckDifficultyProgression = default(DiffcultyMover[]);
		[SerializeField] private float m_SpawnDelay = default(float);
		[SerializeField] private float m_SpeedModifier = 3f;
		[SerializeField] private TextMeshProUGUI m_KillText = default(TextMeshProUGUI);
		[SerializeField] private TextMeshProUGUI m_GameOverKillText = default(TextMeshProUGUI);
		[SerializeField] private TextMeshProUGUI m_HighScoreText = default(TextMeshProUGUI);
		private List<EntityMover> _buck;
		private bool _startedLeft;
		private int _kills;
		private EntityMover _playerMover;
		private EntityMover _startDeerMover;
		private Transform _tapToShoot;
		private ProjectileThrower _projectileThrower;
		private float _powerTime;
		private float _normalizedPower;
		private Transform _gameOver;




		public void Construct()
		{
			m_Container.Locate(out _playerMover, Tags.PLAYER);
			m_Container.Locate(out _startDeerMover, Tags.START_DEER);
			m_Container.Locate(out _tapToShoot, Tags.TAP_TO_SHOOT);
			m_Container.Locate(out _gameOver, Tags.GAME_OVER);
			m_Container.Locate(out _projectileThrower);

			_tapToShoot.gameObject.SetActive(false);
			_gameOver.gameObject.SetActive(false);
			m_KillText.gameObject.SetActive(false);

			_buck = new List<EntityMover>();

			Machine.RegisterState("Start", new StartState());
			Machine.RegisterState("Playing", new Playing());
			Machine.RegisterState("GameOver", new GameOver());
		}


		protected override void Update()
		{
			base.Update();
			Machine.PushSignal(Signals.Update);
		}




		private void TargetHit(Projectile projectile, RaycastHit2D collision, ProjectileTarget target)
		{
			Debug.Log("HIT TARGET");
			projectile.OnGroundHit -= OnGroundHit;
			projectile.OnProjectileTargetHit -= TargetHit;

			projectile.enabled = false;
			projectile.transform.parent = target.transform;

			Vector3 pos = projectile.transform.localPosition;
			pos.y = -1f;
			projectile.transform.localPosition = pos;

			target.transform.parent.GetComponentInChildren<SpriteRenderer>().sprite = m_DeadBuckFrame;
			target.transform.parent.GetComponentInChildren<Animator>().enabled = false;
			StartCoroutine(SlowDeerRoutine(target.GetComponentInParent<EntityMover>()));

			_kills++;

			Destroy(target.transform.parent.gameObject, 6f);
			Invoke("SpawnNextBuck", m_SpawnDelay);
		}


		private void SpawnNextBuck()
		{
			_buck.Clear();
			CollectionUtlity.ResetList(ref _buck, 4);

			var buck = Instantiate(GetBuckForProgress());

			_startedLeft = Random.Range(0, 2) == 0;

			if (_startedLeft)
			{
				buck.Speed += GetSpeedModifier();
				buck.SetDistance(m_LeftSpawn.position.x);
			}
			else
			{
				buck.Speed -= GetSpeedModifier();
				buck.SetDistance(m_RightSpawn.position.x);
			}

			_buck.Add(buck);
		}


		private float GetSpeedModifier()
		{
			return m_SpeedModifier;
		}


		private EntityMover GetBuckForProgress()
		{
			var filteredList = m_BuckDifficultyProgression.Where(d => _kills >= d.MinKills && _kills < d.MaxKills).ToArray();

			if (filteredList.Length == 0)
			{
				return m_BuckDifficultyProgression.Random().Mover;
			}

			return filteredList.Random().Mover;
		}


		private IEnumerator SlowDeerRoutine(EntityMover mover)
		{
			EntityMovementVariation variation = mover.GetComponent<EntityMovementVariation>();

			if (variation != null)
			{
				variation.enabled = false;
			}

			float startSpeed = mover.Speed;
			float time = 0;

			while (time < m_DeerSlowTime)
			{
				mover.Speed = Mathf.Lerp(startSpeed, 0f, time/m_DeerSlowTime);
				time += Time.deltaTime;
				yield return null;
			}

			mover.Speed = 0f;
		}

		private void OnGroundHit(Projectile projectile, RaycastHit2D collision)
		{
			Debug.Log("HIT GROUND");
			projectile.OnGroundHit -= OnGroundHit;
			projectile.OnProjectileTargetHit -= TargetHit;

			projectile.enabled = false;
			projectile.transform.position = collision.point;
		}


		public enum Signals
		{
			Update,
			UseCharacterAction
		}


		private class StartState : State<GameController, Signals>
		{
			protected override void OnEnter()
			{
				Context._playerMover.SetDistance(0f);
				Context._startDeerMover.SetDistance(Context._startDeerMover.transform.position.x);
				Context._powerTime = Context.m_StartPowerNormalized*Context.m_PowerBarTime;
				Context._normalizedPower = Mathf.PingPong(Context._powerTime, Context.m_PowerBarTime) / Context.m_PowerBarTime;
				Context._tapToShoot.gameObject.SetActive(true);
				base.OnEnter();
			}

			protected override void OnExit()
			{
				Context._tapToShoot.gameObject.SetActive(false);
				base.OnEnter();
			}


			protected override bool HandleSignal(Signals signal, object args)
			{
				switch (signal)
				{
					case Signals.Update:
#if UNITY_EDITOR
						Context._powerTime = Context.m_StartPowerNormalized * Context.m_PowerBarTime;
						Context._normalizedPower = Mathf.PingPong(Context._powerTime, Context.m_PowerBarTime) / Context.m_PowerBarTime;
#endif
						Context._projectileThrower.SetPreviewPath(Context._playerMover.Speed, Context._normalizedPower);
						break;
					case Signals.UseCharacterAction:
						Projectile projectile = Context._projectileThrower.ThrowProjectile(Context._playerMover.Speed, Context._normalizedPower);
						projectile.OnGroundHit += Context.OnGroundHit;
						projectile.OnProjectileTargetHit += Context.TargetHit;

						Machine.SetState("Playing");
						break;
				}

				return true;
			}
		}


		private class Playing : State<GameController, Signals>
		{
			protected override void OnEnter()
			{
				base.OnEnter();
				Context._kills = 0;
				Context.m_KillText.gameObject.SetActive(true);
			}


			protected override void OnExit()
			{
				Context.m_KillText.gameObject.SetActive(false);
				base.OnExit();
			}

			protected override bool HandleSignal(Signals signal, object args)
			{
				switch (signal)
				{
					case Signals.Update:

						Context.m_KillText.text = Context._kills.ToString();
						if (Context._startedLeft)
						{
							if (Context._buck.Any(b => b.transform.position.x > Context.m_RightSpawn.position.x))
							{
								Machine.SetState("GameOver");
								return true;
							}
						}
						else
						{
							if (Context._buck.Any(b => b.transform.position.x < Context.m_LeftSpawn.position.x))
							{
								Machine.SetState("GameOver");
								return true;
							}
						}

						Context._powerTime += Time.deltaTime;
						Context._normalizedPower = Mathf.PingPong(Context._powerTime, Context.m_PowerBarTime)/Context.m_PowerBarTime;
						Context._projectileThrower.SetPreviewPath(Context._playerMover.Speed, Context._normalizedPower);
						break;
					case Signals.UseCharacterAction:
						Projectile projectile = Context._projectileThrower.ThrowProjectile(Context._playerMover.Speed, Context._normalizedPower);
						projectile.OnGroundHit += Context.OnGroundHit;
						projectile.OnProjectileTargetHit += Context.TargetHit;

						break;
				}

				return true;
			}
		}


		private class GameOver : State<GameController, Signals>
		{
			private float _enterTime;




			protected override void OnEnter()
			{
				_enterTime = Time.time;

				int currentHighScore = PlayerPrefs.GetInt("user_score", 0);

				if (Context._kills > currentHighScore)
				{
					currentHighScore = Context._kills;
					PlayerPrefs.SetInt("user_score", currentHighScore);
				}

				Context.m_GameOverKillText.text = Context._kills.ToString();
				Context.m_HighScoreText.text = currentHighScore.ToString();

				Context._gameOver.gameObject.SetActive(true);
				Context._projectileThrower.EmptyPreviewPath();
				base.OnEnter();
			}


			protected override void OnExit()
			{
				Context._gameOver.gameObject.SetActive(false);
				base.OnEnter();
			}



			protected override bool HandleSignal(Signals signal, object args)
			{
				switch (signal)
				{
					case Signals.UseCharacterAction:
						if (_enterTime < Time.time - 0.2f)
						{
							Context.SpawnNextBuck();
							Machine.SetState("Playing");
						}
						break;
				}

				return true;
			}
		}


		[Serializable]
		private struct DiffcultyMover
		{
			public int MaxKills;
			public int MinKills;
			public EntityMover Mover;
		}
	}
}
