@tool
extends EditorPlugin
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#	Script Spliter
#	https://github.com/CodeNameTwister/Script-Spliter
#
#	Script Spliter addon for godot 4
#	author:		"Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

const BUILDER : Script = preload("res://addons/script_spliter/core/builder.gd")
const CONTEXT : Script = preload("res://addons/script_spliter/context/context_window.gd")

const CMD_MENU_TOOL : String = "Script Spliter"

#CONTEXT
const ICON_ADD_COLUMN : Texture = preload("res://addons/script_spliter/context/icons/split_cplus.svg")
const ICON_REMOVE_COLUMN : Texture = preload("res://addons/script_spliter/context/icons/split_cminus.svg")
const ICON_ADD_ROW : Texture = preload("res://addons/script_spliter/context/icons/split_rplus.svg")
const ICON_REMOVE_ROW : Texture = preload("res://addons/script_spliter/context/icons/split_rminus.svg")
const ICON_FLOATING : Texture = preload("res://addons/script_spliter/context/icons/atop.png")
const ICON_TAB : Texture = preload("res://addons/script_spliter/assets/tab_icon.svg")

var _rmb_editor_add_split : EditorContextMenuPlugin = null
var _rmb_editor_remove_split: EditorContextMenuPlugin = null
var _rmb_editor_code_add_split : EditorContextMenuPlugin = null
var _rmb_editor_code_remove_split : EditorContextMenuPlugin = null
var _rmb_editor_pop_script : EditorContextMenuPlugin = null
var _rmb_editor_code_pop_script : EditorContextMenuPlugin = null

var _rmb_close_all_tab_in_split : EditorContextMenuPlugin = null
var _rmb_close_all_tab_in_split_right : EditorContextMenuPlugin = null
var _rmb_close_all_tab_in_split_left : EditorContextMenuPlugin = null

var _menu_split_selector : Window = null
var _builder : Object = null

var _daemon_chaser : Node = null

#region _REF_
var _tab_container : Node = null:
	get:
		if !is_instance_valid(_tab_container):
			var script_editor: ScriptEditor = EditorInterface.get_script_editor()
			_tab_container = find(script_editor, "*", "TabContainer")
		return _tab_container
		
var _item_list : Node = null:
	get:
		if !is_instance_valid(_item_list):
			var script_editor: ScriptEditor = EditorInterface.get_script_editor()
			_item_list = find(script_editor, "*", "ItemList")
		return _item_list
#endregion

#region _USER_BUFFER_
var _rows : int = 0:
	set(e):
		_rows = maxi(e, 0)
var _columns : int = 0:
	set(e):
		_columns = maxi(e, 0)
var _refresh_warnings_on_save : bool = true
#endregion

var _frm : int = 0
var _d_chase : bool = false

func get_builder() -> Object:
	return _builder

func find(root : Node, pattern : String, type : String) -> Node:
	var e : Array[Node] = root.find_children(pattern, type, true, false)
	if e.size() > 0:
		return e[0]
	return null

func get_split_rows() -> int:
	return _rows

func get_split_columns() -> int:
	return _columns
	
func _on_save(res : Resource) -> void:
	if res is Script:
		_save_external_data()
	
func _save_external_data() -> void:
	if _d_chase == true:
		return
	_d_chase = true
	if _refresh_warnings_on_save and is_instance_valid(_builder):
		if _daemon_chaser == null:
			_daemon_chaser = ResourceLoader.load("res://addons/script_spliter/core/DaemonChaser.gd").new()
			add_child(_daemon_chaser)
		_daemon_chaser.set_current_index(_builder.get_current_focus_index())
		_daemon_chaser.buffer = _builder.get_focus_config()
		_builder.enable_focus_highlight(false)
		_daemon_chaser.run(_builder.focus_by_index, _builder.set_focus_config)
	set_deferred(&"_d_chase", false)

func _process(__: float) -> void:
	if _frm < 2:
		_frm += 1
		return
	_frm = 0
	set_process(false)
	if is_instance_valid(_builder):
		_builder.update()

func _on_change_settings() -> void:
	if is_instance_valid(_builder):
		_builder.update_config()
		var settings : EditorSettings = EditorInterface.get_editor_settings()
		var changes : PackedStringArray = settings.get_changed_settings()
		
		if &"plugin/script_spliter/behaviour/refresh_warnings_on_save" in changes:
			_refresh_warnings_on_save = settings.get_setting(&"plugin/script_spliter/behaviour/refresh_warnings_on_save")
			
