extends Node

const SETTING_USAGE_FLAG := PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_EDITOR
const CONFIG_PATH := "user://settings.cfg"

@export_group("Gameplay")
@export_subgroup("Camera")
@export var camera_fov: float = 75:
	set(value):
		camera_fov = value

@export_group("Video")
@export_subgroup("Display")
@export var fullscreen: bool = false:
	set(value):
		DisplayServer.window_set_mode(
			(
				DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN
				if value
				else DisplayServer.WINDOW_MODE_WINDOWED
			)
		)
		fullscreen = value

@export var vsync: DisplayServer.VSyncMode = DisplayServer.VSYNC_DISABLED:
	set(value):
		DisplayServer.window_set_vsync_mode(value)
		vsync = value

@export var limit_max_fps: bool = true:
	set(value):
		if value:
			Engine.max_fps = max_fps
		else:
			Engine.max_fps = 0
		limit_max_fps = value

@export var max_fps: int = 60:
	set(value):
		if limit_max_fps:
			Engine.max_fps = value
		else:
			Engine.max_fps = 0
		max_fps = value

@export_subgroup("Quality")
@export var resolution_scale: float = 1:
	set(value):
		get_viewport().scaling_3d_scale = value
		resolution_scale = value

@export var filtering: Viewport.Scaling3DMode = Viewport.SCALING_3D_MODE_BILINEAR:
	set(value):
		get_viewport().scaling_3d_mode = value
		filtering = value

@export_group("UI")
@export var ui_scale: float = 1:
	set(value):
		get_tree().root.content_scale_factor = value
		ui_scale = value

@export_group("Audio")

@export_group("Controls")
@export_subgroup("KB & Mouse")
## rotation speed in degrees per pixel
@export var mouse_sensitivity: float = 0.5
@export var invert_mouse_horizontal: bool = false
@export var invert_mouse_vertical: bool = false

@export_subgroup("Controller")
## horizontal rotation speed in degrees per second
@export var controller_sensitivity_horizontal: float = 270
## vertical rotation speed in degrees per second
@export var controller_sensitivity_vertical: float = 120
@export var invert_controller_horizontal: bool = false
@export var invert_controller_vertical: bool = false

var _default_values: Dictionary = {}


func _init() -> void:
	for property in get_property_list():
		if _property_is_setting(property):
			var property_name := StringName(property["name"] as String)
			_default_values[property_name] = get(property_name)


func _ready() -> void:
	process_mode = Node.PROCESS_MODE_ALWAYS
	load_config()


func _input(event: InputEvent) -> void:
	if event.is_action_pressed("toggle_fullscreen"):
		fullscreen = not fullscreen


func set_defaults() -> void:
	for property_name: StringName in _default_values.keys():
		set(property_name, _default_values[property_name])

	var refresh_rate := DisplayServer.screen_get_refresh_rate()
	if refresh_rate != -1:
		max_fps = refresh_rate as int


func save_config() -> void:
	var config := ConfigFile.new()
	var section: String = ""
	for property in get_property_list():
		if _property_is_section(property):
			section = property["name"] as String

		if _property_is_setting(property):
			var property_name := StringName(property["name"] as String)
			config.set_value(section, property_name, get(property_name))

	var err := config.save(CONFIG_PATH)

	if err != OK:
		printerr("failed to save config file: %s" % err)


func load_config() -> void:
	var config := ConfigFile.new()
	var err := config.load(CONFIG_PATH)

	if err == ERR_FILE_NOT_FOUND:
		set_defaults()
		save_config()
		return

	if err != OK:
		printerr("failed to load config file: %s" % err)
		return

	var section: String = ""
	for property in get_property_list():
		if _property_is_section(property):
			section = property["name"] as String

		if _property_is_setting(property):
			var property_name := StringName(property["name"] as String)

			if not config.has_section_key(section, property_name):
				print("%s not found in config, setting default" % property_name)
				set(property_name, _default_values[property_name])
				continue

			var value: Variant = config.get_value(section, property_name)
			set(property_name, value)


func _property_is_setting(property: Dictionary) -> bool:
	return (property["usage"] as int & SETTING_USAGE_FLAG) == SETTING_USAGE_FLAG


func _property_is_section(property: Dictionary) -> bool:
	return property["usage"] == PROPERTY_USAGE_GROUP
