# Localization data for LocalizationEditor : MIT License
# @author Vladimir Petrenko
extends Resource
class_name LocalizationData

signal data_changed

var _editor
var _undo_redo

@export var data: Dictionary = {"locales": [], "keys": []}
var data_filter: Dictionary = {}

var _locales_filter: String

func locales_filter() -> String:
		return _locales_filter

func set_locales_filter(text):
		_locales_filter = text
		emit_signal("data_changed")

var _locales_selected: bool

func locales_selected() ->  bool:
	return _locales_selected

func set_locales_selected(value):
		_locales_selected = value
		emit_signal("data_changed")

var data_remaps: Dictionary = {"remapkeys": []}
var data_filter_remaps: Dictionary = {}

var data_placeholders: Dictionary = {}
var data_filter_placeholders: Dictionary = {}

const uuid_gen = preload("res://addons/localization_editor/uuid/uuid.gd")

const default_path = "res://localization/"
const default_path_to_file = default_path + "localizations.csv"
const default_path_to_placeholders = default_path + "Placeholders.tres"
const AUTHOR = "# @author Vladimir Petrenko\n"
const SETTINGS_PATH_TO_FILE = "localization_editor/locales_path_to_file"
const SETTINGS_LOCALES_VISIBILITY = "localization_editor/locales_visibility"
const SETTINGS_TRANSLATIONS_SPLIT_OFFSET = "localization_editor/translations_split_offset"
const SETTINGS_PLACEHOLDERS_SPLIT_OFFSET = "localization_editor/placeholders_split_offset"

func set_editor(editor) -> void:
	_editor = editor
	if _editor:
		_undo_redo = _editor.get_undo_redo()

func editor():
	return _editor

func undo_redo():
	return _undo_redo

func emit_signal_data_changed() -> void: 
	emit_signal("data_changed")

func init_data_translations() -> void:
	_init_data_translations_csv()

func _init_data_translations_csv() -> void:
	var path = setting_path_to_file()
	if FileAccess.file_exists(path):
		var file = FileAccess.open(path, FileAccess.READ)
		var locales_line = file.get_csv_line()
		var size = locales_line.size()
		if size > 1:
			for index in range(1, size):
				add_locale(locales_line[index])
		data.keys.clear()
		while !file.eof_reached():
			var values_line = file.get_csv_line()
			if values_line.size() > 1:
				var key = {"uuid": uuid(), "value": values_line[0], "translations": []}
				for index in range(1, values_line.size()):
					var translation = {"locale": locales_line[index], "value": values_line[index]}
					key.translations.append(translation)
				data.keys.append(key)

func save_data_translations(update_script_classes = false) -> void:
	_save_data_translations_csv()
	_save_data_translations_keys()
	_save_data_translations_placeholders()
	_save_data_placeholders()
	_save_data_translations_to_project_settings()
	_save_data_remaps_keys()
	ProjectSettings.save()
	if update_script_classes:
		_editor.get_editor_interface().get_resource_filesystem().scan()

func _save_data_translations_csv() -> void:
	var path = setting_path_to_file()
	var path_directory = file_path(path)
	var directory:= DirAccess.open(path_directory)
	if not directory:
		directory.make_dir(path_directory)
	var file = FileAccess.open(setting_path_to_file(), FileAccess.WRITE)
	var locales_line: Array = ["keys"]
	var locales = data.locales
	if locales.size() <= 0:
		add_locale(OS.get_locale())
		data.keys[0].value = "KEY"
	locales_line.append_array(data.locales)
	file.store_csv_line(locales_line)
	for key in data.keys:
		var values_line: Array[String] = [key.value]
		for translation in key.translations:
			values_line.append(translation.value)
		file.store_csv_line(values_line)

