class_name StateAnimatedActor
extends State
## Animation and movement state class
##
## A state intended to control the movement and animation
## functions of a KinimaticBody2D node. Initialized with a
## player, movement node, and kinematicbody2d 
## I think if we also had modifier states that transfered along with the enter and entrance of nodes, that would be helpful.
##
## @WIP

# export (NodePath) var jump_node

# onready var jump_state: State = get_node(jump_node)

## Deprecated
# export var animation_name: String

# Movement Speed in Pixels Per Second
export var move_speed: float = 60
export var move_acceleration: float = 0
export var move_speed_modifier: float = 0
export var move_modifier_move_acceleration: float = 0
export var jerk_factor: float = 0

#var adjusted_move_speed: float
var jerk: float

var physics_modifier :StateModifier

##TODO: Deprecate this useless fool!
var speed_multiplier: float = 1.0

export(Array, String) var animation_sequence

signal frame_reached(state_name, animation_name, frame_number)
export(Dictionary) var emitter_frame_subscriptions
var frame_signal_emitted: bool = false

var animation_finished: bool = false


# this just wouldn't work well. Maybe I can try a resource 
# export(Array, NodePath) var transition_state_nodes

#TODO: I think these two variables accidentally serve the same purpose
# I think one was intended to be a string of the current animation name. 
var animation_index: int = 0
var current_animation_sequence: int = 0
var animation_suffix: String = '';

var mod_animation_sequence = [PoolStringArray()]

var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")

var animations: AnimatedSprite
var move_component: MovementComponent
var parent: KinematicBody2D

#animation_name :String, frame_number : int, animation_sequence :Array
var previous_animation_name : String
var previous_state_frame_number : int
var previous_state_name: String
var previous_speed_multiplier: float

var modifier: StateModifier

#var animation_sequence_timer: Timer



func _ready():
	##TODO: this bit me in the butt. Should add a safety or something.
	# Only add timout node if timer value was specified.
	if(timeout_seconds > 0.0):
		state_timeout = Timer.new()
		state_timeout.wait_time = timeout_seconds
		state_timeout.one_shot = true
		state_timeout.autostart = false
		add_child(state_timeout)
		
	#modifier = StateModifier.new()

# If called, this will attempt to make the most appropriate animation change
# would likely be triggered from an animation sequence finishing or a 
# modifier being applied/removed outside of enter/exit transitions. 
func update_animation(enter_frame := 0, index := 0, suffix := ''):
	## TODO: 
	# Need to find a way to attempt to keep and transfer the Index or frame
#
#	if clear_modifier:
#		print("Goodbye Modifiers!")
#		modifier = null
#	if modifier:
#		if modifier.state_timeout and modifier.timeout_seconds == 0:
#			modifier = null

	# By Default, we build an animation sequence off of this state's before checking
	# for modifiers
	mod_animation_sequence.clear()
	mod_animation_sequence.append_array(animation_sequence)
	# Reset animation suffix in case there isn't one
	animation_suffix = suffix
	animation_index = index
	current_animation_sequence = animation_index
	#var enter_animation = ''
	#var enter_frame = 0

	if modifier != null:
