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" with "127.0.0.1" since some programs don't resolve it
	var uri_replaced: String = _replace_localhost(uri)
	
	var media_opener: Dictionary = Data.get_media_opener(mime_type)
	var command: String = media_opener["command"]
	if not command.empty():
		var command_parsed: Array = _parse_command(command, uri_replaced)
		OS.execute(
				command_parsed[0],
				command_parsed.slice(1, len(command_parsed) - 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 open_with(uri: String, command: String, mime_type: String="") -> void:
	"""
	Opens `uri` with `command`, unless `command` is empty then opens it based
	on `mime_type`
	"""
	if not command.empty():
		# Replace "localhost" with "127.0.0.1" since some programs don't resolve it
		var uri_replaced: String = _replace_localhost(uri)
		var command_parsed: Array = _parse_command(command, uri_replaced)
		OS.execute(
				command_parsed[0],
				command_parsed.slice(1, len(command_parsed) - 1),
				false
		)
	else:
		open(uri, mime_type)


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)


func _replace_localhost(uri: String) -> String:
	"""
	It seems some programs do not resolve "localhost", namely VLC. Use this to
	replace "localhost" with "127.0.0.1"
	"""
	var replace: String = "://localhost:"
	var with: String = "://127.0.0.1:"
	
	var uri_replaced: String = uri
	var localhost_idx: int = uri_replaced.find(replace)
	if localhost_idx >= 0:
		uri_replaced = uri_replaced.substr(0, localhost_idx) \
				+ with \
				+ uri_replaced.substr(localhost_idx + len(replace))
	return uri_replaced


func _parse_command(command: String, uri: String) -> Array:
	var command_replaced: String = command.replace("%uri%", uri)
	var command_parsed: Array = command_replaced.split(" ")
	return command_parsed
