@tool
extends EditorPlugin

var base:Control
var editor_settings:EditorSettings

const TOOL = preload("res://addons/cba/tool.tscn")
var settings:Dictionary = {}
var bg:TextureRect
var tool:Window
var theme:Theme

var accent_color:Color

func _disable_plugin():
	bg.queue_free()
	editor_settings.set("interface/theme/custom_theme", "")
	editor_settings.set("interface/theme/preset", "Default")
	remove_tool_menu_item("Backgrounds")

func _enter_tree():
	#Benchmark.start("init")
	if not Engine.is_editor_hint(): return
	
	base = EditorInterface.get_base_control()
	editor_settings = EditorInterface.get_editor_settings()
	editor_settings.settings_changed.connect(func():
		for setting in editor_settings.get_changed_settings():
			_setting_changed(setting)
	)
	bg = TextureRect.new()
	bg.name = "Editor Background"
	bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
	bg.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
	bg.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
	base.add_child.call_deferred(bg)
	# move it to the top of the tree so it's behind all the UI
	base.move_child.call_deferred(bg, 0)
	await bg.ready
	
	theme = preload("res://addons/cba/theme.tres")
	if editor_settings.get("interface/theme/custom_theme") != "res://addons/cba/theme.tres":
		editor_settings.set("interface/theme/custom_theme", "res://addons/cba/theme.tres")
	if editor_settings.get("interface/theme/preset") != "Custom":
		editor_settings.set("interface/theme/preset", "Custom")
	
	await base.get_tree().physics_frame
	load_settings()
	
	add_tool_menu_item("Backgrounds", func():
		if is_instance_valid(tool): printerr("There is already a background picker window open."); return
		tool = TOOL.instantiate()
		tool.main = self
		base.add_child(tool)
		tool.start()
		tool.popup_centered()
	)
	#Benchmark.end("init")

func change_setting(value:Variant, setting:String, update_ui:bool = false, update_setting:bool = true):
	var is_prev_ready := is_instance_valid(tool)
	match setting:
		"image":
			var img := load_image(value)
			if is_prev_ready: tool.preview.texture = img
			if update_setting: bg.texture = img
		"stretch":
			if update_setting: bg.stretch_mode = value
			if is_prev_ready:
				tool.preview.stretch_mode = value
				if update_ui:
					tool.get_node("VBoxContainer/HBoxContainer/VBoxContainer/stretch mode").select(value)
		"filter":
			if update_setting: bg.texture_filter = value
			if is_prev_ready:
				tool.preview.texture_filter = value
				if update_ui:
					tool.get_node("VBoxContainer/HBoxContainer/VBoxContainer/filter mode").select(value)
		"ui_color":
			if is_prev_ready:
				if update_ui:
					value = Color(settings["ui_color"])
					tool.get_node("VBoxContainer/HBoxContainer/VBoxContainer2/ui_color").color = value
				else:
					value = tool.get_node("VBoxContainer/HBoxContainer/VBoxContainer2/ui_color").color
			if not update_ui && update_setting:
				if value == Color(settings["ui_color"]): return
				change_theme_color(value)
				settings["ui_color"] = value.to_html()
			value = null
		"bg_modulate":
			if value is String: value = Color(value)
			if update_setting: bg.modulate = value
			if is_prev_ready:
				tool.preview.modulate = value
				if update_ui:
					tool.get_node("VBoxContainer/HBoxContainer/VBoxContainer2/bg_modulate").color = value
			value = value.to_html()
		"edit_transparency":
			if is_prev_ready:
				if update_ui:
					value = settings["edit_transparency"]
					tool.get_node("VBoxContainer/edit_transparency").button_pressed = value
				else:
					value = tool.get_node("VBoxContainer/edit_transparency").button_pressed
			if not update_ui && update_setting:
				if value == settings["edit_transparency"]: return
				settings["edit_transparency"] = value
				change_theme_color(Color(settings["ui_color"]))
				value = null
	if value != null: settings[setting] = value

