@tool
extends EditorPlugin


const BBCodeEdit: GDScript = preload("res://addons/bbcode_edit.editor/bbcode_edit.gd")
const Scraper = preload("res://addons/bbcode_edit.editor/editor_interface_scraper.gd")


const ADDON_NAME = "BBCode Editor"
const ACTION_OPEN_DOC = &"bbcode_edit/editor/open_current_file_documentation"
const ACTION_SETTINGS: Array[StringName] = [
	"input/" + ACTION_OPEN_DOC,
	"input/" + BBCodeEdit.ACTION_TOGGLE_BOLD,
	"input/" + BBCodeEdit.ACTION_TOGGLE_ITALIC,
	"input/" + BBCodeEdit.ACTION_TOGGLE_UNDERLINE,
	"input/" + BBCodeEdit.ACTION_TOGGLE_STRIKE,
]


func _enable_plugin() -> void:
	print("Enabling ", ADDON_NAME)
	add_keybinds()
	_on_editor_startup.call_deferred()
	print("Enabled ", ADDON_NAME)


func _disable_plugin() -> void:
	print("Disabling ", ADDON_NAME)
	for editor in EditorInterface.get_script_editor().get_open_script_editors():
		editor.get_base_editor().set_script(null)
	remove_keybinds()
	for setting in ACTION_SETTINGS:
		InputMap.erase_action(setting.substr(6))
	print("Disabled ", ADDON_NAME)


func _enter_tree() -> void:
	if not EditorInterface.has_meta(&"bbcode_edit_saved_once"):
		EditorInterface.set_meta(&"bbcode_edit_saved_once", PackedStringArray())
	_on_editor_startup.call_deferred()


var started_up: bool = false
func _on_editor_startup() -> void:
	if started_up:
		return
	started_up = true
	
	# TODO check if InputMap.load_from_project_settings() is better
	for setting in ACTION_SETTINGS:
		if !ProjectSettings.has_setting(setting):
			continue
		var action_dict: Dictionary = ProjectSettings.get_setting(setting)
		var action_name: StringName = setting.substr(6)
		InputMap.add_action(action_name, action_dict["deadzone"])
		for event in action_dict["events"]:
			InputMap.action_add_event(action_name, event)
	
	EditorInterface.get_script_editor().editor_script_changed.connect(check_current.unbind(1))
	check_current.call_deferred()


func add_keybinds() -> void:
	var toggle_bold := InputEventKey.new()
	toggle_bold.alt_pressed = true
	toggle_bold.keycode = 66
	ProjectSettings.set_setting(
		&"input/bbcode_edit/toggle_bold",
		{
			"deadzone": 0.5,
			"events": [toggle_bold],
		}
	)
	
	var toggle_italic := InputEventKey.new()
	toggle_italic.alt_pressed = true
	toggle_italic.keycode = 73
	ProjectSettings.set_setting(
		&"input/bbcode_edit/toggle_italic",
		{
			"deadzone": 0.5,
			"events": [toggle_italic],
		}
	)
	
	var toggle_underline := InputEventKey.new()
	toggle_underline.alt_pressed = true
	toggle_underline.keycode = 85
	ProjectSettings.set_setting(
		&"input/bbcode_edit/toggle_underline",
		{
			"deadzone": 0.5,
			"events": [toggle_underline],
		}
	)
	
	var toggle_strike := InputEventKey.new()
	toggle_strike.alt_pressed = true
	toggle_strike.keycode = _get_striketrough_keycode()
	ProjectSettings.set_setting(
		&"input/bbcode_edit/toggle_strike",
		{
			"deadzone": 0.5,
			"events": [toggle_strike],
		}
	)
	
	if Engine.is_editor_hint():
		add_editor_keybinds()
	
	ProjectSettings.save()
	print_rich("[color=orange]If you don't see the keybinds in the InputMap, please reload the Project.[/color]")	


func _get_striketrough_keycode() -> int:
	var editor_shortcuts: Variant = EditorInterface.get_editor_settings().get(&"shortcuts")
	
	if editor_shortcuts == null:
		return KEY_C
	
	for shortcut: Dictionary in editor_shortcuts:
		if shortcut["name"] == "bottom_panels/toggle_shader_editor_bottom_panel":
			var input_events: Array = shortcut.get("shortcuts")
			for input_event: InputEvent in input_events:
				if input_event.keycode == KEY_S:
					return KEY_C
			return KEY_S
	
	return KEY_C