#		if modifier.modifier_type == modifier.TYPE.EXIT_ANIMATION:
#			#print("OHHH Modifier Applies! ", modifier.animation_name)
#			mod_animation_sequence.push_front(modifier.animation_name)
#			enter_frame = modifier.starting_frame
		match modifier.modifier_type:
			modifier.TYPE.EXIT_ANIMATION:
				mod_animation_sequence.push_front(modifier.animation_name)
				enter_frame = modifier.starting_frame

			modifier.TYPE.ANIMATION_SUFFIX:
				if animations.frames.has_animation(mod_animation_sequence[animation_index] + modifier.animation_name):
					animation_suffix = modifier.animation_name
				else:
					print("Warning!: Modifier attempting to apply suffix that doesn't exist ", 
												mod_animation_sequence[animation_index] + modifier.animation_name)

			modifier.TYPE.REPLACE_ANIMATION:
				#animations.play(modifier_stack_ref[i].animation_name)
				#animations.frame = modifier_stack_ref[i].starting_frame
				mod_animation_sequence.clear()
				mod_animation_sequence.push_back(modifier.animation_name)
				enter_frame = modifier.starting_frame

	# Some safety checks with supplied update parameters
	if animation_index != 0:
		if mod_animation_sequence.size() < animation_index + 1:
			if debug_state:
				print("Warning!: attempting to set to non-existant index ")
			animation_index  = 0
			current_animation_sequence = animation_index

	if animations.frames.has_animation(mod_animation_sequence[animation_index] + animation_suffix) == false:
		if debug_state:
			print("Warning!: Animation doesn't exist! ")
		
	if enter_frame != 0:
		if animations.frames.get_frame_count(animations.animation) != animations.frames.get_frame_count(mod_animation_sequence[animation_index] + animation_suffix):
			if debug_state:
				print("Warning!: attempting to set a mismatched frame ")
			enter_frame = 0

	if mod_animation_sequence.size() > 0:
		if debug_state:
			print("Starting Animation: " , mod_animation_sequence[animation_index] + animation_suffix)
		animations.play(mod_animation_sequence[animation_index] + animation_suffix)
		#TODO: maybe check current animatio has this frame
		animations.frame = enter_frame
	else:
		print("Error! Resolved to empty animation sequence!?")
	return


func enter() -> void:	
	if debug_state:
		print(parent.name, " entering State: ", self.name)
	move_component.current_movement_state = self.name
	emit_signal("state_entered")
	#modifier_stack_ref = state_modifiers
	update_animation()

	# Reset movespeed counters
	#move_component.momentum.x = move_speed_modifier
	jerk = 0

	return

func exit() -> void:
	emit_signal("state_exited")
#	animations.disconnect("animation_finished", self, "_on_AnimatedSprite_animation_finished")
	#modifier = null
	return

func transfer_modifiers(exiting_state_modifier : StateModifier):
	if modifier == null: # We have no existing modifier applied.
		match exiting_state_modifier.modifier_type:
			exiting_state_modifier.TYPE.ANIMATION_SUFFIX:
				if debug_state:
					print("Transferring Animation Suffix")
				modifier = exiting_state_modifier
			exiting_state_modifier.TYPE.EXIT_ANIMATION:
				if debug_state:
					print("Exit Animation: well this was useless.")
	else:
		print("Insert modifier merge function here...")

func process_input(_event: InputEvent) -> State:
	return null

func process_animations():

	if modifier != null:
		if modifier.state_timeout and modifier.state_timeout.time_left == 0:
			print("Expired Modifier, updating animation.")
			#modifier = null
			match modifier.modifier_type:

				modifier.TYPE.ANIMATION_SUFFIX:
					# Attempt to seemlessly transition animations.
					modifier = null
					update_animation(animations.frame,animation_index)

				modifier.TYPE.REPLACE_ANIMATION:
					if debug_state:
						print("I don't have anything for replacements yet.")
					modifier = null
					update_animation()
				_:
					modifier = null
			return

	
	# We have no more sequence animations and the current one doesn't loop
	# we do this to prevent the default idle animation from playing.
	##TODO: between this and the signal based iterator I get some really strange behavior
	if animation_index >= mod_animation_sequence.size() and animations.frames.get_animation_loop(
			animations.animation) == false:
		#print(animations.animation, " Animation Stopped")
		animations.stop()
		#animation_finished = true
	elif mod_animation_sequence.size() > animation_index and current_animation_sequence != animation_index:
		#TODO: why was I doing this
#		if animation_index >= mod_animation_sequence.size():
#			animation_index = 0
		##TODO: Add a safety check here. 
		animations.play(mod_animation_sequence[animation_index] + animation_suffix)
		if debug_state:
			print("An animation sequence: ", mod_animation_sequence[animation_index - 1], " -> ", 
				animations.animation , " (", animation_index, ")")
		current_animation_sequence = animation_index

	##TODO: Could probably add some more checks here.

	# Singal based frame call. 
	if emitter_frame_subscriptions.has(animations.frame):
		if emitter_frame_subscriptions[animations.frame] == animations.animation:
			if frame_signal_emitted == false:
				emit_signal("frame_reached", self.name, animations.animation, animations.frame)
				# Try to only so this once.
				frame_signal_emitted = true
	else:
		frame_signal_emitted = false

func process_frame(_delta: float) -> State:
	return null