func load_image(path:String) -> Texture2D:
	var file := FileAccess.open(path, FileAccess.READ)
	if file == null: printerr("file not found: ", path); return
	var image = Image.load_from_file(path)
	var out = ImageTexture.create_from_image(image)
	return out

func load_settings():
	if FileAccess.file_exists("res://addons/cba/config.json"):
		var file := FileAccess.open("res://addons/cba/config.json", FileAccess.READ)
		if file == null:
			file = FileAccess.open("res://addons/cba/config.json", FileAccess.WRITE_READ)
			assert(file != null, "Error opening file.")
			return
		else:
			settings = JSON.parse_string(file.get_as_text())
			file.close()
	else:
		var file := FileAccess.open("res://addons/cba/config.json", FileAccess.WRITE)
		var defaults := {
			"filter": 0.0,
			"image": ProjectSettings.globalize_path("res://addons/cba/images/default.png"),
			"stretch": 1,
			"ui_color": "00000088",
			"bg_modulate": "ffffffb0",
			"edit_transparency": false,
		}
		file.store_string(JSON.stringify(defaults, "\t"))
		settings = defaults
		file.close()
	for s in settings.keys():
		change_setting(settings[s], s, true)

func save_settings():
	var file := FileAccess.open("res://addons/cba/config.json", FileAccess.WRITE)
	file.store_string(JSON.stringify(settings, "\t"))

func _setting_changed(setting:String):
	match setting:
		"interface/theme/accent_color":
			change_theme_color(Color(settings["ui_color"]))

func change_theme_color(col:Color):
	#Benchmark.start("change theme color")
	
	accent_color = editor_settings.get_setting("interface/theme/accent_color")
	
	var controls_list = get_all_controls([base])

	var col2 := Color(col, col.a/2.0)
	var col3 := Color(col, min(col.a/col.v, col.a/4.0))
	change_color("EditorStyles", "Background", col)
	change_color("EditorStyles", "BottomPanel", col)
	change_color("EditorStyles", "BottomPanelDebuggerOverride", col)
	change_color("EditorStyles", "Content", col)
	change_color("EditorStyles", "LaunchPadNormal", col)
	
	change_color("TabContainer", "panel", col)
	change_color("TabContainer", "tab_selected", col, accent_color)
	change_color("TabContainer", "tab_unselected", col2)
	change_color("TabContainer", "tab_hovered", col2)
	
	change_color("TabBar", "tab_selected", col, accent_color)
	change_color("TabBar", "tab_unselected", col2)
	change_color("TabBar", "tab_hovered", col2)
	
	change_color("TabContainerOdd", "tab_selected", col, accent_color)
	change_color("TabContainerOdd", "panel", col2)
	
	# bordered
	change_color("Button", "normal", col3)
	change_color("MenuButton", "normal", col3)
	change_color("OptionButton", "normal", col3)
	change_color("RichTextLabel", "normal", col3)
	change_color("LineEdit", "normal", col3)
	change_color("TextEdit", "normal", col3)
	change_color("LineEdit", "read_only", col3)
	change_color("TextEdit", "read_only", col3)
	change_color("EditorProperty", "child_bg", col3)
	
	change_color("EditorInspectorCategory", "bg", col2)
	
	# fix to old values showing up in transparent preview
	if settings["edit_transparency"]:
		change_color("LineEdit", "focus", col3)
	else:
		change_color("LineEdit", "focus", Color.BLACK)
	
	# trigger an update
	theme.get_stylebox("Background", "EditorStyles").emit_changed()
	
	#Benchmark.end("change theme color")

func get_all_controls(nodes:Array[Node]) -> Array[Node]:
	var out:Array[Node] = []
	for node in nodes:
		if node is Control: out.append(node)
		var children := node.get_children() as Array[Node]
		out += get_all_controls(children)
	return out

func change_color(type:String, name:String, col:Color, border = null):
	var box:StyleBoxFlat = theme.get_stylebox(name, type)
	box.set_block_signals(true)
	box.bg_color = col
	if border != null: 
		box.border_color = border
	box.set_block_signals(false)