func _save_data_translations_keys() -> void:
	var file = FileAccess.open(default_path + "LocalizationKeys.gd", FileAccess.WRITE)
	var source_code = "# Keys for LocalizationManger to use in source code: MIT License\n"
	source_code += AUTHOR
	source_code += "@tool\n"
	source_code += "class_name LocalizationKeys\n\n"
	for key in data.keys:
		source_code += "const " + key.value.replace(" ", "_") + " = \"" + key.value +"\"\n"
	source_code += "\nconst KEYS = [\n"
	for index in range(data.keys.size()):
		source_code += " \"" + data.keys[index].value + "\",\n"
	source_code = source_code.substr(0, source_code.rfind(",\n"))
	source_code += "\n]"
	file.store_string(source_code)

func _save_data_translations_placeholders() -> void:
	var placeholders = {}
	var regex = RegEx.new()
	regex.compile("{{(.+?)}}")
	for key in data.keys:
		var results = regex.search_all(key.translations[0].value)
		for result in results:
			var name = result.get_string()
			var clean_name = name.replace("{{", "");
			clean_name = clean_name.replace("}}", "");
			if not placeholders.has(clean_name):
				placeholders[clean_name] = name
	var file = FileAccess.open(default_path + "LocalizationPlaceholders.gd", FileAccess.WRITE)
	var source_code = "# Placeholders for LocalizationManger to use in source code: MIT License\n"
	source_code += AUTHOR
	source_code += "@tool\n"
	source_code += "class_name LocalizationPlaceholders\n\n"
	for placeholder_key in placeholders.keys():
		source_code += "const " + placeholder_key + " = \"" + placeholders[placeholder_key] +"\"\n"
	source_code += "\nconst PLACEHOLDERS = [\n"
	var count = 0
	for placeholder_key in placeholders.keys():
		source_code += " \"" + placeholder_key + "\""
		if count != placeholders.size() - 1:
			source_code += ",\n"
		count += 1
	source_code += "\n]"
	file.store_string(source_code)

func _save_data_placeholders() -> void:
	var placeholders_data = LocalizationPlaceholdersData.new()
	placeholders_data.placeholders = data_placeholders
	ResourceSaver.save(placeholders_data, default_path_to_placeholders)

func _save_data_translations_to_project_settings() -> void:
	var file = setting_path_to_file()
	file = file_path_without_extension(file)
	var translations: PackedStringArray = []
	for locale in data.locales:
		var entry = file + "." + locale + ".translation"
		translations.append(entry)
	ProjectSettings.set_setting("internationalization/locale/translations", translations)

func _save_data_remaps_keys() -> void:
	var internationalization_path = "internationalization/locale/translation_remaps"
	var file = FileAccess.open(default_path + "LocalizationRemaps.gd", FileAccess.WRITE)
	var source_code = "# Remapkeys for LocalizationManger to use in source code: MIT License\n"
	source_code += AUTHOR
	source_code += "@tool\n"
	source_code += "class_name LocalizationRemaps\n\n"
	if ProjectSettings.has_setting(internationalization_path):
		var settings_remaps = ProjectSettings.get_setting(internationalization_path)
		if settings_remaps.size():
			var keys = settings_remaps.keys();
			for key in keys:
				var filename = key.get_file()
				source_code += "const " + filename.replace(".", "_").to_upper() + " = \"" + filename.replace(".", "_").to_upper() +"\"\n"
			file.store_string(source_code)

# ***** UUID ****
static func uuid() -> String:
	return uuid_gen.v4()

# ***** LOCALES *****
func locales() -> Array:
	return data.locales

func check_locale(locale: String) -> void:
	if find_locale(locale) != null:
		add_locale(locale)

func find_locale(code: String) -> Variant:
	if data.has("locales"):
		for locale in data.locales:
			if locale == code:
				return locale
	return null