func process_physics(_delta: float) -> State:
	move_actor_as_desired(_delta)
	return null

#func _on_AnimatedSprite_animation_finished():
#	if animations.frames.get_animation_loop(animations.animation) == false:
#		animation_index += 1

func move_actor_as_desired(delta: float, x_move_direction_override: float = 0):

	var _move_speed = move_speed
	var _move_speed_modifier = move_speed_modifier
	var _move_modifier_move_acceleration = move_modifier_move_acceleration
	var _move_acceleration = move_acceleration
	var _jerk_factor = jerk_factor
	var _gravity = gravity
	
	if 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 = 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 move_component.desired_movement_vector.x != 0:
		_move_speed = abs(move_component.desired_movement_vector.x)

	# Determine or physics movement direction	
	var current_x_velocity = move_component.velocity.x #move_component.velocity.x
	var move_direction = 0.0
	# Determine movement direction if we have momentum
	if move_component.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 = move_component.desired_movement_vector.x
		else:
			move_direction = x_move_direction_override

## TODO: I hate that I have to do this check again.
	if physics_modifier:
		var mod_props :ModifierProperties = 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(move_component.acceleration.x) != abs(_move_modifier_move_acceleration + _move_acceleration):
			if move_direction:
				print("Acceleration changed.", abs(move_component.acceleration.x), "-", (_move_modifier_move_acceleration + _move_acceleration))
				move_component.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(move_component.desired_movement_vector.x) and move_component.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(move_component.acceleration.x) == sign(move_component.momentum.x):
					print("Whoh Woah")
					# Flip the direction of the acceleration 
					move_component.acceleration.x *= -1
			if (sign(move_component.desired_movement_vector.x) == -1 and 
				sign(current_x_velocity) == 1) or (sign(move_component.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(move_component.desired_movement_vector.x) == sign(current_x_velocity) and move_component.momentum.x != 0:
			if sign(move_component.acceleration.x) != sign(move_component.momentum.x) and x_move_direction_override == 0:
				print("Step it up!")
				# Flip the direction of the acceleration 
				move_component.acceleration.x *= -1

	# Apply momentum and acceleration if a modifer exists
	if move_component.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:
			move_component.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
				move_component.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.
				move_component.momentum.x = abs(move_speed_modifier)
				print("momentum applied!")

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

		
	# We're going to adjust our move speed only to the modifier 
	move_component.momentum.x += move_component.acceleration.x * delta
	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
		move_component.momentum.x = clamp(move_component.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 * move_component.momentum.x) # + jerk
	elif sign(_move_speed_modifier) == +1: # increased speed modifier
#		MIN_SPEED = _move_speed
#		MAX_SPEED = _move_speed + _move_speed_modifier
		move_component.momentum.x = clamp(move_component.momentum.x, 0, _move_speed_modifier )
		new_move_speed = _move_speed + move_component.momentum.x # + jerk
	else: # physics won't apply here
#		move_component.acceleration.x = 0
#		move_component.momentum.x = 0
#		new_move_speed = _move_speed 
		move_component.momentum.x = clamp(move_component.momentum.x, 0, MIN_SPEED )
		new_move_speed = _move_speed + move_component.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 * move_component.momentum.x))

#	if move_component.momentum.x != 0:
	if true:
		UiManager.debug_text = ( "Mod(" + str(sign(_move_speed_modifier)) + ") Dir(" + str(move_direction) + ") Vel(" + str(sign(current_x_velocity)) + ") Mov(" + str(sign(move_component.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(move_component.momentum.x)) +  
			",A" + str(round(move_component.acceleration.x)) +
			"\nVel:" + str(round(move_component.velocity.x)) + "Min:" + str(round(MIN_SPEED)) + ",Max:" + str(round(MAX_SPEED))
		) # str(round(jerk))
		
	move_component.sim_velocity.x = move_direction * sim_move_speed
	#print(adjusted_move_speed, ",", new_move_speed, ",", jerk)

	#print(new_move_speed, " ", adjusted_move_speed)

	move_component.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
	move_component.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 move_component.velocity.y < -8:
		snap = Vector2.ZERO
	move_component.velocity = parent.move_and_slide_with_snap(move_component.velocity, snap , Vector2.UP, true)
