using System;
using System.Collections;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using ParadoxNotion.Services;
using Unity.Cinemachine;
using Unity.Mathematics;
using UnityEngine;
using Reset.Movement;
using Reset.Units;

namespace NodeCanvas.Tasks.Actions {

	[Category("Reset/Movement")]
	[Description("Finalizes movement and sends the final move commands to the controller")]
	// TODO: Rename to UpdateMovementDirection
	public class UpdateMovementDirection : ActionTask<CharacterController>{
		[ParadoxNotion.Design.Header("References")]
		public BBParameter<Vector3> moveDirection;
		// TODO: Remove this reference and let each copy of this use their own settings.
		public BBParameter<PlayerFacingDirection> playerFacingDirection;
		
		[ParadoxNotion.Design.Header("Settings")]
		public BBParameter<float> accelerationSmoothing = 5f;
		public BBParameter<float> deaccelerationSmoothing = 5f;
		public BBParameter<AnimationCurve> deaccelerationCurve;

		public BBParameter<float> directionChangeMinimumMagnitude; // Unused. Use to only make input change if it's over a certain input threshold. Maybe smoothing can affect this?
		
		private float lastLookMagnitude;
		private Vector3 currentMoveDir;
	
		// References
		private PlayerControls controls;
		
		//Use for initialization. This is called only once in the lifetime of the task.
		//Return null if init was successfull. Return an error string otherwise
		protected override string OnInit() {
			// Reference to controls
			controls = agent.GetComponent<PlayerControls>();
			
			return null;
		}

		//This is called once each time the task is enabled.
		//Call EndAction() to mark the action as finished, either in success or failure.
		//EndAction can be called from anywhere.
		protected override void OnExecute(){
			// Initialize the smoothed direction with the value as is on input
			// currentMoveDir = new Vector3(moveDirection.value.x, moveDirection.value.y, moveDirection.value.z);
		}

		//Called once per frame while the action is active.
		protected override void OnUpdate(){
	        // Construct move direction
	        Vector3 targetDirection = moveDirection.value;
	        
	        // Get input value
	        Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y);

	        if (inputMovement.magnitude < .1f) {
		        inputMovement = Vector3.zero;
	        }

			// Change how movement is managed based on facing state
			switch (playerFacingDirection.value) {
				case PlayerFacingDirection.TowardsTarget:
					break;
				case PlayerFacingDirection.MatchForward: // TODO: Recomment
					// targetDirection = agent.transform.forward * inputMovement.magnitude;
					targetDirection = inputMovement;
					break;
				case PlayerFacingDirection.MatchCamera:
					targetDirection = Quaternion.Euler(Camera.main.transform.forward.DirectionTo(agent.transform.forward)) * inputMovement;	// NOTE: This used to be t: currentRotSpeed * Time.deltaTime.
					// See how it feels with a hard set value and go fro there.
					break;
				case PlayerFacingDirection.Static:
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}

			// Remove Y from variables
			Vector3 targetNoY = new Vector3(targetDirection.x, 0f, targetDirection.z);
			Vector3 currentNoY = new Vector3(currentMoveDir.x, 0f, currentMoveDir.z);
			
			// Smooth movement 
			if (targetNoY.magnitude > currentNoY.magnitude) {
				currentMoveDir = Vector3.Lerp(currentMoveDir, targetNoY,accelerationSmoothing.value * Time.deltaTime);	

			} else {
				float deaccelValue = deaccelerationCurve.value.Evaluate(inputMovement.magnitude); 
				
				currentMoveDir = Vector3.Lerp(currentMoveDir, targetNoY, deaccelerationSmoothing.value * Time.deltaTime);	
			}
			
			
			currentMoveDir.y = moveDirection.value.y;
			// Commit move direction
			moveDirection.value = currentMoveDir;
			// Debug.Log(moveDirection.value);
			EndAction(true);
		}
		
		// Gravity is processed in LateUpdate (added to MonoManager's onLateUpdate in OnInit())


		//Called when the task is disabled.
		protected override void OnStop() {
			EndAction(true);
		}

		//Called when the task is paused.
		protected override void OnPause() {
			
		}
	}
}