using System;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using Reset.Units;
using UnityEngine;

namespace Reset.Core {
	[Category("Reset")]
	[Description("Commits movement unit changes to the handler.")]
	public class ChangeMovementSettings : ActionTask<UnitMovementHandler> {
		// Move Speed
		[ParadoxNotion.Design.Header("Speed")]
		public FloatValueGroup moveSpeed = new (newLabel: "Move Speed");
		public FloatValueGroup moveSpeedSoothing = new (newLabel: "Move Speed Smoothing");

		[ParadoxNotion.Design.Header("Direction")]
		public FloatValueGroup airDirectionDecay = new FloatValueGroup("Air Direction Decay");
		public FloatValueGroup accelerationSmoothing = new (newLabel: "Acceleration Smoothing");
		public FloatValueGroup deaccelerationSmoothing = new (newLabel: "Deacceleration Smoothing");
		// public CurveValueGroup deaccelerationCurve = new (newLabel: "Deacceleration Curve"); // Currently unused, may return

		// Direction
		[Space(5)]
		public Vector3ValueGroup feedNewDirection = new Vector3ValueGroup("Feed New Direction");
		public float newDirectionStrength;
		[Space(5)]
		public Vector2 addDirectionFromInput;
		public float addInputStrength;
		[SliderField(0,1)]
		public float addInputPriorty;
		
		// Jumping
		[ParadoxNotion.Design.Header("Jumping")]
		public FloatValueGroup jumpPower = new (newLabel: "Jump Power");
		public FloatValueGroup jumpPowerDecay = new (newLabel: "Jump Decay Speed");
    
		// Gravity
		[ParadoxNotion.Design.Header("Gravity")]
		public FloatValueGroup gravityPower = new (newLabel: "Gravity Power");
		public FloatValueGroup gravityMax = new (newLabel: "Gravity Max");
		public FloatValueGroup gravityAcceleration = new (newLabel: "Gravity Acceleration Speed");
		public FloatValueGroup gravityScale = new (newLabel: "Gravity Scale");
		public FloatValueGroup settingsChangeSmoothing = new(newLabel: "Settings Change Smoothing");
        
		// Rotation
		[ParadoxNotion.Design.Header("Rotation")]
		public EnumValueGroup rotateFacing = new EnumValueGroup("Facing Direction", PlayerFacingDirection.TowardsTarget);
		public FloatValueGroup rotationSpeed = new (newLabel: "Rotation Speed");
		public FloatValueGroup rotationSmoothing = new (newLabel: "Rotation Smoothing");
		public FloatValueGroup rotationInputBlending = new("Rotation Input Blending");
		[Space(5)]
		public BBParameter<Vector3> feedNewRotation;
		public BBParameter<Vector3> feedRelativeTo;
		public Space rotationRelativeSpace;

