# Manages currently carried out actions
# MODIFIED FOR RETURN TO MONKEY UI
extends ESCActionManager
class_name ESCActionManagerMonkey

# Set the current action verb
#
# ## Parameters
# - action: The action verb to set
func set_current_action(action: String):
	# MODIFIED FOR RETURN TO MONKEY UI
	if (action != current_action) && (action_state != ACTION_INPUT_STATE.AWAITING_TARGET_ITEM):
		clear_current_tool()

	current_action = action

	if action_state == ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM:
		set_action_input_state(ACTION_INPUT_STATE.AWAITING_ITEM)
	elif action_state == ACTION_INPUT_STATE.AWAITING_VERB:
		set_action_input_state(ACTION_INPUT_STATE.AWAITING_VERB_CONFIRM)

	emit_signal("action_changed")


# Checks if the specified action is valid and returns the associated event;
# otherwise, we see if there's a "fallback" event and use that if necessary and,
# if not, we return no event as there's nothing to do.
#
# #### Parameters
#
# - action: Action to execute (defined in attached ESC file and in
#   action verbs UI) eg: arrived, use, look, pickup...
# - target: Target ESC object
# - combine_with: ESC object to combine with
#
# *Returns* the appropriate ESCEvent to queue/run, or null if none can be found
# or there's a reason not to run an event.
func _get_event_to_queue(
	action: String,
	target: ESCObject,
	combine_with: ESCObject = null
) -> ESCEvent:

	escoria.logger.info(
		self,
		"Checking if action '%s' on '%s' is valid..." % [action, target]
	)

	var event_to_return: ESCEvent = null

	# If we're using an action which item requires to combine
	if target.node is ESCItem \
		and (action in target.node.combine_when_selected_action_is_in
		# MODIFIED FOR RETURN TO MONKEY UI
			or combine_with):
			# or (combine_with && action in combine_with.node.combine_when_selected_action_is_in)):

		# Player has item in inventory, we check the element to use on
		if escoria.inventory_manager.inventory_has(target.global_id):
			if combine_with:
				var target_event = "%s %s" % [
					action,
					combine_with.global_id
				]
				var combine_with_event = "%s %s" % [
					action,
					target.global_id
				]

				if target.events.has(target_event):
					event_to_return = target.events[target_event]
				elif combine_with.events.has(combine_with_event)\
						and not combine_with.node.combine_is_one_way:

					event_to_return = combine_with.events[combine_with_event]
				else:
					# Check to see if there isn't a "fallback" action to
					# run before we declare this a failure.
					if escoria.action_default_script \
						and escoria.action_default_script.events.has(action):

						event_to_return = escoria.action_default_script.events[action]
					else:
						var errors = [
							"Attempted to execute action %s between item %s and item %s" % [
								action,
								target.global_id,
								combine_with.global_id
							]
						]

						if combine_with.node.combine_is_one_way:
							errors.append(
								("Reason: %s's item interaction " + \
								"is one-way.") % combine_with.global_id
							)

						escoria.logger.warn(
							self,
							"Invalid action: " + str(errors)
						)
	else:
		if target.events.has(action):
			event_to_return = target.events[action]
		elif escoria.action_default_script \
			and escoria.action_default_script.events.has(action):

			# If there's a "fallback" action to run, return it
			event_to_return = escoria.action_default_script.events[action]
		else:
			escoria.logger.warn(
				self,
				"Invalid action: " +
					"Event for action %s on object %s not found." % [
						action,
						target.global_id
					]
			)

	return event_to_return


