## Performs the actual Saving[br][br]
##
## Below is a basic definition of various components and resources for reference:[br]
## * SaveLevelDataComponent - The root save component that performs saving and loading of the actual data from disk
## * SaveDataComponent - Attached to a node that has properties that need to be saved
## * Node Type Resources - Each type of node that requires saving, such as a TileMapLayer, needs a resource created
extends Node


signal game_saved
signal game_loaded
signal refresh_saves_list
signal create_auto_save_file
signal create_save_file(save_name: String)
signal delete_save_file(filename: String)
signal load_save_file(filename: String)
signal quick_save
signal quick_load
signal open_save_list_ui
signal close_save_list_ui


var _game_data_resource: SaveGameDataResource = SaveGameDataResource.new()
var _save_level_data_component: SaveLevelDataComponent ## Contains the save paths and filenames


func _ready() -> void:
	quick_load.connect(quick_load_game)
	quick_save.connect(quick_load_game)
	create_save_file.connect(_on_save_game_as_resource)
	load_save_file.connect(_load_game_resource)
	delete_save_file.connect(_on_delete_save_file)

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("quick_save"):
		quick_save_game()
	if event.is_action_pressed("quick_load"):
		quick_load_game()


func list_saves(include_quick_saves: bool = true, include_auto_saves: bool = true) -> Array[SaveFileDetailsResource]:
	var save_files: Array[SaveFileDetailsResource] = []
	if not _load_save_level_data_component():
		return save_files

	var save_game_data_path: String = _save_level_data_component.settings.save_game_data_path
	if !DirAccess.dir_exists_absolute(save_game_data_path):
		return save_files

	for filename: String in ResourceLoader.list_directory(save_game_data_path):
		# TODO: Rework so the settings determine the file_name prepends
		if filename.begins_with("quicksave_") and not include_quick_saves: continue
		elif filename.begins_with("autosave_") and not include_auto_saves: continue
		elif !filename.begins_with("save_") and !filename.begins_with("quicksave_") and !filename.begins_with("autosave_"): continue

		var _save_path: String = save_game_data_path + filename
		var _save_icon: String = filename.replace(".tres", ".png")
		var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new()

		# TODO: Reconsider loading the save to get the name
		#       This could be a large save making opening each one slow
		#       Should work out a better method for getting the save name (filename != save name)
		var _save_file: SaveGameDataResource = ResourceLoader.load(_save_path)
		_save_resource.save_name = _save_file.save_name

		_save_resource.filename = filename
		_save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path))

		## In 4.5, this can probably be replaced with FileAccess.get_size(_save_path)
		##   This should reduce the need for opening the file here
		var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ)
		_save_resource.filesize = _loaded_file.get_length()

		if FileAccess.file_exists(save_game_data_path + _save_icon):
			_save_resource.save_icon = _save_icon

		save_files.append(_save_resource)

	save_files.sort_custom(_custom_save_file_sort)
	return save_files

func quick_save_game() -> void:
	if not _load_save_level_data_component(): return

	_game_data_resource.save_name = "Quick Save"
	_save_game_as_resource(_save_level_data_component.settings.quicksave_file_name)
	game_saved.emit()

func quick_load_game() -> void:
	if not _load_save_level_data_component(): return

	# TODO: Don't reset world if quicksave not found
	EntityManager.reset_world.emit()
	_load_game_resource(_save_level_data_component.settings.quicksave_file_name)
	game_loaded.emit()


## Sort the save files list by date created, descending
func _custom_save_file_sort(a: SaveFileDetailsResource, b: SaveFileDetailsResource) -> bool:
	return a.date_created > b.date_created

## Find the SaveLevelDataComponent within the level which stores the save paths and filenames
func _load_save_level_data_component() -> bool:
	_save_level_data_component = get_tree().get_first_node_in_group("save_level_data_component")
	if _save_level_data_component == null:
		push_error("Could not find SaveLevelDataComponent node in level")
		return false

	return true

func _load_game_resource(resource_filename: String) -> void:
	if not _load_save_level_data_component(): return
	var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename
	if !FileAccess.file_exists(save_game_file_path):
		printerr("Failed to load save. File does not exist: ", save_game_file_path)
		return

	_game_data_resource = ResourceLoader.load(save_game_file_path)
	if _game_data_resource == null:
		printerr("Failed to load save. Unknown format? ", save_game_file_path)
		return

	var root_node: Window = get_tree().root
	for resource: Resource in _game_data_resource.save_data_nodes:
		if resource is Node3DDataResource:
			(resource as Node3DDataResource)._load_data(root_node)

func _save_game_as_resource(resource_filename: String) -> void:
	if not _load_save_level_data_component(): return

	if !DirAccess.dir_exists_absolute(_save_level_data_component.settings.save_game_data_path):
		DirAccess.make_dir_absolute(_save_level_data_component.settings.save_game_data_path)

	var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename
	_save_node_data()

	var result: int = ResourceSaver.save(_game_data_resource, save_game_file_path)
	if result != OK:
		printerr("Failed to save game (" , result, "): ", save_game_file_path)

## Save the properties defined on the SaveDataComponents attached to various nodes (such as Block)
func _save_node_data() -> void:
	var nodes: Array = get_tree().get_nodes_in_group("save_data_component")

	if nodes == null: return

	for node: Node in nodes:
		if node is SaveDataComponent:
			@warning_ignore("unsafe_method_access")
			var save_data_resource: Node3DDataResource = node._save_data()
			var save_final_resource: Node3DDataResource = save_data_resource.duplicate()
			_game_data_resource.save_data_nodes.append(save_final_resource)


func _on_save_game_as_resource(save_name: String) -> void:
	if not _load_save_level_data_component(): return

	var current_date: String = Time.get_datetime_string_from_system().replace(":", "")
	var _filename: String = "save_" + current_date + ".tres"

	_game_data_resource.save_name = save_name
	_save_game_as_resource(_filename)
	game_saved.emit()

func _on_delete_save_file(filename: String) -> void:
	if filename.length() < 1:
		printerr("_on_delete_save_file(): Empty filename provided")
		return

	var save_file_path: String = _save_level_data_component.settings.save_game_data_path + filename
	DirAccess.remove_absolute(save_file_path)