func add_locale(locale: String, sendSignal = true) -> void:
	if not data.locales.has(locale):
		if _undo_redo != null:
			_undo_redo.create_action("Add locale " + locale)
			_undo_redo.add_do_method(self, "_add_locale", locale, sendSignal)
			_undo_redo.add_undo_method(self, "_del_locale", locale)
			_undo_redo.commit_action()
		else:
			_add_locale(locale, sendSignal)

func _add_locale(locale, sendSignal: bool) -> void:
	data.locales.append(locale)
	if data.keys.size() <= 0:
		_add_key(uuid())
	for key in data.keys:
		if not key_has_locale(key, locale):
			key.translations.append({"locale": locale, "value": ""})
	if data_remaps.remapkeys.size() <= 0:
		_add_remapkey(uuid())
	for remapkey in data_remaps.remapkeys:
		if not remapkey_has_locale(remapkey, locale):
			remapkey.remaps.append({"locale": locale, "value": ""})
	if sendSignal:
		emit_signal("data_changed")

func del_locale(locale: String) -> void:
	if data.locales.has(locale):
		if _undo_redo != null:
			_undo_redo.create_action("Del locale " + locale)
			_undo_redo.add_do_method(self, "_del_locale", locale)
			_undo_redo.add_undo_method(self, "_add_locale", locale)
			_undo_redo.commit_action()
		else:
			_del_locale(locale)

func _del_locale(locale: String) -> void:
	data.locales.erase(locale)
	setting_locales_visibility_del(locale)
	for key in data.keys:
		for translation in key.translations:
			if translation.locale == locale:
				var t = key.translations.find(translation)
				key.translations.remove_at(t)
				break
	for remapkey in data_remaps.remapkeys:
		for remap in remapkey.remaps:
			if remap.locale == locale:
				var r = remapkey.remaps.find(remap)
				remapkey.remaps.remove_at(r)
				break
	emit_signal("data_changed")

# ***** KEYS *****
signal data_key_value_changed

func emit_data_key_value_changed() -> void:
	emit_signal("data_key_value_changed")

func keys() -> Array:
	return data.keys

func keys_filtered() -> Array:
	var keys = _filter_by_keys()
	for filter_key in data_filter.keys():
		if filter_key != "keys":
			keys = _key_filter_by_translations(keys, filter_key)
	return keys

func _filter_by_keys() -> Array:
	var keys = []
	for key in data.keys:
		if not data_filter.has("keys") or data_filter["keys"] == "" or key.value == null or key.value == "" or data_filter["keys"] in key.value:
			keys.append(key)
	return keys

func _key_filter_by_translations(keys, locale) -> Array:
	var new_keys = []
	for key in keys:
		var value = translation_value_by_locale(key, locale)
		if data_filter[locale] == "" or value == null or value == ""  or data_filter[locale] in value:
			new_keys.append(key)
	return new_keys

func translation_value_by_locale(key, locale) -> String:
	var translation = translation_by_locale(key, locale)
	if translation != null and translation.value != null:
		return translation.value 
	return ""

func translation_by_locale(key, locale):
	for translation in key.translations:
		if translation.locale == locale:
			return translation 
	return null

func add_key_object(key) -> void:
	data.keys.append(key)

func _add_key(uuid: String, emitSignal = true) -> void:
	data.keys.append(_create_key(uuid))
	if emitSignal:
		emit_signal("data_changed")

func add_key_new_after_uuid(after_uuid: String, uuid = uuid()) -> void:
	if _undo_redo != null:
		_undo_redo.create_action("Add new key " + uuid + " after " + after_uuid)
		_undo_redo.add_do_method(self, "_add_key_new_after_uuid", after_uuid, uuid)
		_undo_redo.add_undo_method(self, "_del_key", uuid)
		_undo_redo.commit_action()
	else:
		_add_key_new_after_uuid(after_uuid, uuid)

func _add_key_after_uuid(after_uuid: String, key) -> void:
	var position = _key_position(after_uuid)
	if position != -1 and position < data.keys.size():
		data.keys.insert(position + 1, key)
		emit_signal("data_changed")
	else:
		data.keys.append(key)

