@tool
extends DialogicEditor

## Editor that handles the editing of styles and their layers.


var styles: Array[DialogicStyle] = []
var current_style : DialogicStyle = null
var default_style := ""

var premade_style_parts := {}



#region EDITOR MANAGEMENT
################################################################################

func _get_title() -> String:
	return "Styles"


func _get_icon() -> Texture:
	return load(DialogicUtil.get_module_path('StyleEditor').path_join("styles_icon.svg"))


func _register() -> void:
	editors_manager.register_simple_editor(self)
	alternative_text = "Change the look of the dialog in your game"


func _open(extra_info:Variant = null) -> void:
	load_style_list()


func _close() -> void:
	save_style_list()
	save_style()


#endregion


func _ready() -> void:
	collect_styles()

	setup_ui()


#region STYLE MANAGEMENT
################################################################################

func collect_styles() -> void:
	for indexer in DialogicUtil.get_indexers():
		for layout in indexer._get_layout_parts():
			premade_style_parts[layout['path']] = layout

	var style_list: Array = ProjectSettings.get_setting('dialogic/layout/style_list', [])
	for style in style_list:
		if ResourceLoader.exists(style):
			var style_res := load(style)
			if style != null:
				styles.append(ResourceLoader.load(style, "DialogicStyle"))
			else:
				print("[Dialogic] Failed to open style '", style, "'. Some dependency might be broken.")
		else:
			print("[Dialogic] Failed to open style '", style, "'. Might have been moved or deleted.")

	default_style = ProjectSettings.get_setting('dialogic/layout/default_style', 'Default')


func save_style_list() -> void:
	ProjectSettings.set_setting('dialogic/layout/style_list', styles.map(func(style:DialogicStyle): return style.resource_path))
	ProjectSettings.set_setting('dialogic/layout/default_style', default_style)
	ProjectSettings.save()


func save_style() -> void:
	if current_style == null:
		return

	ResourceSaver.save(current_style)


func add_style(file_path:String, style:DialogicStyle, inherits:DialogicStyle= null) -> void:
	style.resource_path = file_path
	style.inherits = inherits
	if style.layers.is_empty() and style.inherits != null:
		for l in style.get_layer_list():
			style.add_layer('', {})
	ResourceSaver.save(style, file_path)
	styles.append(style)
	if len(styles) == 1:
		default_style = style.resource_path
	save_style_list()
	load_style_list()
	select_style(style)


func delete_style(style:DialogicStyle) -> void:
	for other_style in styles:
		if other_style.inherits == style:
			other_style.realize_inheritance()
			push_warning('[Dialogic] Style "',other_style.name,'" had to be realized because it inherited "', style.name,'" which was deleted!')

	if style.resource_path == default_style:
		default_style = ""
	styles.erase(style)
	save_style_list()


func delete_style_by_name(style_name:String) -> void:
	for style in styles:
		if style.name == style_name:
			delete_style(style)
			return


func realize_style() -> void:
	current_style.realize_inheritance()

	select_style(current_style)


#endregion
#region USER INTERFACE
################################################################################

func setup_ui() -> void:
	%AddButton.icon = get_theme_icon("Add", "EditorIcons")
	%DuplicateButton.icon = get_theme_icon("Duplicate", "EditorIcons")
	%InheritanceButton.icon = get_theme_icon("GuiDropdown", "EditorIcons")
	%RemoveButton.icon = get_theme_icon("Remove", "EditorIcons")

	%EditNameButton.icon = get_theme_icon("Edit", "EditorIcons")
	%TestStyleButton.icon = get_theme_icon("PlayCustom", "EditorIcons")
	%MakeDefaultButton.icon = get_theme_icon("Favorites", "EditorIcons")

	%StyleList.item_selected.connect(_on_stylelist_selected)
	%AddButton.get_popup().index_pressed.connect(_on_AddStyleMenu_selected)
	%AddButton.about_to_popup.connect(_on_AddStyleMenu_about_to_popup)
	%InheritanceButton.get_popup().index_pressed.connect(_on_inheritance_index_pressed)
	%StyleList.set_drag_forwarding(_on_stylelist_drag, _on_stylelist_can_drop, _on_style_list_drop)
	%StyleView.hide()
	%NoStyleView.show()

