extends ESCGame

# Left mouse button on item
const ACTION1 = "action1"
# Right mouse button on item
const ACTION2 = "action2"
# Left mouse button on inventory item
const ACTION3 = "action3"
# Right mouse button on inventory item
const ACTION4 = "action4"

const VERB_USE = "use"
const VERB_WALK = "walk"


"""
Implement methods to react to inputs.

- left_click_on_bg(position: Vector2)
- right_click_on_bg(position: Vector2)
- left_double_click_on_bg(position: Vector2)

- element_focused(element_id: String)
- element_unfocused()

- left_click_on_item(item_global_id: String, event: InputEvent)
- right_click_on_item(item_global_id: String, event: InputEvent)
- left_double_click_on_item(item_global_id: String, event: InputEvent)

- left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- inventory_item_focused(inventory_item_global_id: String)
- inventory_item_unfocused()
- open_inventory()
- close_inventory()

- mousewheel_action(direction: int)

- hide_ui()
- show_ui()

- pause_game()
- unpause_game()
- show_main_menu()
- hide_main_menu()

- apply_custom_settings()

- _on_event_done(event_name: String)
"""

# Value to use for `device` argument to various `Input.get_joy` methods.
const JOY_DEVICE = 0

# See https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html?#dead-zone
const DEADZONE = 0.2

# Multiplier to apply to axis when it exceeds DEADZONE.
const AXIS_WEIGHT = 50.0

# JOY_BUTTON_2 corresponds to the "X" button on an XBox controller
# or the Square button on a Playstation controller. These appear to
# map to the "primary action," in practice, so we treat it like a left click.
const PRIMARY_ACTION_BUTTON = JOY_BUTTON_2

# JOY_BUTTON_3 corresponds to the "Y" button on an XBox controller
# or the Triangle button on a Playstation controller. These appear to
# map to the "secondary action," in practice, so we treat it like a right click.
const CHANGE_VERB_BUTTON = JOY_BUTTON_3

# Input action for use by InputMap
const ESC_UI_CHANGE_VERB_ACTION = "esc_change_verb"

# true when a gamepad is connected.
var _is_gamepad_connected = false

# Tracks the mouse's current position onscreen.
var _current_mouse_pos = Vector2.ZERO

# A reference to the node handling tooltip2
var tooltip2_node: Object
var last_target: Object

func _ready():
	# We need a slightly modified version of Action Manager to combine items with different actions.
	escoria.action_manager = ESCActionManagerMonkey.new()

	if $tooltip_layer/tooltip.connect("tooltip_size_updated", self, "update_tooltip_following_mouse_position", [tooltip_node]) != 0:
		escoria.logger.error(self, "Error connecting tooltip_size_updated with update_tooltip_following_mouse_position")

func _enter_tree():
	var room_selector_parent = $CanvasLayer/ui/HBoxContainer/VBoxContainer

	if ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.ENABLE_ROOM_SELECTOR) \
		and room_selector_parent.get_node_or_null("room_select") == null:

		room_selector_parent.add_child(
			preload(
				"res://addons/escoria-core/ui_library/tools/room_select" +\
				"/room_select.tscn"
			).instance()
		)

	var input_handler = funcref(self, "_process_input")
	escoria.inputs_manager.register_custom_input_handler(input_handler)

	_is_gamepad_connected = Input.is_joy_known(JOY_DEVICE)
	if _is_gamepad_connected:
		_on_gamepad_connected()

	if Input.connect("joy_connection_changed", self, "_on_joy_connection_changed") != 0:
		escoria.logger.error(self, "Error connecting joy_connection_changed")


func _exit_tree():
	escoria.inputs_manager.register_custom_input_handler(null)
	Input.disconnect("joy_connection_changed", self, "_on_joy_connection_changed")
	if _is_gamepad_connected:
		_on_gamepad_disconnected()