func _add_key_new_after_uuid(after_uuid: String, uuid = uuid()) -> void:
	var position = _key_position(after_uuid)
	if position != -1 and position < data.keys.size():
		var key = _create_key(uuid)
		data.keys.insert(position + 1, key)
		emit_signal("data_changed")
	else:
		_add_key(uuid)

func _create_key(uuid: String):
	var key = {"uuid": uuid, "value": "", "translations": []}
	for locale in data.locales:
		var translation = {"locale": locale, "value": ""}
		key.translations.append(translation)
	return key

func del_key(uuid: String, emitSignal = true) -> void:
	if _undo_redo != null:
		var before_uuid = before_uuid(uuid)
		var key = key(uuid)
		_undo_redo.create_action("Del key " + uuid)
		_undo_redo.add_do_method(self, "_del_key", uuid)
		_undo_redo.add_undo_method(self, "_add_key_after_uuid", before_uuid, key)
		_undo_redo.commit_action()
	else:
		_del_key(uuid)

func _del_key(uuid: String, emitSignal = true) -> void:
	data.keys.remove_at(_key_position(uuid))
	if emitSignal:
		emit_signal("data_changed")

func after_uuid(uuid: String):
	var position = _key_position(uuid)
	if position != -1 and position < data.keys.size():
		return data.keys[position + 1].uuid
	else:
		return null

func before_uuid(uuid: String):
	var position = _key_position(uuid)
	if position > 0:
		return data.keys[position - 1].uuid
	else:
		return null

func _key_position(uuid: String) -> int:
	for index in range(data.keys.size()):
		if data.keys[index].uuid == uuid:
			return index
	return -1

func key_has_locale(key, locale: String) -> bool:
	for translation in key.translations:
		if translation.locale == locale:
			return true
	return false

func key_value(uuid: String):
	var key = key(uuid)
	if key != FAILED:
		return key.value
	else:
		return "" 

func key_value_change(key, key_value: String):
	if _undo_redo != null:
		_undo_redo.create_action("Change key value ")
		_undo_redo.add_do_method(self, "_key_value_change", key, key_value)
		_undo_redo.add_undo_method(self, "_key_value_change", key, "" + key.value)
		_undo_redo.commit_action()
	else:
		_key_value_change(key, key_value)

func _key_value_change(key, key_value: String):
	key.value = key_value
	emit_data_key_value_changed()

func key(uuid: String):
	for key in data.keys:
		if key.uuid == uuid:
			return key
	return FAILED

func is_key_value_double(value: String) -> bool:
	var count = 0
	for key in data.keys:
		if key.value != null and !key.value.length() <= 0  and key.value == value:
			count = count + 1
		if count > 1:
			return true
	return false

# ***** TRANSLATIONS *****
func get_translations_by_locale(locale: String) -> Array:
	var translations = []
	for key in data.keys:
		for translation in key.translations:
			if translation.locale == locale:
				translations.append(translation)
				break
	return translations

func key_by_translation(translation):
	for key in data.keys:
		for translation_obj in key.translations:
			if translation == translation_obj:
				return key
	return null

func get_translations() -> Array:
	var translations = {}
	for locale in data.locales:
		var translation_for_server = Translation.new()
		translation_for_server.set_locale(locale)
		translations[locale] = translation_for_server
	for key in data.keys:
		for translation in key.translations:
			translations[translation.locale].add_message(key.value, str(translation.value))
	return translations.values()

# ***** VALUE *****
func value_by_locale_key(locale_value: String, key_value: String) -> String:
	for key in data.keys:
		if key.value == key_value:
			for translation in key.translations:
				if translation.locale == locale_value:
					return translation.value 
	return key_value

# ***** FILTER *****
func data_filter_by_type(type: String) -> String:
	return data_filter[type] if data_filter.has(type) else ""

