class_name Movement_StateReceiver
extends Node

## Movement component 
# attempts to interact with movement of the scene root without knowing what
# it is. It can be perhaps a static body or kinematicbody 
# it doesn't actually move a node, that's what the state machine does
# but it does keep track of velocity
# I can't give it an actor node or a direct reference. 
# It can use a number of detection components to help inform decisions.

export var debug_component: bool = false

onready var current_state = StateAnimatedActor.new()
onready var parent: KinematicBody2D = get_parent()


export var callable_state_machine :NodePath
var request_state_change: FuncRef

onready var desired_movement_vector: Vector2 = Vector2(0,0)

# Since animactor state machine can actually view properties from this type
# I'm thinking about moving the velocity tracker in here instead of player.
var velocity = Vector2(0,0)
var momentum = Vector2(0,0)
var acceleration = Vector2(0,0)

#Removing Probably not used
#var sim_velocity = Vector2(0,0)
var physics_delta:float
var process_delta:float

var modifier: StateModifierAnimatedActor

#Removing Probably not used
#var attack_function: FuncRef

const UP = -1.0
const DOWN = 1.0
const LEFT = -1.0
const RIGHT = 1.0

func _ready():
	## FuncRef to request state machine change state
	request_state_change = funcref(get_node(callable_state_machine), 'change_to_known_state')
	## Connect signal to get alerted of state change
	get_node(callable_state_machine).connect("state_changed", self,"_on_state_change")
	## A reference to the current state machine state
	current_state = get_node(callable_state_machine).current_state
	## A reference to the state machine merged modifiers
	modifier = get_node(callable_state_machine).merged_animation_state_modifiers
	
############
## These get called by the parent
############
func process_physics(delta):
	physics_delta = delta
	if has_method('_state_process_physics_' + current_state.name):
		call('_state_process_physics_' + current_state.name)
	else:
		move_actor_as_desired()

# more likely needed for Players
func process_physics_input(delta):
	physics_delta = delta
	if has_method('_state_process_physics_input_' + current_state.name):
		call('_state_process_physics_input_' + current_state.name)

# More likely needed for NPCs
func process(delta):
	process_delta = delta
	if has_method('_state_process_' + current_state.name):
		call('_state_process_' + current_state.name)

# For event based input polling (Hadouken?)
func process_input(event: InputEvent):
	# Reset desired movement vector
	desired_movement_vector = Vector2(0,0)
	if has_method('_state_process_input_' + current_state.name):
			call('_state_process_input_' + current_state.name)

############

############
## A Series of helper functions
############
func go_up():
	desired_movement_vector.y = UP
func go_down():
	desired_movement_vector.y = DOWN
func go_left():
	desired_movement_vector.x = LEFT
func go_right():
	desired_movement_vector.x = RIGHT
func stop():
	desired_movement_vector = Vector2(0,0)

# Return the desired direction of movement for the character
# in the range [-1, 1], where positive values indicate a desire
# to move to the right and negative values to the left.
func get_movement_direction() -> float:
	return desired_movement_vector.x

# Return a boolean indicating if the character wants to jump
func wants_jump() -> bool:
	return false

# Return a boolean indicating if the character wants to attack
func wants_shoot() -> bool:	
	return false

# Return a boolean indicating if the character wants to dash
func wants_dash() -> bool:	
	return false

func wants_roll() -> bool:	
	return false

func wants_climb() -> bool:	
	return false
############

func get_climb_shape_location() -> Vector2:
	return Vector2(-1,-1)

func _on_state_change(old_state_name:String, new_state :State):
	#print ("got the state change signal (MC) ", new_state.name)
	# set current state to new_state
	#TODO: Confirm that this is a reference to the state in the
	###### State machine
	if new_state is StateAnimatedActor: #Testing this. Update: It works
		current_state = new_state
		if has_method('_on_state_change_' + current_state.name):
			call('_on_state_change_' + current_state.name)
	else:
		push_warning("Received non animated Actor state.")
	#current_state = new_state


func move_actor_as_desired( x_move_direction_override: float = 0):
	var delta:float = physics_delta
	var _move_speed = current_state.horizontal_speed
	var _move_acceleration = current_state.horizontal_acceleration
	var _move_speed_modifier = current_state.horizontal_speed_offset
	var _move_modifier_move_acceleration = current_state.horizontal_speed_offset_acceleration
	var _jerk_factor = current_state.jerk_factor
	var _gravity = current_state.gravity
	
