using System;
using System.Linq;
using NodeCanvas.Framework;
using ParadoxNotion;
using ParadoxNotion.Design;
using UnityEngine;
using UnityEngine.InputSystem;
using Reset.Player.Movement;

namespace NodeCanvas.Tasks.Actions {
	[Category("Reset/Movement")]
	public class AddJump : ActionTask<CharacterController> {
        public BBParameter<Vector3> airMoveDirection;
        public BBParameter<float> jumpPower;
        
        [Space(5)]
        public BBParameter<float> jumpStrength;
        
        [SliderField(0, 1)]
        public BBParameter<float> standStillJumpStrength;

        [Tooltip("Determines how much current movement vectors into jump direction")]
        [SliderField(0, 1)]
        public BBParameter<float> currentVelocityInheritence;
        public BBParameter<Vector3> directionalForce;
        
        [SliderField(0, 1)]
        public BBParameter<float> directionalForceStrength;
        
		protected override string info {
			get{
				string dirStrength = "";
				
				if (directionalForce.value != Vector3.zero && directionalForceStrength.value > 0f) {
					dirStrength = $", towards <i>{directionalForce.value}</i> * {directionalForceStrength.value}";
				}

				return string.Format($"<b>Start Jump</b>, <i>{jumpStrength.value}</i> strength" + dirStrength);
			}
		}

		//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(){
			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(){
			// Set jump power
			jumpPower.value = jumpStrength.value;
			
			// Save current velocity and get current input direction
			Vector3 currentVelocityVector3 = new Vector3(agent.velocity.x, 0f, agent.velocity.z);
			Vector2 currentInput = agent.GetComponent<PlayerControls>().rawMoveInput;
			Vector3 currentInputVector3 = new Vector3(currentInput.x, 0f, currentInput.y);
			
			// Ignore rotation for the current velocity
			Vector3 currentVelocityWorld = agent.transform.InverseTransformDirection(currentVelocityVector3.normalized);
			
			// Get the dot product between current velocity's direction and current input (UNUSED FOR NOW)
			float velocityInputDot = Vector3.Dot(currentVelocityWorld, currentInputVector3);
			
			// Set air move direction
			if (agent.isGrounded) {
				airMoveDirection.value = currentVelocityVector3;
			} else {
				// Hold new desired air direction and Dot against the existing air moving directioin
				Vector3 desiredAirMoveDirection = currentInputVector3;
				float airMoveDirectionDot = Vector3.Dot(desiredAirMoveDirection.normalized, airMoveDirection.value.normalized);
				
				// Check threshold of current XZ velocity- if it's too close to zero, use the jumpStrength for jumping velocity. If it's not, use the current velocity
				float velocityThreshold = 4f;
				float magnitudeZeroDifference = Mathf.Clamp(currentVelocityVector3.magnitude - velocityThreshold, 0f, Mathf.Infinity) / velocityThreshold; // Divided by maximum to return a 0-1 value. Clamping not required.
				float outputHoritontalVelocity = Mathf.Lerp(jumpStrength.value, currentVelocityVector3.magnitude, Math.Clamp(magnitudeZeroDifference, 0f, 1f));
				
				outputHoritontalVelocity = Mathf.Min(outputHoritontalVelocity, Mathf.Lerp(standStillJumpStrength.value * jumpStrength.value, jumpStrength.value * .4f, magnitudeZeroDifference));

				// Do the same for directional jump strength
				outputHoritontalVelocity = Mathf.Lerp(outputHoritontalVelocity, jumpStrength.value, directionalForceStrength.value);
				
				// Remap the dot to  set -1 (opposing direction) to -.5f, and 1 (same direciton) to 1.2f
				// This is done to allow some sideways jumping direction change, but none backwards, and all forwards
				float remappedAirDirectionDot = Mathf.Lerp(.1f, 1.2f, airMoveDirectionDot);
				remappedAirDirectionDot = Mathf.Clamp(remappedAirDirectionDot, 0f, 1f);
				
				// Lerp between the current direction and the inputted direction based on the previous dot product
				Vector3 outputDirection = Vector3.Lerp(currentInputVector3.normalized, currentVelocityVector3.normalized, remappedAirDirectionDot);
				
				// If there is a directional force (such as the Wall Climb jump going straight upward) supplied in the task, lean into that based on it's strength
				outputDirection = Vector3.Lerp(outputDirection, directionalForce.value.normalized, directionalForceStrength.value).normalized;
				
				// Extra math to degrade current air move direction by velocity inheritence, before applying new air direction
				airMoveDirection.value *= currentVelocityInheritence.value;
				
				// Account for the camera's rotation before setting it as the air move direction
				outputDirection = Camera.main.transform.rotation.Flatten(0, null, 0) * outputDirection;
				
				// Set air move direction
				airMoveDirection.value += outputDirection * outputHoritontalVelocity;
			}
			EndAction(true);
		}
		
		//Called once per frame while the action is active.
		protected override void OnUpdate(){
		}

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

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