		private Vector3 feedDir;

		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() {
			// Direction 
			UpdateFloatValue(airDirectionDecay, ref agent.data.airDirectionDecay, ref agent.data.airDirectionDecay);
			UpdateFloatValue(accelerationSmoothing, ref agent.data.accelerationSmoothing, ref agent.defaultData.accelerationSmoothing);
			UpdateFloatValue(deaccelerationSmoothing, ref agent.data.deaccelerationSmoothing, ref agent.defaultData.deaccelerationSmoothing);
			// UpdateCurveProperty(deaccelerationCurve, ref agent.data.deaccelerationCurve, ref agent.defaultData.deaccelerationCurve); // Currently unused, may return
			
			// Direction from value
			// Check that feedDir is not changed
			UpdateVector3Value(feedNewDirection, ref feedDir, ref feedDir); 

			// If there's a direciton add it to the player for a frame
			if (feedDir != Vector3.zero) {
				agent.AddToCurrentDirection(agent.transform.rotation * feedDir.normalized, newDirectionStrength);
				// Reset the fed direction after it's added so future runs don't have
				feedDir = Vector3.zero;
			}
			
			// Direction from controller input
			if (addDirectionFromInput != Vector2.zero){
				agent.OverwriteDirectionFromInput(new Vector3(addDirectionFromInput.x, addDirectionFromInput.y), addInputPriorty, addInputStrength);
			}
			
			// Move Speed
			UpdateFloatValue(moveSpeed, ref agent.data.moveSpeed, ref agent.defaultData.moveSpeed);
			UpdateFloatValue(moveSpeedSoothing, ref agent.data.moveSpeedSoothing, ref agent.defaultData.moveSpeedSoothing);
			
			// Jump
			UpdateFloatValue(jumpPower, ref agent.data.jumpPower, ref agent.defaultData.jumpPower);
			UpdateFloatValue(jumpPowerDecay, ref agent.data.jumpPowerDecay, ref agent.defaultData.jumpPowerDecay);
			
			// Gravity
			UpdateFloatValue(gravityPower, ref agent.data.gravityPower, ref agent.defaultData.gravityPower);
			UpdateFloatValue(gravityMax, ref agent.data.gravityMax, ref agent.defaultData.gravityMax);
			UpdateFloatValue(gravityAcceleration, ref agent.data.gravityAcceleration, ref agent.defaultData.gravityAcceleration);
			UpdateFloatValue(gravityScale, ref agent.data.gravityScale, ref agent.defaultData.gravityScale);
			UpdateFloatValue(settingsChangeSmoothing, ref agent.data.settingsChangeSmoothing, ref agent.defaultData.settingsChangeSmoothing);

			// Rotation
			UpdateEnumValue(rotateFacing, ref agent.data.rotateFacing, ref agent.defaultData.rotateFacing);
			UpdateFloatValue(rotationSpeed, ref agent.data.rotationSpeed, ref agent.defaultData.rotationSpeed);
			UpdateFloatValue(rotationSmoothing, ref agent.data.rotationSmoothing, ref agent.defaultData.rotationSmoothing);
			UpdateFloatValue(rotationInputBlending, ref agent.data.rotationInputBlending, ref agent.defaultData.rotationInputBlending);
			
			// Rotation from value
			if (feedNewRotation.value != Vector3.zero) {
				if (rotationRelativeSpace == Space.World) {
					Debug.Log(Quaternion.LookRotation(feedRelativeTo.value));
					agent.SetSpecifiedRotation(Quaternion.Euler(feedNewRotation.value) * Quaternion.LookRotation(feedRelativeTo.value));
				} else {
					agent.SetSpecifiedRotation(agent.transform.rotation * Quaternion.Euler(feedNewRotation.value) * Quaternion.LookRotation(feedRelativeTo.value));
				}
			}
			
			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() {
			
		}

		public void UpdateCurveProperty(CurveValueGroup valueGroup, ref AnimationCurve targetProperty, ref AnimationCurve defaultProperty){
			switch (valueGroup.changeValue) {
				case ValueChangeAction.NoChange:
					break;
				case ValueChangeAction.NewValue:
					targetProperty = valueGroup.newValue;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty = defaultProperty;
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}
		}
		
		public void UpdateVector3Value(Vector3ValueGroup valueGroup, ref Vector3 targetProperty, ref Vector3 defaultProperty){
			switch (valueGroup.changeX) {
				case ValueChangeAction.NewValue:
					targetProperty.x = valueGroup.newValue.x;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty.x = defaultProperty.x;
					break;
			}
			
			switch (valueGroup.changeY) {
				case ValueChangeAction.NewValue:
					targetProperty.y = valueGroup.newValue.y;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty.y = defaultProperty.y;
					break;
			}
			
			switch (valueGroup.changeZ) {
				case ValueChangeAction.NewValue:
					targetProperty.z = valueGroup.newValue.z;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty.z = defaultProperty.z;
					break;
			}
		}
		
		public void UpdateVector2Value(Vector2ValueGroup valueGroup, ref Vector2 targetProperty, ref Vector2 defaultProperty){
			switch (valueGroup.changeX) {
				case ValueChangeAction.NewValue:
					targetProperty.x = valueGroup.newValue.x;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty.x = defaultProperty.x;
					break;
			}
			
			switch (valueGroup.changeY) {
				case ValueChangeAction.NewValue:
					targetProperty.y = valueGroup.newValue.y;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty.y = defaultProperty.y;
					break;
			}
		}

		
		public void UpdateBoolValue(BoolValueGroup valueGroup, ref bool targetProperty, ref bool defaultProperty){
			switch (valueGroup.changeValue) {
				case ValueChangeAction.NewValue:
					targetProperty = valueGroup.value;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty = defaultProperty;
					break;
			}
		}
		
		public void UpdateEnumValue(EnumValueGroup valueGroup, ref Enum targetProperty, ref Enum defaultProperty){
			switch (valueGroup.changeValue) {
				case ValueChangeAction.NewValue:
					targetProperty = valueGroup.newValue;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty = defaultProperty;
					break;
			}
		}
		
		public void UpdateFloatValue(FloatValueGroup valueGroup, ref float targetProperty, ref float defaultProperty){
			switch (valueGroup.changeValue) {
				case ValueChangeAction.NewValue:
					targetProperty = valueGroup.value.value;
					break;
				case ValueChangeAction.ResetValue:
					targetProperty = defaultProperty;
					break;
			}
		}
	}
}