#	if current_state.physics_modifier:
#		#physics_modifier.modifier_properties.jerk_factor
#		#print(physics_modifier.name)
#		#UiManager.debug_text = physics_modifier.name + str(round(adjusted_move_speed))
#		var mod_props :ModifierProperties = current_state.physics_modifier.get_modifier_properties()
#		if mod_props.move_speed != 0 and mod_props.directional_modifier == false:
#			_move_speed = mod_props.move_speed
#		_move_speed_modifier += mod_props.move_speed_modifier
#		_move_acceleration += mod_props.move_acceleration
#		_move_modifier_move_acceleration += mod_props.move_modifier_move_acceleration
#		_jerk_factor += mod_props.jerk_factor

	var help_me_not_be_dumb = ''

	# Allow us to bump out of halt.
	if _move_speed == 0 and desired_movement_vector.x != 0:
		_move_speed = abs(desired_movement_vector.x)

	# Determine or physics movement direction	
	var current_x_velocity = velocity.x #move_component.velocity.x
	var move_direction = 0.0
	# Determine movement direction if we have momentum
	if momentum.x != 0:
		if x_move_direction_override == 0:
			move_direction = sign(current_x_velocity)
#			if move_component.desired_movement_vector.x != 0:
#				# set the move direction to the desired direction
#				move_direction = move_component.desired_movement_vector.x
#			else:
#				# Set move direction to the transform direction 
#				move_direction = parent.transform.x.x
		else:
			move_direction = x_move_direction_override
	else: # No current momentum in place
		if x_move_direction_override == 0:
			move_direction = desired_movement_vector.x
		else:
			move_direction = x_move_direction_override

## TODO: I hate that I have to do this check again.
#	if current_state.physics_modifier:
#		var mod_props :ModifierProperties = current_state.physics_modifier.get_modifier_properties()
#		if mod_props.move_speed != 0 and mod_props.directional_modifier == true:
#			# Since move_direction is always positive
#			if sign(move_direction) == sign(mod_props.move_speed):
#				_move_speed += mod_props.move_speed				
#			else:
#				_move_speed -= mod_props.move_speed
#				if sign(move_direction) == 0:
#					move_direction = sign(mod_props.move_speed) * -1
#					#print("doing this? ")


	# Determine the maximum move speed
	var MAX_SPEED :float = 0
	var MIN_SPEED :float = 0
	if sign(_move_speed_modifier) == -1: # decreased speed modifier
		help_me_not_be_dumb += '-SpeedMod'
		# Move speed cannot go below zero with modifier applied 
		MIN_SPEED = _move_speed + _move_speed_modifier
		# Poor man's clamp
		if MIN_SPEED < 0:
			MIN_SPEED = 0
		MAX_SPEED = _move_speed
	elif sign(_move_speed_modifier) == +1: # increased speed modifier
		help_me_not_be_dumb += '+SpeedMod'
		MIN_SPEED = _move_speed
		MAX_SPEED = _move_speed + _move_speed_modifier
	else: # physics won't apply here
		help_me_not_be_dumb += '_Speed' # Neutral Speed
		MIN_SPEED = _move_speed
		MAX_SPEED = _move_speed

	# get the latest aggregate acceleration
	# If we have any acceleration applying
	if (_move_modifier_move_acceleration + _move_acceleration) != 0:
		# The acceleration is differant
		# Perhaps it would be better to trigger this on a difference in modifier but they usually go together.
		if abs(acceleration.x) != abs(_move_modifier_move_acceleration + _move_acceleration):
			if move_direction:
				#print("Acceleration changed.", abs(acceleration.x), "-", (_move_modifier_move_acceleration + _move_acceleration))
				acceleration.x = _move_modifier_move_acceleration + _move_acceleration

	# If we're no longer tryingg to move int the direction of our movement and momentum.
	if sign(current_x_velocity) != sign(desired_movement_vector.x) and momentum.x != 0:
		if sign(_move_speed_modifier) == -1 : # decreased speed modifier

			# Maybe we can compare the direction of the acceleration
			# The direction of the acceleration should usually be positive at this point.
			# when the modifier is negative. 
			if sign(move_direction) == sign(current_x_velocity): 
				if sign(acceleration.x) == sign(momentum.x):
					print("Whoh Woah")
					# Flip the direction of the acceleration 
					acceleration.x *= -1
			if (sign(desired_movement_vector.x) == -1 and 
				sign(current_x_velocity) == 1) or (sign(desired_movement_vector.x) == -1 and 
				sign(current_x_velocity) == -1):
				print("be more opposite")