func data_filter_put(type: String, filter: String) -> void:
	data_filter[type] = filter
	emit_signal("data_changed")

func data_filter_remaps_by_type(type: String) -> String:
	return data_filter_remaps[type] if data_filter_remaps.has(type) else ""

func data_filter_remaps_put(type: String, filter: String) -> void:
	data_filter_remaps[type] = filter
	emit_signal("data_changed")

func data_filter_placeholders_by_type(type: String) -> String:
	return data_filter_placeholders[type] if data_filter_remaps.has(type) else ""

func data_filter_placeholders_put(type: String, filter: String) -> void:
	data_filter_placeholders[type] = filter
	emit_signal("data_changed")

# ***** REMAPS *****
func remaps() -> Array:
	return data_remaps.remapkeys

func init_data_remaps() -> void:
	var internationalization_path = "internationalization/locale/translation_remaps"
	data_remaps.remapkeys = []
	if ProjectSettings.has_setting(internationalization_path):
		var settings_remaps = ProjectSettings.get_setting(internationalization_path)
		if settings_remaps.size():
			var keys = settings_remaps.keys();
			for key in keys:
				var remaps = []
				for remap in settings_remaps[key]:
					var index = remap.rfind(":")
					var locale  = remap.substr(index + 1)
					check_locale(locale)
					var value = remap.substr(0, index)
					var remap_new = {"locale": locale, "value": value }
					remaps.append(remap_new)
				data_remaps.remapkeys.append({"uuid": uuid(), "remaps": remaps})
			_check_remapkeys()
			return
	var remap = _create_remapkey(uuid())
	data_remaps.remapkeys.append(remap)

func _check_remapkeys() -> void:
	for locale in locales():
		for remapkey in data_remaps.remapkeys:
			if not remapkey_has_locale(remapkey, locale):
				remapkey.remaps.append({"locale": locale, "value": ""})

func save_data_remaps() -> void:
	var remapkeys = data_remaps.remapkeys.size() > 1 or data_remaps.remapkeys.size() == 1
	if remapkeys:
		if data_remaps.remapkeys[0].remaps.size() > 1 and data_remaps.remapkeys[0].remaps[0].value.length() > 0:
			_save_data_remaps()

func _save_data_remaps() -> void:
	var remaps = {}
	for remapkey in  data_remaps.remapkeys:
		if remapkey.remaps.size() > 0:
			var key = remapkey.remaps[0].value
			remaps[key] = []
			for index in range(0, remapkey.remaps.size()):
				var remap = remapkey.remaps[index]
				var value = remap.value + ":" + remap.locale
				remaps[key].append(value)
	ProjectSettings.set_setting("internationalization/locale/translation_remaps", remaps)

signal data_remapkey_value_changed

func emit_data_remapkey_value_changed() -> void:
	emit_signal("data_remapkey_value_changed")

func remapkeys_filtered() -> Array:
	var remapkeys = _filter_by_remapkeys()
	for filter_remapkey in data_filter_remaps.keys():
		if filter_remapkey != "remapkeys":
			remapkeys = _remapkey_filter_by_remaps(remapkeys, filter_remapkey)
	return remapkeys

func _filter_by_remapkeys() -> Array:
	var remapkeys = []
	for remapkey in data_remaps.remapkeys:
		if _remapkey_allow_by_filter(remapkey):
			remapkeys.append(remapkey)
	return remapkeys

func _remapkey_allow_by_filter(remapkey) -> bool:
	if data_filter_remaps.has("remapkeys"):
		if data_filter_remaps.remapkeys.length() <= 0:
			return true
		else:
			for remap in remapkey.remaps:
				if remap_type(remap) in data_filter_remaps.remapkeys:
					return true
			return false
	else:
		return true