func _input(event: InputEvent) -> void:
	if escoria.get_escoria().is_ready_for_inputs():
		if event is InputEventMouseMotion:
			#_current_mouse_pos = get_global_mouse_position() # Escoria core
			_current_mouse_pos = get_viewport().get_mouse_position()
			update_tooltip_following_mouse_position(tooltip_node)
			update_tooltip_following_mouse_position(tooltip2_node)
			


# https://github.com/godotengine/godot-demo-projects/blob/3.4-585455e/misc/joypads/joypads.gd
# was informative in wiring up the gamepad properly.

func _on_gamepad_connected():
	set_physics_process(true)

	var primary_event = InputEventJoypadButton.new()
	primary_event.button_index = PRIMARY_ACTION_BUTTON
	InputMap.add_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)
	InputMap.action_add_event(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION, primary_event)

	var verb_event = InputEventJoypadButton.new()
	verb_event.button_index = CHANGE_VERB_BUTTON
	InputMap.add_action(ESC_UI_CHANGE_VERB_ACTION)
	InputMap.action_add_event(ESC_UI_CHANGE_VERB_ACTION, verb_event)


func _on_gamepad_disconnected():
	InputMap.action_erase_events(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)
	InputMap.erase_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)

	InputMap.action_erase_events(ESC_UI_CHANGE_VERB_ACTION)
	InputMap.erase_action(ESC_UI_CHANGE_VERB_ACTION)

	set_physics_process(false)
	_is_gamepad_connected = false


func _on_joy_connection_changed(device: int, connected: bool) -> void:
	if device != JOY_DEVICE:
		return
	elif connected:
		_on_gamepad_connected()
	else:
		_on_gamepad_disconnected()


func _process(_delta) -> void:
	if !_is_gamepad_connected:
		return

	var x = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_0)
	var y = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_1)
	var delta_x = int(x * AXIS_WEIGHT) if abs(x) > DEADZONE else 0
	var delta_y = int(y * AXIS_WEIGHT) if abs(y) > DEADZONE else 0
	if delta_x or delta_y:
		var direction = Vector2(delta_x, delta_y)
		escoria.logger.trace("gamepad direction:", [direction])
		var viewport = get_viewport()
		viewport.warp_mouse(viewport.get_mouse_position() + direction)


func _process_input(event: InputEvent, is_default_state: bool) -> bool:
	if not is_default_state:
		# ESCBackground is not guaranteed to be set, as we may be on
		# the "New Game" screen.
		return false
	elif _is_gamepad_connected and event is InputEventJoypadButton:
		escoria.logger.trace("InputEventJoypadButton:", [event.as_text()])
		if event.is_action_pressed(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION):
			# Admittedly, this breaks abstraction barriers and is completely
			# inappropriate, but it's what works right now.
			escoria.inputs_manager._on_left_click_on_bg(get_global_mouse_position())
			return true
		elif event.is_action_pressed(ESC_UI_CHANGE_VERB_ACTION):
			escoria.logger.error("Someone invoked ESC_UI_CHANGE_VERB_ACTION:", [event.as_text()])
			return true
	return false


## BACKGROUND ##

func click_on_bg(position: Vector2) -> void:
	# If we are using an inventory item reset mouse cursor and cancel action
	if escoria.action_manager.action_state == ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM:
		Input.set_custom_mouse_cursor(null)
		escoria.action_manager.clear_current_tool()

	if escoria.main.current_scene.player:
		escoria.action_manager.do(
			escoria.action_manager.ACTION.BACKGROUND_CLICK,
			[escoria.main.current_scene.player.global_id, position],
			true
		)

func left_click_on_bg(position: Vector2) -> void:
	click_on_bg(position)

func right_click_on_bg(position: Vector2) -> void:
	click_on_bg(position)

func left_double_click_on_bg(position: Vector2) -> void:
	if escoria.main.current_scene.player:
		escoria.action_manager.do(
			escoria.action_manager.ACTION.BACKGROUND_CLICK,
			[escoria.main.current_scene.player.global_id, position, true],
			true
		)