#		if sign(_move_speed_modifier) == 1: # increased speed modifier
#			print("faster faster.")
	elif sign(desired_movement_vector.x) == sign(current_x_velocity) and momentum.x != 0:
			if sign(acceleration.x) != sign(momentum.x) and x_move_direction_override == 0:
				print("Step it up!")
				# Flip the direction of the acceleration 
				acceleration.x *= -1

	# Apply momentum and acceleration if a modifer exists
	if momentum.x <= 0 and _move_speed_modifier !=0:
		#if we're trying to move but we have no momentum applied currently
		#if move_component.desired_movement_vector.x != 0:
		if move_direction:
			acceleration.x = _move_modifier_move_acceleration + _move_acceleration
			##TODO: I don't know where I should actually set the momentum. :
			if sign(_move_speed_modifier) == -1: # decreased speed modifier
				momentum.x = 0
			elif sign(_move_speed_modifier) == +1: # increased speed modifier
				##TODO: in most cases this should only be applied once. need to find a way to warn against this.
				momentum.x = abs(_move_speed_modifier)
				print("momentum applied!")

	# Reverse the accelerations if we carry over momentum but the modifier no longer applies. 
	if momentum.x > 0 and _move_speed_modifier == 0:
		if sign(acceleration.x) == sign(momentum.x):
			print("Whoh Woah your done.")
			# Flip the direction of the acceleration 
			acceleration.x *= -1

		
	# We're going to adjust our move speed only to the modifier 
	momentum.x += acceleration.x * delta
	##TODO: Apparently I disabled jerk some time ago for some reason.
	current_state.jerk += _jerk_factor * delta
	

	var new_move_speed :float = 0
	if sign(_move_speed_modifier) == -1: # decreased speed modifier
#		MIN_SPEED = _move_speed + _move_speed_modifier
#		MAX_SPEED = _move_speed
		momentum.x = clamp(momentum.x, 0, abs(_move_speed_modifier))
#		new_move_speed = (_move_speed + _move_speed_modifier ) + (sign(_move_speed_modifier) * -1 * move_component.momentum.x) # + jerk
		new_move_speed = MIN_SPEED + (sign(_move_speed_modifier) * -1 * momentum.x) # + jerk
	elif sign(_move_speed_modifier) == +1: # increased speed modifier
#		MIN_SPEED = _move_speed
#		MAX_SPEED = _move_speed + _move_speed_modifier
		momentum.x = clamp(momentum.x, 0, _move_speed_modifier )
		new_move_speed = _move_speed + momentum.x # + jerk
	else: # physics won't apply here
#		move_component.acceleration.x = 0
#		move_component.momentum.x = 0
#		new_move_speed = _move_speed 
		momentum.x = clamp(momentum.x, 0, MIN_SPEED )
		new_move_speed = _move_speed + momentum.x # + jerk

	#new_move_speed = clamp(new_move_speed, MIN_SPEED, MAX_SPEED)

	var sim_move_speed = (MIN_SPEED + (sign(_move_speed_modifier) * -1 * momentum.x))

#	if move_component.momentum.x != 0:
	if debug_component == true:
		UiManager.debug_text = ( "Mod(" + str(sign(_move_speed_modifier)) + ") Dir(" + str(move_direction) + ") Vel(" + str(sign(current_x_velocity)) + ") Mov(" + str(sign(desired_movement_vector.x)) +
			")\nNS:" + str(round(sim_move_speed))  + #" Z:" + str(sign(_move_speed_modifier) * -1 * move_component.momentum.x) + 
			"\nS:" + str(round(_move_speed + _move_speed_modifier)) + ",M" + str(round(momentum.x)) +  
			",A" + str(round(acceleration.x)) +
			"\nVel:" + str(round(velocity.x)) + "Min:" + str(round(MIN_SPEED)) + ",Max:" + str(round(MAX_SPEED))
		) # str(round(jerk))
		
	#sim_velocity.x = move_direction * sim_move_speed
	#print(adjusted_move_speed, ",", new_move_speed, ",", jerk)

	#print(new_move_speed, " ", adjusted_move_speed)

	velocity.y += _gravity * delta
	#parent.velocity = parent.move_and_slide(parent.velocity, Vector2(0, -1))
	#print(speed_multiplier)
#	move_component.velocity.x = move_component.desired_movement_vector.x * _move_speed
	velocity.x = move_direction * new_move_speed
	#move_component.velocity = parent.move_and_slide(move_component.velocity, Vector2.UP)
	var snap = Vector2.DOWN * 8
	if velocity.y < -8:
		snap = Vector2.ZERO
	velocity = parent.move_and_slide_with_snap(velocity, snap , Vector2.UP, true)

