extends Control

signal params_loaded
signal params_reset

@export var shader: ShaderMaterial
var loaded_images := {}
var loaded_image_paths := {}
var image_modification_times := {}

var trans_tween = null
var params := ImagineParams.new()
var time = 0.0
var time_scale_factor = 1.0
var fx_path = ""

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	params.time_scale = 0.01
	params.transition_dur = 2

	%LoadImageButton.pressed.connect(func():
		%LoadImageDialog.visible = true
	)
	%LoadImageDialog.files_selected.connect(func(paths):
		for p in paths:
			load_image(p)
	)
	%SaveFXButton.pressed.connect(func():
		if fx_path == "":
			%SaveFXDialog.visible = true
		else:
			save_effects()
	)
	%SaveFXAsButton.pressed.connect(func():
		%SaveFXDialog.visible = true
	)
	%SaveFXDialog.file_selected.connect(func(path):
		fx_path = path
		save_effects()
	)
	%LoadFXDialog.file_selected.connect(func(path):
		fx_path = path
		params = ResourceLoader.load(path)
		params_loaded.emit()
		%FXFileLabel.text = fx_path
	)

	%LoadFXButton.pressed.connect(func():
		%LoadFXDialog.visible = true
	)
	%NewFXButton.pressed.connect(func():
		params = ImagineParams.new()
		fx_path = ""
		%FXFileLabel.text = "unsaved FX"
		params_reset.emit()
	)

	%ImageList.item_clicked.connect(func(index, at, btn):
		if btn == MouseButton.MOUSE_BUTTON_LEFT:
			transition_to_image(%ImageList.get_item_text(index))
		elif btn == MouseButton.MOUSE_BUTTON_RIGHT && Input.is_action_pressed("shift"):
			var file = %ImageList.get_item_text(index)
			loaded_images.erase(file)
			loaded_image_paths.erase(file)
			image_modification_times.erase(file)
			%ImageList.remove_item(index)
	)

	for child in %Params.get_children():
		link_param(child)

func save_effects():
	ResourceSaver.save(params, fx_path)
	%FXFileLabel.text = fx_path
	%SaveConfirmPopup.dialog_text = "FX saved to %s" % fx_path
	%SaveConfirmPopup.show()

func _process(delta):
	if Input.is_action_just_pressed("toggle_fullscreen"):
		%ControlSide.visible = !%ControlSide.visible
	time += delta * params.time_scale * time_scale_factor
	shader.set_shader_parameter("time", time)

	for path in loaded_image_paths.values():
		var prev_mod = image_modification_times[path.get_file()]
		var last_mod = FileAccess.get_modified_time(path)
		if last_mod > prev_mod:
			load_image(path)

func load_image(path: String):
	var file = path.get_file()
	var image = Image.load_from_file(path)
	if image == null:
		return
	
	var tex = ImageTexture.create_from_image(image)
	if file in loaded_images:
		for i in range(%ImageList.item_count):
			var txt = %ImageList.get_item_text(i)
			if txt == file:
				%ImageList.set_item_icon(i, tex)
				print("Reloaded %s" % txt)

		var prev_tex = loaded_images[file]
		if shader.get_shader_parameter("texture1") == prev_tex:
			shader.set_shader_parameter("texture1", tex)
		if shader.get_shader_parameter("texture2") == prev_tex:
			shader.set_shader_parameter("texture2", tex)
	else:
		%ImageList.add_item(file, tex)

	loaded_images[file] = tex
	loaded_image_paths[file] = path
	image_modification_times[file] = FileAccess.get_modified_time(path)

func transition_to_image(file: String):
	if trans_tween != null:
		if trans_tween.is_running():
			return
		trans_tween.kill()

	var texture1 = shader.get_shader_parameter("texture1")
	var texture2 = loaded_images[file]
	shader.set_shader_parameter("texture2", texture2)

	var tw = create_tween()
	trans_tween = tw
	var set_trans = func(x):
		shader.set_shader_parameter("transition", x)

	tw.tween_callback(func():
		time_scale_factor = 5)
	tw.tween_method(set_trans, 0.0, 1.0, params.transition_dur/2).set_trans(Tween.TRANS_SINE)
	tw.tween_callback(func():
		shader.set_shader_parameter("texture1", texture2)
		shader.set_shader_parameter("texture2", texture1)
	)
	tw.tween_method(set_trans, 1.0, 0.0, params.transition_dur/2).set_trans(Tween.TRANS_SINE)
	tw.tween_callback(func():
		time_scale_factor = 1)

func link_param(node: Node):
	if node is Label:
		return

	var param = node.name.to_snake_case()
	var slider = node.get_node("Slider")
	var button = node.get_node("ResetButton")
	var val = shader.get_shader_parameter(param)
	
	if param == "time_scale":
		slider.value = params.time_scale
	elif param == "frequency":
		var noise = shader.get_shader_parameter("ripples").noise
		slider.value = noise.frequency
		slider.value_changed.connect(func(x):
			noise.frequency = x
		)
	elif param == "transition_dur":
		slider.value = params.transition_dur
	elif val != null:
		slider.value = val
		slider.value_changed.connect(func(x):
			shader.set_shader_parameter(param, x)
		)

	slider.default_value = slider.value
	params[param] = slider.value
	slider.value_changed.connect(func(x):
		params[param] = x
	)
	params_loaded.connect(func():
		slider.value = params[param]
	)
	button.pressed.connect(func():
		slider.reset()
	)
	params_reset.connect(func():
		slider.reset()
	)