## ITEM/HOTSPOT FOCUS ##

func element_focused(element_id: String) -> void:
	var target_obj = escoria.object_manager.get_object(element_id).node
	if target_obj is ESCItem or ESCItemWithTooltip:
		$tooltip_layer/tooltip.set_target(target_obj.tooltip_name)
		$tooltip_layer/tooltip.set_target_object(target_obj)
		target_obj.highlight(true)
		if last_target != null:
			last_target.highlight(false)
		last_target = target_obj
		

func element_unfocused() -> void:
	$tooltip_layer/tooltip.set_target("")
	$tooltip_layer/tooltip.set_target_object(null)
	if(last_target != null):
		last_target.highlight(false)
		last_target = null



## ITEMS ##
func click_on_item(item_global_id: String, event: InputEvent, action: String) -> void:
	# If we are using an inventory item reset mouse cursor and use inventory actions (change action1 for action3 and action2 for action4)
	if escoria.action_manager.action_state == ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM:
		Input.set_custom_mouse_cursor(null)
		action = ACTION3 if action == ACTION1 else ACTION4

	escoria.action_manager.set_current_action(action)
	escoria.action_manager.do(
		escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
		[item_global_id, event],
		true
	)

	# Hide tooltip if some action is being performed
	if escoria.action_manager.action_state == ESCActionManager.ACTION_INPUT_STATE.COMPLETED:
		$tooltip_layer/tooltip.setHidden()

func left_click_on_item(item_global_id: String, event: InputEvent) -> void:
	click_on_item(item_global_id, event, ACTION1)

func right_click_on_item(item_global_id: String, event: InputEvent) -> void:
	click_on_item(item_global_id, event, ACTION2)

func left_double_click_on_item(item_global_id: String, event: InputEvent) -> void:
	escoria.action_manager.do(
		escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
		[item_global_id, event],
		true
	)


## INVENTORY ##
func click_on_inventory_item(item_global_id: String, event: InputEvent, action: String) -> void:
	# If we are using an inventory item reset mouse cursor
	if escoria.action_manager.action_state == ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM:
		Input.set_custom_mouse_cursor(null)

	escoria.action_manager.set_current_action(action)
	var target_obj = escoria.object_manager.get_object(item_global_id).node

	# If item needs combination with this action, use the item texture as mouse cursor
	if action in target_obj.combine_when_selected_action_is_in:
		Input.set_custom_mouse_cursor(target_obj.inventory_texture)

	escoria.action_manager.do(
		escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
		[item_global_id, event],
		true
	)
	
	# Hide tooltip if some action is being performed
	if escoria.action_manager.action_state == ESCActionManager.ACTION_INPUT_STATE.COMPLETED:
		$tooltip_layer/tooltip.setHidden()

func left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
	click_on_inventory_item(inventory_item_global_id, event, ACTION3)

func right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
	click_on_inventory_item(inventory_item_global_id, event, ACTION4)



func inventory_item_focused(inventory_item_global_id: String) -> void:
	var target_obj = escoria.object_manager.get_object(inventory_item_global_id).node
	if target_obj is ESCItemWithTooltip:
		$tooltip_layer/tooltip.set_target(target_obj.action3_text)
		$tooltip_layer/tooltip.set_target_object(target_obj)


func inventory_item_unfocused() -> void:
	element_unfocused()

func open_inventory():
	$CanvasLayer/ui/HBoxContainer/inventory_ui.show_inventory()

func close_inventory():
	$CanvasLayer/ui/HBoxContainer/inventory_ui.hide_inventory()

func hide_ui():
	$CanvasLayer/ui/HBoxContainer/inventory_ui.hide()
	$CanvasLayer/ui.hide()

func show_ui():
	$CanvasLayer/ui/HBoxContainer/inventory_ui.show()
	$CanvasLayer/ui.show()