func _remapkey_filter_by_remaps(remapkeys, locale) -> Array:
	var new_remapkeys = []
	for remapkey in remapkeys:
		var value = _remap_value_by_locale(remapkey, locale)
		if data_filter_remaps[locale] == "" or value == null or value == ""  or data_filter_remaps[locale] in value:
			new_remapkeys.append(remapkey)
	return new_remapkeys

func _remap_value_by_locale(remapkey, locale):
	for remap in remapkey.remaps:
		if remap.locale == locale:
			if remap.value != null:
				return remap.value 
	return ""

func add_remapkey_object(remapkey) -> void:
	data_remaps.remapkeys.append(remapkey)

func _add_remapkey(uuid: String, emitSignal = true) -> void:
	data_remaps.remapkeys.append(_create_remapkey(uuid))
	if emitSignal:
		emit_signal("data_changed")

func add_remapkey_new_after_uuid_remap(after_uuid_remap: String, uuid = uuid()) -> void:
	if _undo_redo != null:
		_undo_redo.create_action("Add new remapkey " + uuid + " after " + after_uuid_remap)
		_undo_redo.add_do_method(self, "_add_remapkey_new_after_uuid_remap", after_uuid_remap, uuid)
		_undo_redo.add_undo_method(self, "_del_remapkey", uuid)
		_undo_redo.commit_action()
	else:
		_add_remapkey_new_after_uuid_remap(after_uuid_remap, uuid)

func _add_remapkey_after_uuid_remap(after_uuid_remap: String, remapkey) -> void:
	var position = _remapkey_position(after_uuid_remap)
	if position != -1 and position < data_remaps.remapkeys.size():
		data_remaps.remapkeys.insert(position + 1, remapkey)
		emit_signal("data_changed")
	else:
		data_remaps.remapkeys.append(remapkey)

func _add_remapkey_new_after_uuid_remap(after_uuid_remap: String, uuid = uuid()) -> void:
	var position = _remapkey_position(after_uuid_remap)
	if position != -1 and position < data_remaps.remapkeys.size():
		var remapkey = _create_remapkey(uuid)
		data_remaps.remapkeys.insert(position + 1, remapkey)
		emit_signal("data_changed")
	else:
		_add_remapkey(uuid)

func _create_remapkey(uuid: String):
	var remapkey = {"uuid": uuid, "remaps": []}
	for locale in data.locales:
		var remap = {"locale": locale, "value": ""}
		remapkey.remaps.append(remap)
	return remapkey

func del_remapkey(uuid: String, emitSignal = true) -> void:
	if _undo_redo != null:
		var before_uuid_remap = before_uuid_remap(uuid)
		var remapkey = remapkey(uuid)
		_undo_redo.create_action("Del remapkey " + uuid)
		_undo_redo.add_do_method(self, "_del_remapkey", uuid)
		_undo_redo.add_undo_method(self, "_add_remapkey_after_uuid_remap", before_uuid_remap, remapkey)
		_undo_redo.commit_action()
	else:
		_del_remapkey(uuid)

func _del_remapkey(uuid: String, emitSignal = true) -> void:
	data_remaps.remapkeys.remove_at(_remapkey_position(uuid))
	if emitSignal:
		emit_signal("data_changed")

func after_uuid_remap(uuid: String):
	var position = _remapkey_position(uuid)
	if position != -1 and position < data_remaps.remapkeys.size():
		return data_remaps.remapkeys[position + 1].uuid
	else:
		return null

func before_uuid_remap(uuid: String):
	var position = _remapkey_position(uuid)
	if position > 0:
		return data_remaps.remapkeys[position - 1].uuid
	else:
		return null

func _remapkey_position(uuid: String) -> int:
	for index in range(data_remaps.remapkeys.size()):
		if data_remaps.remapkeys[index].uuid == uuid:
			return index
	return -1

func remapkey_has_locale(remapkey, locale: String) -> bool:
	for remap in remapkey.remaps:
		if remap.locale == locale:
			return true
	return false