func _init() -> void:
	var o : Object = _tab_container
	if o == null:
		#push_warning("[Script-Spliter] 0x000A")
		return
	o = _item_list
	if o == null:
		#push_warning("[Script-Spliter] 0x000B")
		return

func _run() -> void:
	if is_instance_valid(_builder):
		var settings : EditorSettings = EditorInterface.get_editor_settings()
		var scripts_tab_container : Node = _tab_container
		
		if !scripts_tab_container:
			push_warning("[Script-Spliter] 0x000A Error can not find editor reference!")
			return
		
		var il : Node = _item_list
		if null != il:
			_builder.set_item_list(il)
		else:
			push_warning("[Script-Spliter] 0x000B Error can not find editor reference!")
			return
	
		if  !scripts_tab_container.is_node_ready():
			await scripts_tab_container.ready
			if !is_instance_valid(scripts_tab_container):
				push_error("Unspected error reference be replace or free it, can not run plugin!")
				return

		settings.settings_changed.connect(_on_change_settings)

		_builder.init_1()
		_builder.build(scripts_tab_container, _columns, _rows)
		set_process_input(true)

func set_type_split(columns : int, rows : int) -> void:
	_columns = columns
	_rows = rows
	
	var str_columns : String = str(maxi(_columns, 1))
	var str_rows : String = str(maxi(rows, 1))
	
	print("[{0}] {1} {2}: > {3} {5} - {4} {6}".format(
		[
			_get_translated_text("INFO"),
			_get_translated_text("Setting"),
			_get_translated_text("To"),
			_get_translated_text("Columns"),
			_get_translated_text("Rows"),
			str_columns, 
			str_rows
			]) )

	if is_instance_valid(_builder):
		_builder.update_build(columns, rows)

func _exit_tree() -> void:
	remove_tool_menu_item(CMD_MENU_TOOL)
	_setup(0)

	if is_instance_valid(_builder):
		_builder.init_0()
		_builder.free.call_deferred()

	var settings : EditorSettings = EditorInterface.get_editor_settings()
	if settings.settings_changed.is_connected(_on_change_settings):
		settings.settings_changed.disconnect(_on_change_settings)
		
	if is_instance_valid(_daemon_chaser) and !_daemon_chaser.is_queued_for_deletion():
		_daemon_chaser.queue_free()

func _get_translated_text(text : String) -> String:
	# TODO: Translation
	return text

func swap(caller : Object) -> void:
	if is_instance_valid(_builder):
		_builder.swap(caller)