func add_editor_keybinds() -> void:
	var open_current_file_documentation := InputEventKey.new()
	open_current_file_documentation.shift_pressed = true
	open_current_file_documentation.keycode = 4194332
	ProjectSettings.set_setting(
		"input/" + ACTION_OPEN_DOC,
		{
			"deadzone": 0.5,
			"events": [open_current_file_documentation],
		}
	)
	# NB: Initial values DON'T work, see [url]https://github.com/godotengine/godot/issues/56598[/url]
	#ProjectSettings.set_initial_value(&"input/bbcode_edit/editor/open_current_file_documentation", open_current_file_documentation.duplicate())
	


func remove_keybinds() -> void:
	ProjectSettings.set_setting("input/" + BBCodeEdit.ACTION_TOGGLE_BOLD, null)
	ProjectSettings.set_setting("input/" + BBCodeEdit.ACTION_TOGGLE_ITALIC, null)
	ProjectSettings.set_setting("input/" + BBCodeEdit.ACTION_TOGGLE_UNDERLINE, null)
	ProjectSettings.set_setting("input/" + BBCodeEdit.ACTION_TOGGLE_STRIKE, null)
	
	# This calls ProjectSettings.save(), so please call it last
	remove_editor_keybinds()


func remove_editor_keybinds() -> void:
	ProjectSettings.set_setting("input/" + ACTION_OPEN_DOC, null)
	ProjectSettings.save()


func check_current() -> void:
	var current_editor := EditorInterface.get_script_editor().get_current_editor()
	if current_editor == null:
		return
	check_bbcode_pretendant(current_editor.get_base_editor())


func check_bbcode_pretendant(pretendant: Control) -> void:
	if pretendant is CodeEdit and not pretendant.has_meta(&"BBCode_utilities"):
		add_bbcode_handling(pretendant)


func add_bbcode_handling(code_edit: CodeEdit) -> void:
	# TODO MAYBE implement automatic script inheritence if script is already overriden by another addon 
	code_edit.set_meta(&"never_changed", true)
	code_edit.set_script(BBCodeEdit)


## [b]WARING:[/b] not fully implemented for non-current script
func open_doc(script: Script, code_edit: CodeEdit = null) -> void:
	var class_name_: String = script.get_global_name()
	
	if class_name_ == "":
		class_name_ = '"' + script.resource_path.trim_prefix("res://") + '"'
		var bbcode_edit_saved_once: PackedStringArray = EditorInterface.get_meta(&"bbcode_edit_saved_once", PackedStringArray())
		if code_edit and class_name_ not in bbcode_edit_saved_once:
			bbcode_edit_saved_once.append(class_name_)
			print_rich("[color=orange]The script never changed since startup: brute-forcing documentation generation. (See [url=https://github.com/godotengine/godot/pull/95821]godot#95821[/url])[/color]")
			code_edit.text = code_edit.text
			EditorInterface.save_all_scenes()
		elif Scraper.is_current_script_unsaved():
			# TODO ↑ Fix this for non-current script
			print_rich("[color=orange]Saving to make godot generate documentation.[/color]")
			EditorInterface.save_all_scenes()
	
	elif Scraper.is_current_script_unsaved():
		print_rich("[color=orange]Saving to make godot generate documentation.[/color]")
		EditorInterface.save_all_scenes()
	
	EditorInterface.get_script_editor().get_current_editor().go_to_help.emit.call_deferred("class_name:"+class_name_)



func _unhandled_input(event: InputEvent) -> void:
	if (
		InputMap.has_action(ACTION_OPEN_DOC)
		and InputMap.event_is_action(event, ACTION_OPEN_DOC, true)
	):
		# TODO find a workaround for the appearance delay of (*) to check unsaved status.
		var current_editor := EditorInterface.get_script_editor().get_current_editor()
		if current_editor == null:
			return
		
		var code_edit := current_editor.get_base_editor()
		if code_edit is CodeEdit:
			open_doc(EditorInterface.get_script_editor().get_current_script(), code_edit)