func remapkey_value(uuid: String):
	var remapkey = remapkey(uuid)
	if remapkey != FAILED:
		return remapkey.value
	else:
		return "" 

func remapkey_value_change(remapkey, remapkey_value: String):
	if _undo_redo != null:
		_undo_redo.create_action("Change remapkey value ")
		_undo_redo.add_do_method(self, "_remapkey_value_change", remapkey, remapkey_value)
		_undo_redo.add_undo_method(self, "_remapkey_value_change", remapkey, "" + remapkey.value)
		_undo_redo.commit_action()
	else:
		_remapkey_value_change(remapkey, remapkey_value)

func _remapkey_value_change(remapkey, remapkey_value: String):
	remapkey.value = remapkey_value
	emit_data_remapkey_value_changed()

func remapkey(uuid: String):
	for remapkey in data_remaps.remapkeys:
		if remapkey.uuid == uuid:
			return remapkey
	return FAILED

func remap_type(remap) -> String:
	match file_extension(remap.value):
		"ogg", "wav", "mp3":
			return "audio"
		"bmp", "dds", "exr", "hdr", "jpg", "jpeg", "png", "tga", "svg", "svgz", "webp":
			return "image"
		"webm", "o", "ogv":
			return "video"
		_:
			return "undefined"

func supported_file_extensions() -> Array:
	return ["ogg", "ogv", "wav", "mp3", "bmp", "dds", "exr", "hdr", "jpg", "jpeg", "png", "tga", "svg", "svgz", "webp", "webm", "o"]

# ***** PLACEHOLDERS *****
func init_data_placeholders() -> void:
	var  placeholders = calc_placeholders()
	for key in placeholders.keys():
		if not data_placeholders.has(key):
			data_placeholders[key] = placeholders[key]
	if FileAccess.file_exists(default_path_to_placeholders):
		var resource = ResourceLoader.load(default_path_to_placeholders)
		if resource and resource.placeholders and not resource.placeholders.size() <= 0:
			for key in resource.placeholders.keys():
					data_placeholders[key] = resource.placeholders[key]

func calc_placeholders() -> Dictionary:
	var placeholders = {}
	var regex = RegEx.new()
	regex.compile("{{(.+?)}}")
	for key in data.keys:
		for index in range(key.translations.size()):
			var results = regex.search_all(key.translations[index].value)
			for result in results:
				var name = result.get_string()
				var clean_name = name.replace("{{", "");
				clean_name = clean_name.replace("}}", "");
				if not placeholders.has(name):
					var placeholder = {}
					for locale in data.locales:
						placeholder[locale] = ""
					placeholders[clean_name] = placeholder
	return placeholders

func placeholders_filtered() -> Dictionary:
	var placeholders = _filter_by_placeholderkeys()
	for filter_placeholderkey in data_filter_placeholders.keys():
		if filter_placeholderkey != "placeholderkeys":
			placeholders = _key_filter_by_placeholders(placeholders, filter_placeholderkey)
	return placeholders

func _key_filter_by_placeholders(placeholders, locale) -> Dictionary:
	var new_placeholders = {}
	for placeholderkey in placeholders.keys():
		var value = placeholders[placeholderkey][locale]
		if data_filter_placeholders[locale] == "" or data_filter_placeholders[locale] in value:
			new_placeholders[placeholderkey] = placeholders[placeholderkey]
	return new_placeholders

func _filter_by_placeholderkeys() -> Dictionary:
	var placeholders = {}
	for placeholderkey in data_placeholders.keys():
		if not data_filter_placeholders.has("placeholderkeys") or data_filter_placeholders["placeholderkeys"] == "" or placeholderkey == null or placeholderkey == "" or data_filter_placeholders["placeholderkeys"] in placeholderkey:
			placeholders[placeholderkey] = data_placeholders[placeholderkey]
	return placeholders