func _setup(input : int) -> void:
	var settings : EditorSettings = EditorInterface.get_editor_settings()

	if input != 0:
		add_to_group(&"ScriptSpliter")
		
		main_screen_changed.connect(_on_change)
		resource_saved.connect(_on_save)
		
		var ctx_add_column : String = _get_translated_text("ADD_SPLIT").capitalize()
		var ctx_remove_split : String = _get_translated_text("REMOVE_SPLIT").capitalize()
		var ctx_pop_script : String = _get_translated_text("MAKE_FLOATING_SCRIPT").capitalize()
		var ctx_close_tabs : String = _get_translated_text("CLOSE_OTHERS_TABS_IN_SPLIT").capitalize()
		var ctx_close_tabs_right : String = _get_translated_text("CLOSE_RIGHT_TABS_IN_SPLIT").capitalize()
		var ctx_close_tabs_left : String = _get_translated_text("CLOSE_LEFT_TABS_IN_SPLIT").capitalize()

		#SETUP
		_rmb_editor_add_split = CONTEXT.new(ctx_add_column, _add_window_split, _can_add_split, ICON_ADD_COLUMN)
		_rmb_editor_remove_split = CONTEXT.new(ctx_remove_split, _remove_window_split, _can_remove_split, ICON_REMOVE_COLUMN)

		_rmb_editor_code_add_split = CONTEXT.new(ctx_add_column, _add_window_split, _can_add_split, ICON_ADD_COLUMN)
		_rmb_editor_code_remove_split = CONTEXT.new(ctx_remove_split, _remove_window_split, _can_remove_split, ICON_REMOVE_COLUMN)

		_rmb_editor_pop_script = CONTEXT.new(ctx_pop_script, _make_pop_script, _can_make_pop_script, ICON_FLOATING)
		_rmb_editor_code_pop_script = CONTEXT.new(ctx_pop_script, _make_pop_script, _can_make_pop_script, ICON_FLOATING)

		_rmb_close_all_tab_in_split = CONTEXT.new(ctx_close_tabs, _close_all_tabs_in_split, _can_close_tab_in_split, ICON_TAB)
		_rmb_close_all_tab_in_split_left = CONTEXT.new(ctx_close_tabs_left, _close_all_tabs_in_split_left, _can_close_left_tab_in_split, ICON_TAB)
		_rmb_close_all_tab_in_split_right = CONTEXT.new(ctx_close_tabs_right, _close_all_tabs_in_split_right, _can_close_right_tab_in_split, ICON_TAB)
		
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_close_all_tab_in_split)
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_close_all_tab_in_split_left)
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_close_all_tab_in_split_right)
		

		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_editor_add_split)
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_editor_remove_split)

		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _rmb_editor_code_add_split)
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _rmb_editor_code_remove_split)
		
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _rmb_editor_pop_script)
		add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _rmb_editor_code_pop_script)

		if !settings.has_setting(&"plugin/script_spliter/rows"):
			settings.set_setting(&"plugin/script_spliter/rows", _rows)
		else:
			_rows = settings.get_setting(&"plugin/script_spliter/rows")
		if !settings.has_setting(&"plugin/script_spliter/columns"):
			settings.set_setting(&"plugin/script_spliter/columns", _columns)
		else:
			_columns = settings.get_setting(&"plugin/script_spliter/columns")
		if !settings.has_setting(&"plugin/script_spliter/save_rows_columns_count_on_exit"):
			settings.set_setting(&"plugin/script_spliter/save_rows_columns_count_on_exit", false)
		if !settings.has_setting(&"plugin/script_spliter/behaviour/refresh_warnings_on_save"):
			settings.set_setting(&"plugin/script_spliter/behaviour/refresh_warnings_on_save", _refresh_warnings_on_save)
		else:
			_refresh_warnings_on_save = settings.get_setting(&"plugin/script_spliter/behaviour/refresh_warnings_on_save")
	else:
		if is_in_group(&"ScriptSpliter"):
			remove_from_group(&"ScriptSpliter")
			
		main_screen_changed.disconnect(_on_change)
		resource_saved.disconnect(_on_save)

		if is_instance_valid(_rmb_close_all_tab_in_split):
			remove_context_menu_plugin(_rmb_close_all_tab_in_split)
		if is_instance_valid(_rmb_close_all_tab_in_split_right):
			remove_context_menu_plugin(_rmb_close_all_tab_in_split_right)
		if is_instance_valid(_rmb_close_all_tab_in_split_left):
			remove_context_menu_plugin(_rmb_close_all_tab_in_split_left)		
		
		if is_instance_valid(_rmb_editor_add_split):
			remove_context_menu_plugin(_rmb_editor_add_split)
		if is_instance_valid(_rmb_editor_remove_split):
			remove_context_menu_plugin(_rmb_editor_remove_split)
		if is_instance_valid(_rmb_editor_code_add_split):
			remove_context_menu_plugin(_rmb_editor_code_add_split)
		if is_instance_valid(_rmb_editor_code_remove_split):
			remove_context_menu_plugin(_rmb_editor_code_remove_split)
		if is_instance_valid(_rmb_editor_pop_script):
			remove_context_menu_plugin(_rmb_editor_pop_script)
		if is_instance_valid(_rmb_editor_code_pop_script):
			remove_context_menu_plugin(_rmb_editor_code_pop_script)


		if settings.has_setting(&"plugin/script_spliter/save_rows_columns_count_on_exit"):
			if settings.get_setting(&"plugin/script_spliter/save_rows_columns_count_on_exit") == true:
				settings.set_setting(&"plugin/script_spliter/rows", _rows)
				settings.set_setting(&"plugin/script_spliter/columns", _columns)

func _can_close_tab_in_split(path : PackedStringArray) -> bool:
	return _builder.has_other_tabs()
	
func _can_close_right_tab_in_split(path : PackedStringArray) -> bool:
	return _builder.has_right_tabs()
	
func _can_close_left_tab_in_split(path : PackedStringArray) -> bool:
	return _builder.has_left_tabs()

func _close_all_tabs_in_split(__ : Variant) -> void:
	_builder.close_other_tabs()
	
func _close_all_tabs_in_split_right(__ : Variant) -> void:
	_builder.close_right_tabs()
	
func _close_all_tabs_in_split_left(__ : Variant) -> void:
	_builder.close_left_tabs()
	
func _can_add_split(path : PackedStringArray) -> bool:
	if !is_instance_valid(_builder):
		return false
	if path.size() == 0:
		return _builder.can_add_split(null)
	else:
		for x : String in path:
			if x.begins_with("res://"):
				var sc : ScriptEditor = EditorInterface.get_script_editor()
				return _builder.can_add_split(sc.get_current_editor().get_base_editor())
			else:
				var node : Node = get_node_or_null(x)
				if node:
					return _builder.can_add_split(node)
				else:
					var sc : ScriptEditor = EditorInterface.get_script_editor()
					return _builder.can_add_split(sc.get_current_editor().get_base_editor())
	return false

