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_speed_decay: float = 0
export var move_speed_modifier: float = 0
export var move_speed_modifier_decay: float = 0
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

# 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 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)


func enter() -> void:
#	animations.connect("animation_finished", self, "_on_AnimatedSprite_animation_finished")
	mod_animation_sequence.clear()
	#mod_animation_sequence = animation_sequence.duplicate(true)
	mod_animation_sequence.append_array(animation_sequence)
	# Reset animation suffix in case there isn't one
	animation_suffix = ''
	animation_index = 0
	current_animation_sequence = 0
	#var enter_animation = ''
	var enter_frame = 0
	
	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
	
	# A new attempt at animation modifiers
	if modifier_stack_ref.empty() == false:
		print("nope: ")
		var i = modifier_stack_ref.size() - 1
		##NOTE: i apparently reverse range itteration doesn't work
		#for i in range(modifier_stack_ref.size(), 0): # work in reverse
		while (i >= 0 and modifier_stack_ref[i].modifier_type != "None"):
			print("Animation Modifier!: ",modifier_stack_ref[i].modifier_type)
			match modifier_stack_ref[i].modifier_type:
				"Exit Animation":
					mod_animation_sequence.push_front(modifier_stack_ref[i].animation_name)
					enter_frame = modifier_stack_ref[i].starting_frame
					
					# Gonna ty and avoid this for now
					#modifier_stack_ref.pop_at(i) # we're done with this modifier
					
				"Animation Suffix":
					if animations.frames.has_animation(mod_animation_sequence[animation_index] + modifier_stack_ref[i].animation_name):
						animation_suffix = modifier_stack_ref[i].animation_name
					else:
						print("Warning!: Animation suffix that doesn't exist ", 
							mod_animation_sequence[animation_index] + modifier_stack_ref[i].animation_name)
				"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_stack_ref[i].animation_name)
					enter_frame = modifier_stack_ref[i].starting_frame
			i -= 1


#	if modifier_stack_ref.empty() == false: # a modifier is applied
#		if modifier_stack_ref[-1].animation_name != '': # the first one has an animation
#			if modifier_stack_ref[-1].animation_suffix: # it's a suffix type
#				if mod_animation_sequence.size() > 0: # the current animation is multipart (sequence > 0)
#					animation_index = 0
#					current_animation_sequence = 0
#					# Guess I don't need this one anymore
#					#if animations.frames.get_animation_names().has(animation_sequence[animation_index] + modifier_stack_ref[-1].animation_name): 
#					if animations.frames.has_animation(mod_animation_sequence[animation_index] + modifier_stack_ref[-1].animation_name):
#						animation_suffix = modifier_stack_ref[-1].animation_name
#			elif modifier_stack_ref[-1].pre_append_animation == true: # it's a pre-append type
#				#animation_sequence.size()
#				print ("DEBUG: A prepend animation")
#				#mod_animation_sequence = animation_sequence
#				mod_animation_sequence.push_front(modifier_stack_ref[-1].animation_name)
#			else:
#				animations.play(modifier_stack_ref[-1].animation_name)
#				animations.frame = modifier_stack_ref[-1].starting_frame
#				return

	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 exit() -> void:
	emit_signal("state_exited")
#	animations.disconnect("animation_finished", self, "_on_AnimatedSprite_animation_finished")
	return

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

func process_frame(_delta: float) -> State:
	#if animation_sequence.size() > 0 and current_animation_sequence != animation_index:
	if 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
	# We have no more sequence animations and the current one doesn't loop
	# we do this to prevent the default idle animation from playing.
	if animation_index >= mod_animation_sequence.size() and animations.frames.get_animation_loop(
			animations.animation) == false:
		animations.stop()

	# 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

	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):
	var new_speed = move_speed * speed_multiplier
	if speed_multiplier > 1.0:
		speed_multiplier -= delta *  speed_multiplier
		#speed_decay_rate += delta * speed_decay_rate
		
	elif speed_multiplier < 1.0:
		speed_multiplier += delta * speed_multiplier
		#speed_decay_rate += delta * speed_decay_rate
	
	# Deadzone
	if speed_multiplier < 1.1 and speed_multiplier > 0.9:
		speed_multiplier = 1.0
#	if (new_speed > move_speed):
#		print("Go Faster Booooy! ", move_speed)
	move_component.velocity.y += gravity * delta
	#parent.velocity = parent.move_and_slide(parent.velocity, Vector2(0, -1))
	move_component.velocity.x = move_component.desired_movement_vector.x * new_speed
	move_component.velocity = parent.move_and_slide(move_component.velocity, Vector2(0, -1))
	
func push_animation_state_modifier(modifier: StateModifier):
	## This function attempt to optimally place multiple modifiers in an array
	# in such a way where the first elements are modifiers without animation changes
	# the last elements are animation changes or animation suffixes.
	# there is some potential for performance issues at scale with this kinds of operations. 
	
	# This function will grow in complexity as needed but the idea is that here is
	# where different modifier logic can be tested and developed.
	
	# First assure this modifer isn't already there. If it is, maybe I should reset it?
	if modifier_stack_ref.has(modifier) == false:
		modifier.enter() # call enter function of the modifier
		if modifier_stack_ref.size() > 0: # only bother with this if multiple modifers applied
			# Get the last modifier in the stack 
			var last_modifer: StateModifier = modifier_stack_ref[-1]
			# If the modifier at the back and the new one are the same type, we have a problem.
			if last_modifer.modifier_type == modifier.modifier_type and last_modifer.modifier_type != 'None':
				print("WARNING: Multiple same type modifiers applying.")
				# Replace the modifier
				modifier_stack_ref.pop_back()
				modifier_stack_ref.push_back(modifier)
			else:
				match last_modifer.modifier_type:
					"None":
						# no special processing, push it to the front of a list
						modifier_stack_ref.push_front(modifier)
					"Exit Animation":
						if modifier.modifier_type == "Animation Suffix":
							# We place this at the back so we can still modify it maybe? 
							modifier_stack_ref.push_back(modifier)
					"Animation Suffix":
						if modifier.modifier_type == "Exit Animation":
							# We have to place this one right before maybe? 
							# Whoops! Forgot to actually do this
							#modifier_stack_ref.push_back(modifier)
							modifier_stack_ref.insert(modifier_stack_ref.size() - 1, modifier)
#			if last_modifer.animation_name != '': # and if an animation applies
#				if last_modifer.pre_append_animation == modifier.pre_append_animation: # They're the same type
#					if modifier.pre_append_animation == true and modifier_stack_ref[-1].animation_suffix == true:
#						modifier.animation_suffix
#						modifier_stack_ref.push_back(modifier)
		else:
			modifier_stack_ref.push_front(modifier)
		

func remove_animation_state_modifiers():
	for i in modifier_stack_ref.size():
		if modifier_stack_ref[i].modifier_type != "None":
			#print("Time to pop state modifer: ", state_modifiers[i].animation_name, current_state.animation_suffix, " <-> ", current_state.animations.animation + current_state.animation_suffix)
			modifier_stack_ref.pop_at(i)