func load_style_list() -> void:
	var latest: String = DialogicUtil.get_editor_setting('latest_layout_style', 'Default')

	%StyleList.clear()
	var idx := 0
	for style in styles:
		%StyleList.add_item(style.name, get_theme_icon("PopupMenu", "EditorIcons"))
		if style.resource_path == default_style:
			%StyleList.set_item_icon_modulate(idx, get_theme_color("warning_color", "Editor"))
		if style.name == latest:
			%StyleList.select(idx)
			load_style(style)
		%StyleList.set_item_tooltip(idx, style.resource_path)
		%StyleList.set_item_metadata(idx, style)
		idx += 1

	if len(styles) == 0:
		%StyleView.hide()
		%NoStyleView.show()

	elif !%StyleList.is_anything_selected():
		%StyleList.select(0)
		load_style(%StyleList.get_item_metadata(0))


func _on_stylelist_selected(index:int) -> void:
	load_style(%StyleList.get_item_metadata(index))


func select_style(style:DialogicStyle) -> void:
	DialogicUtil.set_editor_setting('latest_layout_style', style.name)
	for idx in range(%StyleList.item_count):
		if %StyleList.get_item_metadata(idx) == style:
			%StyleList.select(idx)
			return


func load_style(style:DialogicStyle) -> void:
	if current_style != null:
		current_style.changed.disconnect(save_style)
	save_style()
	current_style = style
	if current_style == null:
		return
	current_style.changed.connect(save_style)

	%LayoutStyleName.text = style.name
	if style.resource_path == default_style:
		%MakeDefaultButton.tooltip_text = "Is Default"
		%MakeDefaultButton.disabled = true
	else:
		%MakeDefaultButton.tooltip_text = "Make Default"
		%MakeDefaultButton.disabled = false

	%StyleEditor.load_style(style)

	%InheritanceButton.visible = style.inherits_anything()
	if %InheritanceButton.visible:
		%InheritanceButton.text = "Inherits " + style.inherits.name


	DialogicUtil.set_editor_setting('latest_layout_style', style.name)

	%StyleView.show()
	%NoStyleView.hide()


func _on_AddStyleMenu_about_to_popup() -> void:
	%AddButton.get_popup().set_item_disabled(3, not %StyleList.is_anything_selected())


func _on_AddStyleMenu_selected(index:int) -> void:
	# add preset style
	if index == 2:
		%StyleBrowserWindow.popup_centered_ratio(0.6)
		%StyleBrowser.current_type = 1
		%StyleBrowser.load_parts()
		var picked_info: Dictionary = await %StyleBrowserWindow.get_picked_info()
		if not picked_info.has('style_path'):
			return

		if not ResourceLoader.exists(picked_info.style_path):
			return

		var new_style: DialogicStyle = load(picked_info.style_path).clone()

		find_parent('EditorView').godot_file_dialog(
			add_style_undoable.bind(new_style),
			'*.tres',
			EditorFileDialog.FILE_MODE_SAVE_FILE,
			"Select folder for new style")

	if index == 3:
		if %StyleList.get_selected_items().is_empty():
			return
		find_parent('EditorView').godot_file_dialog(
			add_style_undoable.bind(DialogicStyle.new(), current_style),
			'*.tres',
			EditorFileDialog.FILE_MODE_SAVE_FILE,
			"Select folder for new style")

	if index == 4:
		find_parent('EditorView').godot_file_dialog(
			add_style_undoable.bind(DialogicStyle.new()),
			'*.tres',
			EditorFileDialog.FILE_MODE_SAVE_FILE,
			"Select folder for new style")