func _can_remove_split(path : PackedStringArray) -> bool:
	if !is_instance_valid(_builder):
		return false
	if path.size() == 0:
		return _builder.can_remove_split(null)
	else:
		for x : String in path:
			if x.begins_with("res://"):
				var sc : ScriptEditor = EditorInterface.get_script_editor()
				return _builder.can_remove_split(sc.get_current_editor().get_base_editor())
			else:
				var node : Node = get_node_or_null(x)
				if node:
					return _builder.can_remove_split(node)
				else:
					var sc : ScriptEditor = EditorInterface.get_script_editor()
					return _builder.can_remove_split(sc.get_current_editor().get_base_editor())
	return false

func _add_window_split(variant : Variant) -> void:
	var control : Control = null
	if variant is Script:
		var sc : ScriptEditor = EditorInterface.get_script_editor()
		if variant == sc.get_current_script():
			control = sc.get_current_editor().get_base_editor()
		else:
			var c : ScriptEditorBase = sc.get_current_editor()
			if c:
				control = c.get_base_editor()
	elif variant is CodeEdit:
		control = variant
	if is_instance_valid(control):
		_builder.add_split(control)

func _remove_window_split(variant : Variant) -> void:
	var control : Control = null
	if variant is Script:
		var sc : ScriptEditor = EditorInterface.get_script_editor()
		if variant == sc.get_current_script():
			control = sc.get_current_editor().get_base_editor()
		else:
			var c : ScriptEditorBase = sc.get_current_editor()
			if c:
				control = c.get_base_editor()
	elif variant is CodeEdit:
		control = variant
	if is_instance_valid(control):
		_builder.remove_split(control)

func _make_pop_script(variant : Variant) -> void:
	var control : Control = null
	if variant is Script:
		var sc : ScriptEditor = EditorInterface.get_script_editor()
		var arr : Array[ScriptEditorBase] = sc.get_open_script_editors()
		var scs : Array[Script] = sc.get_open_scripts()
		if arr.size() == scs.size():
			for y : int in range(0, scs.size(), 1):
				if scs[y] == variant:
					control = arr[y].get_base_editor()
					break
	if variant is CodeEdit:
		control = variant
	if is_instance_valid(control):
		_builder.make_pop_script(control)

func _can_make_pop_script(path : PackedStringArray) -> bool:
	if !is_instance_valid(_builder):
		return false
	for x : String in path:
		var node : Node = get_node_or_null(x)
		if node:
			return !_builder.is_pop_script(node)
	return false

func _on_change(screen_name : String) -> void:
	if screen_name == "Script":
		if is_instance_valid(_builder):
			_builder.update_rect.call_deferred()
	

func _enter_tree() -> void:
	_setup(1)
	if !is_instance_valid(_builder):
		_builder = BUILDER.new(self)
	add_tool_menu_item(CMD_MENU_TOOL, _on_tool_command)

func _on_tool_command() -> void:
	if is_instance_valid(_builder):
		var data : Array[int] = _builder.get_current_columns_and_rows()
		_columns = data[0]
		_rows = data[1]
	
	if !is_instance_valid(_menu_split_selector):
		_menu_split_selector = (ResourceLoader.load("res://addons/script_spliter/context/menu_tool.tscn") as PackedScene).instantiate()
		_menu_split_selector.set_plugin(self)
		_menu_split_selector.visible = false
		add_child(_menu_split_selector)
	_menu_split_selector.popup_centered.call_deferred()

func _ready() -> void:
	set_process(false)
	set_process_input(false)
	if !get_tree().root.is_node_ready():
		await get_tree().root.ready
	for __ : int in range(2):
		await get_tree().process_frame
	_run()

func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		if is_instance_valid(_builder) and !_builder.is_queued_for_deletion():
			_builder.init_0()
			_builder.free()

func _input(event: InputEvent) -> void:
	if event is InputEventKey:
		if event.pressed and event.ctrl_pressed:
			if event.keycode == 49:
				set_type_split(0, 0)
			elif event.keycode == 50:
				set_type_split(2, 1)
			elif event.keycode == 51:
				set_type_split(1, 2)
			elif event.keycode == 52:
				set_type_split(3, 1)
			elif event.keycode == 53:
				set_type_split(1, 3)
			elif event.keycode == 54:
				set_type_split(2, 2)
