﻿using UnityEngine;
using AC;
using Opsive.UltimateCharacterController.Character;

[RequireComponent(typeof(UltimateCharacterLocomotion))]
public class AC_UCC_Character : MonoBehaviour
{

	#region Variables

	[SerializeField] private bool controlCameraDuringCutscenes = false;
	[SerializeField] private bool syncCameraAndCursorLock = true;
	[SerializeField] private Opsive.Shared.Input.PlayerInput playerInput;

	private UltimateCharacterLocomotion ultimateCharacterLocomotion;
	private AC.Char acCharacter;
	private AC_Motion acMotionAbility;
	private Camera uccCamera;

	#endregion


	#region UnityStandards

	private void Awake ()
	{
		ultimateCharacterLocomotion = GetComponent<UltimateCharacterLocomotion>();
		acCharacter = GetComponent<AC.Char>();

		acCharacter.motionControl = acCharacter.IsActivePlayer () ? MotionControl.Manual : MotionControl.Automatic;
		
		var cameraController = Opsive.Shared.Camera.CameraUtility.FindCamera (gameObject).GetComponent<Opsive.UltimateCharacterController.Camera.CameraController> ();
		if (cameraController) uccCamera = cameraController.GetComponent<Camera> ();

		/*var characterAnimator = GetComponent<ModelManager> ().ActiveModel.GetComponent<Animator> ();
#if FIRST_PERSON_CONTROLLER
		var transformLookViewType = cameraController.GetViewType<Opsive.UltimateCharacterController.FirstPersonController.Camera.ViewTypes.TransformLook> ();
		transformLookViewType.MoveTarget = characterAnimator.GetBoneTransform (HumanBodyBones.Head);
		transformLookViewType.RotationTarget = characterAnimator.GetBoneTransform (HumanBodyBones.Hips);
#endif
#if THIRD_PERSON_CONTROLLER
		var lookAtViewType = cameraController.GetViewType<Opsive.UltimateCharacterController.ThirdPersonController.Camera.ViewTypes.LookAt> ();
		lookAtViewType.Target = characterAnimator.GetBoneTransform (HumanBodyBones.Head);
#endif*/
	}


	private void Start ()
	{
		acMotionAbility = ultimateCharacterLocomotion.GetAbility<AC_Motion>();
	}


	private void OnEnable()
	{
		EventManager.OnEnterGameState += OnEnterGameState;
	}


	private void OnDisable()
	{
		EventManager.OnEnterGameState -= OnEnterGameState;
	}
	

	private void Update ()
	{
		if (!KickStarter.stateHandler.IsInGameplay () || !acCharacter.IsActivePlayer ())
		{
			if (acCharacter.IsActivePlayer ())
			{
				Vector2 cameraInput = new Vector2 (GetTargetCameraSpin (), GetTargetCameraPitch ()) * Time.deltaTime * acCharacter.turnSpeed;
				if (uccCamera && KickStarter.mainCamera.attachedCamera != uccCamera)
				{
					cameraInput = Vector2.zero;
				}
				SetCameraInputState (false, cameraInput);
			}
			ultimateCharacterLocomotion.TryStartAbility (acMotionAbility);
			return;
		}
		
		acCharacter.SetLookDirection (ultimateCharacterLocomotion.transform.forward, true);

		if (syncCameraAndCursorLock && (KickStarter.player.freeAimLocked || !KickStarter.playerInput.GetInGameCursorState () || !KickStarter.playerInput.IsCursorLocked ()))
		{
			SetCameraInputState (false);
		}
		else if (KickStarter.playerInput.GetDragState () == DragState.Moveable)
		{
			HeldObjectData[] heldObjectDatas = KickStarter.playerInput.GetHeldObjectData ();
			if (heldObjectDatas.Length > 0)
			{
				bool isPickUp = heldObjectDatas[0].DragObject is Moveable_PickUp;
				if (isPickUp && KickStarter.settingsManager.disableFreeAimWhenDraggingPickUp)
				{
					SetCameraInputState (false);
				}
				else if (!isPickUp && KickStarter.settingsManager.disableFreeAimWhenDragging)
				{
					SetCameraInputState (false);
				}
				else
				{
					SetCameraInputState (true);
				}
			}
			else
			{
				SetCameraInputState (true);
			}
		}
		else
		{
			SetCameraInputState (true);
		}

		/*if (KickStarter.settingsManager.movementMethod == MovementMethod.PointAndClick)
		{
			return;
		}*/

		if (KickStarter.stateHandler.MovementIsOff)
		{
			ultimateCharacterLocomotion.TryStartAbility (acMotionAbility);
		}
		else
		{
			ultimateCharacterLocomotion.TryStopAbility (acMotionAbility);
		}
	}

