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

namespace Nostalgia.Example
{
	[AddComponentMenu("Nostalgia/Example/Player")]
	[RequireComponent(typeof(PlatformController))]
	public sealed class Player : MonoBehaviour
	{
		public float maxSpeed = 10.0f;
		public float jumpSpeed = 10.0f;
		public float jumpMaxHold = 20.0f;
		public float jumpMaxHoldTime = 0.2f;
		public GameObject deadParticle;
		public GameObject landingParticle;
		public AudioClip jumpClip;
		public AudioClip landingClip;
		public AudioClip footStepClip;
		public AudioClip goalClip;
		public PlatformController _Controller;

		Animator _Animator;
		SpriteRenderer _SpriteRenderer;
		AudioSource _AudioSource;
		float _BeginJumpTime;
		bool _PreGround = true;
		bool _IsJumping = false;
		bool _IsGoal = false;
		InfoTrigger _InfoTrigger = null;
		IActionTrigger _ActionTrigger = null;

		static readonly int s_HorizontalID = Animator.StringToHash("Horizontal");
		static readonly int s_VerticalID = Animator.StringToHash("Vertical");
		static readonly int s_isGroundID = Animator.StringToHash("isGround");
		static readonly int s_JumpID = Animator.StringToHash("Jump");
		static readonly int s_DirectionID = Animator.StringToHash("Direction");

		void Awake()
		{
			_Animator = GetComponent<Animator>();

			_Controller = GetComponent<PlatformController>();
			_Controller.trapHitEvent.AddListener(OnTrapHit);

			_AudioSource = GetComponent<AudioSource>();
		}

		void Start()
		{
			Camera mainCamera = Camera.main;
			if (mainCamera == null)
			{
				return;
			}

			GameObject mainCameraObj = mainCamera.gameObject;
			CameraController cameraController = mainCameraObj.GetComponent<CameraController>();
			if (cameraController != null)
			{
				return;
			}

			cameraController = mainCameraObj.AddComponent<CameraController>();
			cameraController.target = transform;

			Stage currentStage = Stage.current;
			if (Map.mapCount > 0)
			{
				GameObject stageObj = new GameObject("Stage", typeof(Stage));
				currentStage = stageObj.GetComponent<Stage>();
				currentStage.mainMap = Map.GetMap(0);
			}
			cameraController.stage = currentStage;

			Debug.LogWarning("CameraController was automatically added to the main camera.");
		}

		void OnTrapHit(TrapTile trapTile)
		{
			Dead();
		}

		public void ClearGround()
		{
			_PreGround = true;
			_Controller.ClearGround();
			_Controller.ClearThroughCell();
		}

		bool IsGroundedThroughCell()
		{
			int groundCount = _Controller.groundCount;
			for (int groundIndex = 0; groundIndex < groundCount; groundIndex++)
			{
				PlatformController.Ground ground = _Controller.GetGround(groundIndex);
				if (ground.tile != null)
				{
					if (ground.tile.GetComponent<ThroughFloorTile>() != null)
					{
						return true;
					}
				}
			}

			return false;
		}

		void Update()
		{
			if (Time.timeScale == 0.0f)
			{
				return;
			}

			if (_IsGoal)
			{
				_Animator.SetFloat(s_HorizontalID, 0);
				return;
			}

			float horizontal = VirtualInput.GetAxis("Horizontal");
			float vertical = VirtualInput.GetAxis("Vertical");

			if (Mathf.Abs(horizontal) > 0.01f)
			{
				_Animator.SetFloat(s_DirectionID, Mathf.Sign(horizontal) == 1 ? 1.0f : -1.0f);
			}

			float speed = horizontal * maxSpeed;
			if (!_Controller.isGround || Mathf.Abs(_Controller.velocity.x) <= Mathf.Abs(speed))
			{
				Vector2 velocity = _Controller.velocity;

				velocity.x = speed;

				_Controller.Move(velocity);
			}

			if (_IsJumping)
			{
				if (_Controller.velocity.y > 0.0f)
				{
					Vector2 velocity = _Controller.velocity;

					if (VirtualInput.GetButton("Jump"))
					{
						float t = (Time.time - _BeginJumpTime) / jumpMaxHoldTime;
						if (t >= 1.0f)
						{
							_IsJumping = false;

							velocity.y = jumpSpeed;

							_Controller.velocity = velocity;
						}
						else
						{
							velocity.y = Mathf.Lerp(jumpSpeed, jumpMaxHold, t);

							_Controller.velocity = velocity;
						}

						_Controller.ClearGround();
					}
					else
					{
						_IsJumping = false;

						velocity.y = jumpSpeed;

						_Controller.velocity = velocity;
					}
				}
				else
				{
					_IsJumping = false;
				}
			}
			else if (VirtualInput.GetButtonDown("Jump") && _Controller.isGround)
			{
				if (IsGroundedThroughCell() && vertical <= -0.5f)
				{
					_Controller.isGroundThrough = true;
				}
				else
				{
					_AudioSource.clip = jumpClip;
					_AudioSource.Play();

					_Animator.SetTrigger(s_JumpID);

					_IsJumping = true;
					_BeginJumpTime = Time.time;

					Vector2 velocity = _Controller.velocity;
					velocity.y = jumpSpeed;
					_Controller.velocity = velocity;

					_Controller.ClearGround();
				}
			}

			_Animator.SetFloat(s_HorizontalID, horizontal);

			if (_ActionTrigger != null && VirtualInput.GetAxisRaw("Vertical") > 0.5f)
			{
				_ActionTrigger.OnAction(this);
			}
		}