func add_style_undoable(file_path:String, style:DialogicStyle, inherits:DialogicStyle = null) -> void:
	style.name = _get_new_name(file_path.get_file().trim_suffix('.'+file_path.get_extension()))
	var undo_redo: EditorUndoRedoManager = DialogicUtil.get_dialogic_plugin().get_undo_redo()
	undo_redo.create_action('Add Style', UndoRedo.MERGE_ALL)
	undo_redo.add_do_method(self, "add_style", file_path, style, inherits)
	undo_redo.add_do_method(self, "load_style_list")
	undo_redo.add_undo_method(self, "delete_style", style)
	undo_redo.add_undo_method(self, "load_style_list")
	undo_redo.commit_action()
	DialogicUtil.set_editor_setting('latest_layout_style', style.name)


func _on_duplicate_button_pressed():
	if !%StyleList.is_anything_selected():
		return
	find_parent('EditorView').godot_file_dialog(
		add_style_undoable.bind(current_style.clone(), null),
		'*.tres',
		EditorFileDialog.FILE_MODE_SAVE_FILE,
		"Select folder for new style")


func _on_remove_button_pressed():
	if !%StyleList.is_anything_selected():
		return

	if current_style.name == default_style:
		push_warning("[Dialogic] You cannot delete the default style!")
		return

	delete_style(current_style)
	load_style_list()


func _on_edit_name_button_pressed():
	%LayoutStyleName.grab_focus()
	%LayoutStyleName.select_all()


func _on_layout_style_name_text_submitted(new_text:String) -> void:
	_on_layout_style_name_focus_exited()


func _on_layout_style_name_focus_exited():
	var new_name :String= %LayoutStyleName.text.strip_edges()
	if new_name == current_style.name:
		return

	for style in styles:
		if style.name == new_name:
			%LayoutStyleName.text = current_style.name
			return

	current_style.name = new_name
	DialogicUtil.set_editor_setting('latest_layout_style', new_name)
	load_style_list()


func _on_make_default_button_pressed():
	default_style = current_style.resource_path
	save_style_list()
	load_style_list()



func _on_test_style_button_pressed():
	var dialogic_plugin := DialogicUtil.get_dialogic_plugin()

	# Save the current opened timeline
	DialogicUtil.set_editor_setting('current_test_style', current_style.name)

	DialogicUtil.get_dialogic_plugin().get_editor_interface().play_custom_scene("res://addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn")
	await get_tree().create_timer(3).timeout
	DialogicUtil.set_editor_setting('current_test_style', '')


func _on_inheritance_index_pressed(index:int) -> void:
	if index == 0:
		realize_style()



func _on_start_styling_button_pressed() -> void:
	var new_style := DialogicUtil.get_fallback_style().clone()

	find_parent('EditorView').godot_file_dialog(
		add_style_undoable.bind(new_style),
		'*.tres',
		EditorFileDialog.FILE_MODE_SAVE_FILE,
		"Select folder for new style")


#endregion

func _on_stylelist_drag(vector:Vector2) -> Variant:
	return null


func _on_stylelist_can_drop(at_position: Vector2, data: Variant) -> bool:
	if not data is Dictionary:
		return false
	if not data.get('type', 's') == 'files':
		return false
	for f in data.files:
		var style := load(f)
		if style is DialogicStyle:
			if not style in styles:
				return true

	return false

func _on_style_list_drop(at_position: Vector2, data: Variant) -> void:
	for file in data.files:
		var style := load(file)
		if style is DialogicStyle:
			if not style in styles:
				styles.append(style)
	save_style_list()
	load_style_list()


#region Helpers
func _get_new_name(base_name:String) -> String:
	var new_name_idx := 1
	var found_unique_name := false
	var new_name := base_name
	while not found_unique_name:
		found_unique_name = true
		for style in styles:
			if style.name == new_name:
				new_name_idx += 1
				new_name = base_name+" "+str(new_name_idx)
				found_unique_name = false
	return new_name

#endregion

