@tool
class_name GlobalTypes
extends Node

@export var force_update: bool = false:
	set(value):
		force_update = false
		if Engine.is_editor_hint():
			_generate_types()
			_generate_labels()

@export_category("Generated Data")
@export_group("Types")
@export var movement_types: Array
@export var unit_types: Array
@export var terrain_types: Array
@export var terrain_sprite_types: Array
@export var weapon_types: Array
@export var weather_types: Array
@export_group("Values")
@export var units: Dictionary
@export var terrains: Dictionary
# Structure: [String/terrain_type] [String/weather_type] [String/movement_type]
@export var movements: Dictionary
# Structure: [String/attacker/unit_type] [String/defender/unit_type]
@export var primary_damage: Dictionary
@export var secondary_damage: Dictionary

var sanitizer_regex: RegEx


func _ready() -> void:
	if Engine.is_editor_hint():
		sanitizer_regex = RegEx.new()
		sanitizer_regex.compile("[^a-zA-Z0-9_]")
		_generate_types()
		_generate_labels()


func _generate_types() -> void:
	var path: String = ProjectSettings.globalize_path("res://") + "../types/enum.json"
	var enum_file: FileAccess = FileAccess.open(path, FileAccess.READ)
	if not enum_file:
		push_error("Can not open file: " + path)
		return
	var json: JSON = JSON.new()
	json.parse(enum_file.get_as_text())
	movement_types = json.data["definitions"]["movement_type"]["enum"]
	unit_types = json.data["definitions"]["unit"]["enum"]
	terrain_types = json.data["definitions"]["terrain"]["enum"]
	terrain_sprite_types = json.data["definitions"]["terrain_sprite"]["enum"]
	weapon_types = json.data["definitions"]["weapon"]["enum"]
	weather_types = json.data["definitions"]["weather"]["enum"]

	path = ProjectSettings.globalize_path("res://") + "../types/units/"
	units = _get_folder_values(path)

	path = ProjectSettings.globalize_path("res://") + "../types/terrain/"
	terrains = _get_folder_values(path)

	path = ProjectSettings.globalize_path("res://") + "../types/movement/chart.json"
	movements = _get_movement_values(path, movement_types, terrain_types, weather_types)

	path = ProjectSettings.globalize_path("res://") + "../types/damage/primary.json"
	primary_damage = _get_damage_values(path, unit_types)

	path = ProjectSettings.globalize_path("res://") + "../types/damage/secondary.json"
	secondary_damage = _get_damage_values(path, unit_types)


func add_text_to_gdscript(
	file: FileAccess, string_name: String, text: String, context: String = ""
) -> void:
	var sanitized_name: String = sanitizer_regex.sub(string_name, "_")
	file.store_string(
		(
			'var _%s: String = tr("%s")\n'
			% [sanitized_name, text.replace('"', '\\"')]
		)
	)


func _generate_labels() -> void:
	var file_dumper: FileAccess = FileAccess.open(
		"res://assets/translations/dynamic_string_loader.gd", FileAccess.WRITE
	)
	if file_dumper.get_error():
		print("Error opening dynamic string loader file")

	file_dumper.store_string("# This file is automatically generated from the types.tscn scene. \n")
	file_dumper.store_string("# To modify the generated code, please edit the types.tscn scene directly in Godot. \n")

	for unit_key: String in units:
		var unit: Dictionary = units.get(unit_key)
		add_text_to_gdscript(file_dumper, unit_key, unit.get("name", "") as String)
		add_text_to_gdscript(
			file_dumper,
			"%s_DESCRIPTION" % unit_key,
			unit.get("description", "") as String
		)
	for terrain_key: String in terrains:
		var terrain: Dictionary = terrains.get(terrain_key)
		add_text_to_gdscript(
			file_dumper, terrain_key, terrain.get("name", "") as String
		)
		add_text_to_gdscript(
			file_dumper,
			"%s_DESCRIPTION" % terrain_key,
			terrain.get("description", "") as String
		)
	for movement_key: String in movement_types:
		add_text_to_gdscript(
			file_dumper, "%s_MOVEMENT" % movement_key, movement_key
		)
		add_text_to_gdscript(
			file_dumper,
			"%s_DESCRIPTION_MOVEMENT" % movement_key,
			"%s_DESCRIPTION" % movement_key
		)

	# Texts used in menus dynamically
	add_text_to_gdscript(
		file_dumper, "map_selection", "MAP_SELECTION"
	)
	add_text_to_gdscript(file_dumper, "menu", "MENU")
	file_dumper.close()
	# Missing some way to tell godot to reload the script


func _get_folder_values(path: String) -> Dictionary:
	var dic: Dictionary = {}
	var dir: DirAccess = DirAccess.open(path)
	if not dir:
		push_error("Can not open directory: " + path)
		return {}
	for file_name: String in dir.get_files():
		var file: FileAccess = FileAccess.open(path + file_name, FileAccess.READ)
		if not file:
			push_error("Can not open file: " + path + file_name)
			return {}
		var json: JSON = JSON.new()
		json.parse(file.get_as_text())
		dic[json.data["name"]] = json.data
	return dic


func _get_movement_values(
	path: String, _movement_types: Array, _terrain_types: Array, _weather_types: Array
) -> Dictionary:
	var file: FileAccess = FileAccess.open(path, FileAccess.READ)
	if not file:
		push_error("Can not open file: " + path)
		return {}
	var json: JSON = JSON.new()
	json.parse(file.get_as_text())
	var dic: Dictionary = {}
	var data: Array = json.data
	for t: int in data.size():
		var sub_data: Array = data[t]
		var terrain: Dictionary = {}
		dic[_terrain_types[t]] = terrain
		for w: int in sub_data.size():
			var sub_sub_data: Array = sub_data[w]
			var weather: Dictionary = {}
			terrain[_weather_types[w]] = weather
			for m: int in sub_sub_data.size():
				weather[_movement_types[m]] = sub_sub_data[m]
	return dic


func _get_damage_values(path: String, _unit_types: Array) -> Dictionary:
	var file: FileAccess = FileAccess.open(path, FileAccess.READ)
	if not file:
		push_error("Can not open file: " + path)
		return {}
	var json: JSON = JSON.new()
	json.parse(file.get_as_text())
	var dic: Dictionary = {}
	var data: Array = json.data
	for a: int in data.size():
		var sub_data: Array = data[a]
		var attacker: Dictionary = {}
		dic[_unit_types[a]] = attacker
		for d: int in sub_data.size():
			attacker[_unit_types[d]] = sub_data[d]
	return dic


func _replace_movement_index_with_type() -> void:
	for unit: String in units:
		units[unit]["movement_type"] = movement_types[units[unit]["movement_type"]]


func _replace_weapon_index_with_types() -> void:
	for unit: String in units:
		var weapons: Array = []
		for index: int in units[unit]["weapons"]:
			if index >= 0:
				weapons.append(weapon_types[index])
		units[unit]["weapons"] = weapons


func _replace_carrying_index_with_types() -> void:
	for unit: String in units:
		var carrying_types: Array = []
		for index: int in units[unit]["carrying_types"]:
			if index >= 0:
				carrying_types.append(unit_types[index])
		units[unit]["carrying_types"] = carrying_types
