@icon("res://components/Data/book-marked.svg")
class_name DataType
extends Base
## Data handler to read in data from files
##
## A data handler that will read in data from txt files and other sources for
## use in various parts of the game. Reads both from the parts folder in the
## main game directory and the mods folder in the user folder to allow users to
## easily mod the game.

var _FILE_FUNCTIONS = {
	"*.txt": _read_txt,
	"*.png": _read_resource,
	"*.png.import": _read_png_import,
	"*.ogg": _read_resource,
	"*.mp3": _read_resource,
	"*.wav": _read_resource,
	"*.gd": _read_script_resource,
}

var _FILE_LOCATION_OVERRIDES = {
	"*.gd": "scripts",
	"*.png.import": "images",
	"*.png": "images",
	"*.ogg": "audio",
	"*.wav": "audio"
}

var data = {}
var components = {}
var location_overrides = {}


func _prespawned():
	_information = {
		"accent": "#a27155",
		"log_category": "DATA",
		"icon": "res://components/Data/book-marked.svg"
	}


func _spawned():
	reload_data()


## Reload the data object by pulling from available sources
func reload_data() -> void:
	data = {} # Remove all old mod data
	
	# Open part folder from project (Ignore if none) and if exists set mod data to it
	var res_dir = DirAccess.open("res://")
	if res_dir.dir_exists("parts"):
		data = _read_directory("res://parts")
		data = _merge_objects(location_overrides, data)
	
	if res_dir.dir_exists("components"):
		location_overrides = {}
		components = _read_directory("res://components")
		components = _merge_objects(location_overrides, components)


func _merge_objects(object1, object2):
	var newObject = {}
	for key in object1:
		newObject[key] = object1[key]
	for key in object2:
		if(newObject.has(key)):
			if(typeof(newObject[key]) == TYPE_STRING):
				newObject[key] = object1[key]
			elif(typeof(newObject[key]) == TYPE_OBJECT):
				if newObject[key] is Texture:
					newObject[key] = object1[key]
				else:
					newObject[key] = _merge_objects(object1[key], object2[key])
			elif(typeof(newObject[key]) == TYPE_DICTIONARY):
				newObject[key] = _merge_objects(object1[key], object2[key])
			elif(typeof(newObject[key]) == TYPE_ARRAY):
				newObject[key] = object1[key] + object2[key]
		else:
			newObject[key] = object2[key]
	return newObject


func _read_directory(path) -> Dictionary:
	var dir = DirAccess.open(path)
	var local_data = {}
	
	if not dir:
		_warn("Could not read directory [%s]" % [path])
		return {}
	
	dir.list_dir_begin()
	var file_name = dir.get_next()
	while file_name != "":
		if dir.current_is_dir():
			var micro_data = _read_directory("%s/%s" % [path, file_name])
			if micro_data:
				local_data[file_name] = micro_data
		else:
			var file_handling = _get_file_handling(file_name)
			
			if (file_handling):
				var location_override = _get_location_override(file_name)
				
				if location_override:
					var micro_data = file_handling.call("%s/%s" % [path, file_name])
					if not location_overrides.has(location_override):
						location_overrides[location_override] = {}
					location_overrides[location_override][file_name.split(".")[0]] = micro_data
				else:
					var micro_data = file_handling.call("%s/%s" % [path, file_name])
					if micro_data:
						local_data[file_name.split(".")[0]] = micro_data  
		
		file_name = dir.get_next()
	
	_info("Read directory ♢%s♢" % [path])
	return local_data


func _get_file_handling(file):
	var split_file = file.split(".", true, 1)
	
	for file_function in _FILE_FUNCTIONS:
		if(file_function == file):
			return _FILE_FUNCTIONS[file_function]
		
		var split_file_function = file_function.split(".", true, 1)
		
		if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]):
			return _FILE_FUNCTIONS[file_function]
	
	return null


func _read_script_resource(path):
	return load(path)


func _read_resource(path):
	var song = load(path)
	return song


func _read_png_import(path):  
	var image = load(path.rsplit(".", true, 1)[0])
	return image


func _get_location_override(file):
	var split_file = file.split(".", true, 1)
	
	for location_override in _FILE_LOCATION_OVERRIDES:
		if location_override == file:
			return _FILE_LOCATION_OVERRIDES[location_override]
		
		var split_file_function = location_override.split(".", true, 1)
		
		if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]):
			return _FILE_LOCATION_OVERRIDES[location_override]


func _read_txt(path) -> Dictionary:
	# Open file for reading
	var file = FileAccess.open(path, FileAccess.READ)
	var content = file.get_as_text()
	var local_data = {}
	
	# Separate into lines
	var split_content = content.split("\n", false)
	
	# Indentation
	var indentation = 0
	var indentation_levels = []
	
	# Iterate over everything
	for content_piece in split_content:
		# Fix Indentation to actual content level
		var actual_indentation = _count_indentation(content_piece)
		
		while(actual_indentation < indentation):
			indentation -= 1
			indentation_levels.pop_back()
		
		# Navigate to current indentation data
		var micro_data = local_data
		var previous_data = null
		var i = 0
		while i < indentation:
			previous_data = micro_data
			micro_data = micro_data[indentation_levels[i]]
			i += 1
		
		# Reading
		if(content_piece.ends_with("[]")):
			# Array handling
			var trimmed_content = content_piece.strip_edges().trim_suffix("[]")
			
			indentation += 1
			indentation_levels.append(trimmed_content)
			micro_data[trimmed_content] = {}
		else:
			var split_args = content_piece.split(":", true, 1)
			
			if split_args.size() == 2:
				# Dict Handling
				var key = split_args[0].strip_edges()
				var value = split_args[1].strip_edges()
				
				if value.is_valid_int():
					value = value.to_int()
				
				micro_data[key] = value
			elif split_args.size() == 1:
				# Value Handling
				var value = split_args[0].strip_edges()
				
				if typeof(micro_data) == TYPE_DICTIONARY:
					# TODO: FIX BELOW
					if indentation_levels.size() == 0:
						continue
					
					if previous_data[indentation_levels[indentation_levels.size()-1]].keys().size() == 0:
						previous_data[indentation_levels[indentation_levels.size()-1]] = [split_args[0].strip_edges()]
					else:
						pass
				else:
					previous_data[indentation_levels[indentation_levels.size()-1]] += [split_args[0].strip_edges()]
	return local_data


func _count_indentation(string: String):
	var tabs = 0
	
	for character in string:
		if(character == "\t"):
			tabs += 1
		else:
			return tabs
	
	return tabs