		void LateUpdate()
		{
			bool isGround = _Controller.isGround;

			_Animator.SetBool(s_isGroundID, isGround);
			_Animator.SetFloat(s_VerticalID, _Controller.velocity.y);

			if (!_PreGround && isGround)
			{
				_AudioSource.clip = landingClip;
				_AudioSource.Play();

				GameObject spawnObject = landingParticle;

				if (_Controller.isGround)
				{
					PlatformController.Ground ground = _Controller.GetGround(0);
					if (ground.tile != null)
					{
						GroundTile groundTile = ground.tile.GetComponent<GroundTile>();
						if (groundTile != null && groundTile.spawnObject != null)
						{
							spawnObject = groundTile.spawnObject;
						}
					}
				}

				if (spawnObject != null)
				{
					Instantiate(spawnObject, transform.position, spawnObject.transform.rotation);
				}
			}

			_PreGround = isGround;
		}

		void OnTriggerEnter2D(Collider2D collider)
		{
			InfoTrigger infoTrigger = collider.GetComponent<InfoTrigger>();
			if (infoTrigger != null)
			{
				if (_InfoTrigger != null)
				{
					_InfoTrigger.OnExit(this);
					_InfoTrigger = null;
				}

				_InfoTrigger = infoTrigger;

				if (_InfoTrigger != null)
				{
					_InfoTrigger.OnEnter(this);
				}
			}

			IActionTrigger actionTrigger = collider.GetComponent<IActionTrigger>();
			if (actionTrigger != null)
			{
				if (_ActionTrigger != null)
				{
					_ActionTrigger.OnExit(this);
					_ActionTrigger = null;
				}

				_ActionTrigger = actionTrigger;

				if (_ActionTrigger != null)
				{
					_ActionTrigger.OnEnter(this);
				}
			}
		}

		void OnTriggerExit2D(Collider2D collider)
		{
			InfoTrigger infoTrigger = collider.GetComponent<InfoTrigger>();
			if (infoTrigger == _InfoTrigger)
			{
				if (_InfoTrigger != null)
				{
					_InfoTrigger.OnExit(this);
					_InfoTrigger = null;
				}

				_InfoTrigger = null;
			}

			IActionTrigger actionTrigger = collider.GetComponent<IActionTrigger>();
			if (actionTrigger == _ActionTrigger)
			{
				if (_ActionTrigger != null)
				{
					_ActionTrigger.OnExit(this);
					_ActionTrigger = null;
				}

				_ActionTrigger = null;
			}
		}

		void Dead()
		{
			Instantiate(deadParticle, transform.position, Quaternion.identity);

			Destroy(gameObject);
		}

		IEnumerator NextStage()
		{
			yield return new WaitForSeconds(10.0f);

			SceneUtil.LoadScene(SceneUtil.activeSceneName);
		}

		public void PlayFootStep()
		{
			AudioClip clip = footStepClip;

			if (_Controller.isGround)
			{
				PlatformController.Ground ground = _Controller.GetGround(0);
				if (ground.tile != null)
				{
					GroundTile groundTile = ground.tile.GetComponent<GroundTile>();
					if (groundTile != null && groundTile.footStepClip != null)
					{
						clip = groundTile.footStepClip;
					}
				}
			}

			AudioUtility.PlayClipAtPoint(clip, transform.position, 0.1f);
		}

		public void Goal()
		{
			if (_IsGoal)
			{
				return;
			}

#if UNITY_2023_1_OR_NEWER
			Timer timer = FindFirstObjectByType<Timer>();
#else
			Timer timer = FindObjectOfType<Timer>();
#endif
			if (timer != null)
			{
				timer.OnGoal();
			}

			StageManager.ChangeBGM(goalClip, false);

			_IsGoal = true;

			StartCoroutine(NextStage());
		}
	}
}