func del_placeholder(key: String, emitSignal = true) -> void:
	if _undo_redo != null:
		var placeholder = data_placeholders[key]
		_undo_redo.create_action("Del _del_placeholder " + key)
		_undo_redo.add_do_method(self, "_del_placeholder", key)
		_undo_redo.add_undo_method(self, "_add_placeholder", key, placeholder)
		_undo_redo.commit_action()
	else:
		_del_placeholder(key)

func _del_placeholder(key: String, emitSignal = true) -> void:
	data_placeholders.erase(key)
	if emitSignal:
		emit_signal("data_changed")

func _add_placeholder(key: String, placeholder, emitSignal = true):
	data_placeholders[key] = placeholder
	if emitSignal:
		emit_signal("data_changed")

# ***** EDITOR SETTINGS *****
signal settings_changed

func setting_path_to_file() -> String:
	var path = default_path_to_file
	if ProjectSettings.has_setting(SETTINGS_PATH_TO_FILE):
		path = ProjectSettings.get_setting(SETTINGS_PATH_TO_FILE)
	return path
	
func setting_path_to_file_put(path: String) -> void:
	ProjectSettings.set_setting(SETTINGS_PATH_TO_FILE, path)
	emit_signal("settings_changed")

func is_locale_visible(locale: String) -> bool:
	if not ProjectSettings.has_setting(SETTINGS_LOCALES_VISIBILITY):
		return true
	var locales = ProjectSettings.get_setting(SETTINGS_LOCALES_VISIBILITY)
	return not locales.has(locale)

func setting_locales_visibility_put(locale: String) -> void:
	var locales = []
	if ProjectSettings.has_setting(SETTINGS_LOCALES_VISIBILITY):
		locales = ProjectSettings.get_setting(SETTINGS_LOCALES_VISIBILITY)
	if not locales.has(locale):
		locales.append(locale)
		ProjectSettings.set_setting(SETTINGS_LOCALES_VISIBILITY, locales)
		emit_signal("data_changed")

func setting_locales_visibility_del(locale: String, emitSignal = true) -> void:
	if ProjectSettings.has_setting(SETTINGS_LOCALES_VISIBILITY):
		var locales = ProjectSettings.get_setting(SETTINGS_LOCALES_VISIBILITY)
		if locales.has(locale):
			locales.erase(locale)
			ProjectSettings.set_setting(SETTINGS_LOCALES_VISIBILITY, locales)
			if emitSignal:
				emit_signal("data_changed")

func setting_translations_split_offset() -> int:
	var offset = 350
	if ProjectSettings.has_setting(SETTINGS_TRANSLATIONS_SPLIT_OFFSET):
		offset = ProjectSettings.get_setting(SETTINGS_TRANSLATIONS_SPLIT_OFFSET)
	return offset

func setting_translations_split_offset_put(offset: int) -> void:
	ProjectSettings.set_setting(SETTINGS_TRANSLATIONS_SPLIT_OFFSET, offset)

func setting_placeholders_split_offset() -> int:
	var offset = 350
	if ProjectSettings.has_setting(SETTINGS_PLACEHOLDERS_SPLIT_OFFSET):
		offset = ProjectSettings.get_setting(SETTINGS_PLACEHOLDERS_SPLIT_OFFSET)
	return offset

func setting_placeholders_split_offset_put(offset: int) -> void:
	ProjectSettings.set_setting(SETTINGS_PLACEHOLDERS_SPLIT_OFFSET, offset)

# ***** UTILS *****
func filename(value: String) -> String:
	var index = value.rfind("/")
	return value.substr(index + 1)

func file_path_without_extension(value: String) -> String:
	var index = value.rfind(".")
	return value.substr(0, index)

func file_path(value: String) -> String:
	var index = value.rfind("/")
	return value.substr(0, index)

func file_extension(value: String):
	var index = value.rfind(".")
	if index == -1:
		return null
	return value.substr(index + 1)