# Event handler when an object/item was clicked
#
# #### Parameters
#
# - obj: Object that was left clicked
# - event: Input event that was received
# - default_action: if true, run the inventory default action
func perform_inputevent_on_object(
	obj: ESCObject,
	event: InputEvent,
	default_action: bool = false
):
	"""
	This algorithm:
	- validates the requested action
	- grabs the corresponding event for the action, if available
	- makes the player move to the clicked object location, if needed
	(if it is located in the room for example) and wait for reaching.
	- when reached, performs an action depending on current defined action
		* no current action defined: do nothing else
		* current action defined:
			* item requires no combination: perform the current action
			  on the item
			* item requires combination: check the status of the combination
			  A combination requires 3 elements to fulfill:
				1/ a verb action
				2/ a first "tool" (item to use)
				3/ a second "tool" (item to use ON)
			  Whatever the user inputs to fulfill the combination (this is
			  determined by gamedev in his game.gd script)
				- combination not fulfilled: no not perform until fulfilled
				- combination fulfilled: perform the combination.
		* else do nothing, except if default_action is requested.
		  In this case, perform the default_action on the item.
	"""

	escoria.logger.info(
		self,
		"%s to perform event %s." % [obj.global_id, event]
	)

	# Don't interact after player movement towards object
	# (because object is inactive for example)
	var dont_interact = false

	# We need to have the new action input state BEFORE initiating the player
	# move so we determine now if the object clicked will require a combination
	# depending on the used action verb.
	var tool_just_set = _set_tool_and_action(obj, default_action)
	# MODIFIED FOR RETURN TO MONKEY UI
	var need_combine = _check_item_needs_combine_obj(obj)

	# If the current tool was not set, this is our first item, make it the tool
	if not current_tool or (current_tool and not need_combine):
		current_tool = obj
	# Else, if we have a tool and combination required, this is our second item,
	# make it the target.
	elif need_combine and not tool_just_set:
		current_target = obj

	# Update the action input state
	if action_state == ACTION_INPUT_STATE.AWAITING_TARGET_ITEM and current_target:
		set_action_input_state(ACTION_INPUT_STATE.COMPLETED)
	elif action_state == ACTION_INPUT_STATE.AWAITING_ITEM and \
			not need_combine:
		set_action_input_state(ACTION_INPUT_STATE.COMPLETED)
	elif action_state == ACTION_INPUT_STATE.AWAITING_ITEM and need_combine and not tool_just_set:
		set_action_input_state(ACTION_INPUT_STATE.AWAITING_TARGET_ITEM)

	var event_to_queue: ESCEvent = null

	# Manage exits
	if obj.node.is_exit and current_action in ["", ACTION_WALK]:
		event_to_queue = _get_event_to_queue(ACTION_EXIT_SCENE, obj)
	else:
		# Manage movements towards object before activating it
		if current_action in ["", ACTION_WALK] and \
				not escoria.inventory_manager.inventory_has(obj.global_id):
			event_to_queue = _get_event_to_queue(ACTION_ARRIVED, obj)
		# Manage action on object
		elif not current_action in ["", ACTION_WALK]:
			if need_combine and current_target:
				event_to_queue = _get_event_to_queue(
					current_action,
					current_tool,
					current_target
				)
			else:
				if need_combine:
					# We're missing a target here for our tool to be used on
					current_tool = obj
					set_action_input_state(
						ACTION_INPUT_STATE.AWAITING_TARGET_ITEM
					)

					# We need to wait for that target
					return
				else:
					event_to_queue = _get_event_to_queue(
						current_action,
						obj
					)

	# Get out of here if there's a specified action but an event couldn't be found.
	# Note that `event_to_queue` may still be null, but we do need to start the
	# player walking towards the destination.
	if current_action and not event_to_queue:
		clear_current_action()
		emit_signal("action_finished")
		return

	var event_flags = event_to_queue.flags if event_to_queue else 0

	if escoria.main.current_scene.player:
		var destination_position: Vector2 = escoria.main.current_scene.player \
				.global_position

		# If clicked object not in inventory, player walks towards it
		if not obj.node is ESCPlayer and \
			not escoria.inventory_manager.inventory_has(obj.global_id) and \
			not event_flags & ESCEvent.FLAG_TK:
				var context = _walk_towards_object(
					obj,
					event.position,
					event.doubleclick
				)

				if context is GDScriptFunctionState:
					context = yield(context, "completed")

				# In case of an interrupted walk, we don't want to proceed.
				if context == null:
					return

				destination_position = context.target_position
				dont_interact = context.dont_interact_on_arrival

		var player_global_pos = escoria.main.current_scene.player.global_position
		# var clicked_position = event.position

		# Using this instead of is_equal_approx due to
		# https://github.com/godotengine/godot/issues/65257
		if (player_global_pos - destination_position).length() > 1:
			dont_interact = true
			escoria.logger.info(
				self,
				"Player could not reach destination coordinates %s. "  % str(destination_position) \
					+ "Any requested action for %s will not fire." % obj.global_id
			)
			if escoria.event_manager.EVENT_CANT_REACH in obj.events:
				escoria.event_manager.queue_event(obj.events[escoria.event_manager.EVENT_CANT_REACH])
			else:
				escoria.logger.info(
					self,
					"%s event not found for object %s so nothing to do." % \
						[escoria.event_manager.EVENT_CANT_REACH, obj.global_id]
				)

	# If no interaction should happen after player has arrived, leave
	# immediately.
	if not dont_interact and event_to_queue:
		_run_event(event_to_queue)

# Prepare the "obj" object for current_action: if required, set the object as
# current tool.
#
# #### Parameters
#
# - obj: the ESCObject to prepare
# - default_action: if true, the default action set on the item is used
#
# *Returns* True if the tool was set in this function
func _set_tool_and_action(obj: ESCObject, default_action: bool):
	var tool_just_set: bool = false
	# Check if current_action and current_tool are already set
	if current_action and current_tool:
	# MODIFIED FOR RETURN TO MONKEY UI
		if (not current_action in escoria.action_manager\
				.current_tool.node.combine_when_selected_action_is_in and not current_action in obj.node.target_when_selected_action_is_in):
			current_tool = obj
			tool_just_set = true
	elif default_action:
		if escoria.inventory_manager.inventory_has(obj.global_id):
			current_action = obj.node.default_action_inventory
		else:
			current_action = obj.node.default_action
	elif current_action in obj.node.combine_when_selected_action_is_in:
		current_tool = obj
		tool_just_set = true
	return tool_just_set


# Checks if object requires a combination with another, according to
# currently selected action verb (or check with default action of the item).
#
# *Returns* True if current action on "obj" requires a combination
	# MODIFIED FOR RETURN TO MONKEY UI
func _check_item_needs_combine_obj(obj: ESCObject) -> bool:
	return current_action \
			and current_tool \
			and (current_action in current_tool.node.combine_when_selected_action_is_in
	# MODIFIED FOR RETURN TO MONKEY UI
			or current_action in  obj.node.target_when_selected_action_is_in)