func hide_main_menu():
	if get_node(main_menu).visible:
		get_node(main_menu).hide()

func show_main_menu():
	if not get_node(main_menu).visible:
		get_node(main_menu).reset()
		get_node(main_menu).show()

func unpause_game():
	if get_node(pause_menu).visible:
		get_node(pause_menu).hide()
		escoria.object_manager.get_object(ESCObjectManager.CAMERA).node.current = true
		escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.resume()
		escoria.main.current_scene.game.show_ui()
		escoria.main.current_scene.show()

func pause_game():
	if not get_node(pause_menu).visible:
		get_node(main_menu).reset()
		get_node(pause_menu).reset()
		get_node(pause_menu).set_save_enabled(
			escoria.save_manager.save_enabled
		)
		get_node(pause_menu).show()
		escoria.object_manager.get_object(ESCObjectManager.CAMERA).node.current = false
		escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.pause()
		escoria.main.current_scene.game.hide_ui()
		escoria.main.current_scene.hide()


func apply_custom_settings(custom_settings: Dictionary):
	if custom_settings.has("a_custom_setting"):
		escoria.logger.info(
			self,
			"custom setting value loaded: %s."
					% str(custom_settings["a_custom_setting"])
		)


func get_custom_data() -> Dictionary:
	return {
		"ui_type": "simplemouse"
	}


# Update the tooltip position
func update_tooltip_following_mouse_position(tooltip: ESCRichTooltip):
	if tooltip == null:
		return
	set_tooltip_position("tooltip1", tooltip, tooltip.get_tooltip1_size(), tooltip.offset_from_cursor_action1)
	set_tooltip_position("tooltip2", tooltip, tooltip.get_tooltip2_size(), tooltip.offset_from_cursor_action2)


func set_tooltip_position(nodeId: String, tooltip: ESCRichTooltip, size: Vector2, offset: Vector2):
	var mouse_position = calculateMousePosition(size)
	var corrected_position = correctPosition(tooltip, size, mouse_position, offset)
	tooltip.get_node(nodeId).position = corrected_position
	#tooltip.get_node(nodeId).position = mouse_position


func calculateMousePosition(size: Vector2):
	size.x / 2
	size.y / 2
	#escoria.logger.info(self, "_current_mouse_pos" + String(_current_mouse_pos.x) + " gameSize: " +  escoria.game_size.x)
 
	#return _current_mouse_pos - size
	return _current_mouse_pos

func correctPosition(tooltip: ESCRichTooltip, size: Vector2, mouse_position: Vector2, offset: Vector2):

	# clamp TOP
	if tooltip.tooltip_distance_to_edge_top(_current_mouse_pos) <= mouse_tooltip_margin:
		mouse_position.y = mouse_tooltip_margin

	# clamp BOTTOM
	if tooltip.tooltip_distance_to_edge_bottom(_current_mouse_pos + size) <= mouse_tooltip_margin:
		mouse_position.y = escoria.game_size.y - mouse_tooltip_margin - size.y

	# clamp LEFT
	if tooltip.tooltip_distance_to_edge_left(_current_mouse_pos - size/2) <= mouse_tooltip_margin:
		mouse_position.x = mouse_tooltip_margin

	# clamp RIGHT
	if tooltip.tooltip_distance_to_edge_right(_current_mouse_pos + size/2) <= mouse_tooltip_margin:
		mouse_position.x = escoria.game_size.x - mouse_tooltip_margin - size.x

	
	return mouse_position - offset

func _on_event_done(return_code: int, _event_name: String):
	escoria.logger.info(self, "EVENT DONE! code=" + String(return_code))

	# Reset mouse cursor (should be not needed, but avoids not resetting the cursor on bugs)
	Input.set_custom_mouse_cursor(null)
	
	# Show tooltips, they were hidden while performing action
	$tooltip_layer/tooltip.show()


func _on_MenuButton_pressed() -> void:
	pause_game()
