@tool
extends Node

#region configuration
@export_file var user_settings_path:String = "user://user_settings.json"
@export_file var user_saves_path:String = "user://savegames"

@export_group("Acessability")
@export var reduce_motion: bool = false
@export var screen_reader_enabled:bool = false
@export var rendering_disabled: bool = false
@export var use_simplified_navigation:bool = false
@export var show_navigation_buttons: bool = false
@export_enum("handwriting", "serif", "legible", "system") var font_style: int = 0
@export_enum("disabled", "text", "cc") var subtitles: int = false
@export var ui_scaling: float = 1:
	set(value):
		ui_scaling = value
		ProjectSettings.set_setting("gui/theme/default_theme_scale", value)
@export var show_content_notes: bool = false
@export var provide_summaries: bool = false
@export var allow_skipping: bool = false

# FIXME find a better way to switch fonts!
var current_main_theme:Theme = preload("res://logic-scenes/themes/handwriting.theme")

@export_group("AudioSettings")
@export var main_volume:float = 1:
	set(volume):
		main_volume = volume
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear_to_db(volume))
@export var sfx_muted:bool = false:
	set(mute):
		sfx_muted = mute
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(mute))
@export var sfx_volume:float = 1:
	set(volume):
		sfx_volume = volume
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(volume/2))
@export var music_muted:bool = false:
	set(mute):
		sfx_muted = mute
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(mute))
@export var music_volume:float = 1:
	set(volume):
		music_volume = volume
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("music"), linear_to_db(volume/2.5))
@export var speech_volume: float = 1:
	set(volume):
		speech_volume = volume
		AudioServer.set_bus_volume_db(AudioServer.get_bus_index("text"), linear_to_db(volume))
@export_enum("system_locale", "english", "german") var text_language: int = -1:
	set(value):
		text_language = value
		match text_language:
			0: TranslationServer.set_locale("en")
			1: TranslationServer.set_locale("de")
			_: TranslationServer.set_locale(OS.get_locale())
@export_enum("system_locale", "english", "german") var speech_language: int = -1:
	set(value):
		speech_language = value	

@export_group("Gameplay Settings")
@export var input_sensitivity: float
@export var inverty_y_axis: bool
@export_enum("off", "top_left", "top_right", "bottom_left", "bottom_right") var stream_overlay_position: int

# for passing VFX settings not contained by project settings to scene environemnt
var ssil_enable:bool = false:
	set(value):
		ssil_enable = value
		environment_settings_changed.emit()
var sdfgi_enable:bool = false:
	set(value):
		sdfgi_enable = value
		environment_settings_changed.emit()

var active_save_game: SaveGame

signal environment_settings_changed
signal theme_changed



func load_user_settings():
	
	if FileAccess.file_exists(user_settings_path):
		var file = FileAccess.open(user_settings_path, FileAccess.READ)
		var raw_json = FileAccess.get_file_as_string(user_settings_path)
		file.close()
		var parsed: Dictionary = JSON.parse_string(raw_json)
		
		for kategory in parsed.keys():
			for key in parsed[kategory].keys():
				set(key, parsed[key])

	else:
		if OS.has_feature("macos"):
			var out: Array
			OS.execute("defaults", ["read", "/Users/$loggedInUser/Library/Preferences/com.apple.universalaccess.plist", "reduceMotion"], out)
			if out[0] == "reduce":
				reduce_motion = true

func save_settings():
	var out_dict = {
		"accessability": {
			"reduce_motion:": reduce_motion,
			"screen_reader_enabled": screen_reader_enabled,
			"rendering_disabled": rendering_disabled,
			"use_simplified_navigation": use_simplified_navigation,
			"show_navigation_buttons": show_navigation_buttons,
			"subtitles": subtitles,
			"font_style": font_style,
			"ui_scaling": ui_scaling,
			"show_content_notes:": show_content_notes,
			"provide_summaries:": provide_summaries,
			"allow_skipping:": allow_skipping
		},
		"audio": {
			"main_volume": main_volume,
			"sfx_muted": sfx_muted,
			"sfx_volume": sfx_volume,
			"music_muted": music_muted,
			"music_volume": music_volume,
			"speech_volume": speech_volume
		},
		"gameplay": {
			"input_sensitivity": input_sensitivity,
			"inverty_y_axis": inverty_y_axis,
			"stream_overlay_position": stream_overlay_position
		}
	}
	
	var file = FileAccess.open(user_settings_path, FileAccess.WRITE)
	file.store_string(JSON.stringify(out_dict))
	file.close()

#endregion

func _ready():

	for child in get_tree().root.get_children():
		if "has_stage" in child:
			pass_stage_to(child)
			break
	music_volume = music_volume

#region focus handling (called staging to avoid name colisions)

var stage_list:Array = []
var focus_locked: bool = false

# Intented for use when an actor wants focus for itself, can reclaim focus, thus dropping the stack that focused.
func take_stage(actor: Object, reclaim: bool = false) -> bool:
	if focus_locked: return false
	if reclaim:
		stage_list.front().has_stage = false
		if stage_list.has(actor):
			while stage_list.pop_front() != actor: break
			actor.has_stage = true
			stage_list.push_front(actor)
			return actor.has_stage
		push_warning(actor, " wanted to reclaim focus, but was not on list.")
	return pass_stage_to(actor)

# Element no longer wants focus, if Element itself is also dropped, this option can be chosen aswell.
func leave_stage(actor:Object, dropObject: bool = false) -> bool:
	if get_tree().paused:
		push_error(actor, " wanted to drop focus while tree is paused.")
	
	if not dropObject: actor.has_stage = false
	focus_locked = false
	stage_list.erase(actor)
	
	if stage_list != []:
		stage_list.front().has_stage = true
	else:
		get_tree().quit()
	
	return false
	
func get_current_actor(): return stage_list.front()

# Used to put a new target on top of the Focus Stack.
func pass_stage_to(target:Object, force = false, lock_focus = true) -> bool:
	if "pass_to_actor" in target:
		pass_stage_to(target.pass_to_actor)
	if (focus_locked or get_tree().paused) and not force:
		push_error(target, " requested focus while it was locked or tree is paused.")
	elif !is_instance_valid(target):
		push_error("Focus instance not valid")
	elif !"has_stage" in target:
		push_error(target, " has no has focus method.")
	else:
		if stage_list.size() > 0:
			if stage_list.front() == target:
				push_warning(target, " is already target. Abort passing focus.")
				return false
		if not stage_list.size() == 0: stage_list.front().has_stage = false
		target.has_stage = true
		if target.has_stage:
			stage_list.push_front(target)
			assert(stage_list.size() < 100)
			return true
	return false
	
# Currently focused element loses focus, but remains in stack.
func free_focus():
	if focus_locked: return false
	if stage_list.size() > 0: stage_list.front().has_stage = false

func transition_stage_to(thief: Object, lock_focus = false):
	focus_locked = lock_focus
	if stage_list.size() > 0:
		if stage_list.front().has_stage:
			stage_list.pop_front().has_stage = false
	return pass_stage_to(thief, true)

func queue_for_stage(target: Object, index: int = 1):
	stage_list.insert(index, target)

#endregion

#region play state

enum rooms {
	MENU,
	DRAVEN,
	YOUTH,
	TRANSITION,
	ADULTHOOD,
	ENDING
}

var current_room = rooms.MENU
