extends Node

# How often to cleanup finished threads in seconds
const THREAD_CLEANUP_INTERVAL: int = 300

var threads: Array = []


func _ready() -> void:
	var cleanup_timer := Timer.new()
	cleanup_timer.connect("timeout", self, "_cleanup_finished_threads")
	cleanup_timer.set_name("ThreadCleanupTimer")
	cleanup_timer.set_wait_time(THREAD_CLEANUP_INTERVAL)
	cleanup_timer.set_autostart(true)
	add_child(cleanup_timer)


func _exit_tree() -> void:
	# Threads that are alive on program exit are not joined on purpose so
	# that the program can be quit without having to end the threads, which
	# could have a video playing or document open
	_cleanup_finished_threads()


func open(uri: String, mime_type: String = "") -> void:
	"""
	Possible issues:
		- All '%placeholder%'s are replaced with no other checks, maybe should
		  add a way to escape this if the user wants to include these
		  characters
		- The command to be executed is split on spaces with no other checks,
		  this may lead to issues if the user intends to pass an argument that
		  includes spaces such as a filepath
	"""
	# Replace "localhost" if in `uri` since it seems some programs do not
	# resolve it, namely VLC
	var replace = "://localhost:"
	var with = "://127.0.0.1:"
	var localhost_idx: int = uri.find(replace)
	if localhost_idx >= 0:
		uri = uri.substr(0, localhost_idx) \
				+ with \
				+ uri.substr(localhost_idx + len(replace))
	
	var command: String = Data.get_media_opener(mime_type)
	if len(command) > 0:
		command = command.replace("%uri%", uri)
		var split_command: Array = command.split(" ")
		OS.execute(
				split_command[0],
				split_command.slice(1, len(split_command) - 1),
				false
		)
	else:
		# `OS.shell_open()` sometimes blocks so running in a thread as a
		# work-around
		# See: https://github.com/godotengine/godot/issues/49946
		var thread := Thread.new()
		threads.append(thread)
		thread.start(OS, "shell_open", uri)


func _cleanup_finished_threads() -> void:
	""" Join and remove the reference to any finished Threads """
	for i in range(len(threads) - 1, -1, -1):
		if threads[i].is_active() and not threads[i].is_alive():
			threads[i].wait_to_finish()
			threads.remove(i)