	#endregion


	#region PublicFunctions

	public void SnapCamera ()
	{
		SnapCharacterToCameraForward (true);
	}

	#endregion


	#region CustomEvents

	private void OnEnterGameState (GameState newGameState)
	{
		if (acCharacter.IsActivePlayer () && newGameState == GameState.Normal && KickStarter.settingsManager.movementMethod != MovementMethod.PointAndClick)
		{
			ultimateCharacterLocomotion.TryStopAbility (acMotionAbility);

			SetCameraInputState (false);

			if (KickStarter.settingsManager.movementMethod == MovementMethod.FirstPerson)
			{
				SnapCharacterToCameraForward ();
			}
			else
			{
				//ultimateCharacterLocomotion.SetRotation (acPlayer.GetTargetRotation());
			}
		}
		else if (newGameState != GameState.Paused)
		{
			ultimateCharacterLocomotion.TryStartAbility (acMotionAbility, true, true);
			SetCameraInputState (true);
		}
	}


	private void OnTeleport ()
	{
		ultimateCharacterLocomotion.SetPositionAndRotation (acCharacter.GetTargetPosition (), acCharacter.GetTargetRotation (), true);

		if (acCharacter.IsActivePlayer ())
		{
			OnEnterGameState (KickStarter.stateHandler.gameState);
		}
	}

	#endregion


	#region PrivateFunctions

	private float GetTargetCameraSpin ()
	{
		var forwardA = Camera.main.transform.rotation * Vector3.forward;
		var forwardB = acCharacter.GetTargetRotation() * Vector3.forward;

		var angleA = Mathf.Atan2(forwardA.x, forwardA.z) * Mathf.Rad2Deg;
		var angleB = Mathf.Atan2(forwardB.x, forwardB.z) * Mathf.Rad2Deg;
		
		return Mathf.DeltaAngle (angleA, angleB);
	}


	private float GetTargetCameraPitch ()
	{
		if (acCharacter.charState == CharState.Move)
		{
			float angle = Camera.main.transform.localEulerAngles.x;
			if (angle > 180f) angle -= 360f;
			return angle;
		}
		return 0f;
	}


	private void SetCameraInputState (bool value)
	{
		SetCameraInputState (value, Vector2.zero);
	}


	private void SetCameraInputState (bool value, Vector2 manualLookVector)
	{
		if (controlCameraDuringCutscenes && !value && KickStarter.stateHandler.IsInCutscene ())
		{
			return;
		}

		if (value)
		{
			playerInput.LookMode = Opsive.Shared.Input.PlayerInput.LookVectorMode.Smoothed;
		}
		else
		{
			playerInput.LookMode = Opsive.Shared.Input.PlayerInput.LookVectorMode.Manual;
			playerInput.CurrentLookVector = manualLookVector;
		}
	}


	private void SnapCharacterToCameraForward (bool snapUCC = false)
	{
		Vector3 cameraForward = Camera.main.transform.forward;
		cameraForward.y = 0f;
		cameraForward.Normalize();
		acCharacter.SetLookDirection (cameraForward, true);

		if (snapUCC)
		{
			ultimateCharacterLocomotion.SetRotation (acCharacter.GetTargetRotation ());
		}
	}

	#endregion

}