# Hurry Curry! - a game about cooking
# Copyright (C) 2025 Hurry Curry! contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
class_name G
extends Node

const VERSION := "2.3.5"
const DISTRIBUTION := "unknown"

signal using_joypad_change(using: bool)
signal using_touch_change(using: bool)
@warning_ignore("UNUSED_SIGNAL")
signal hand_count_change(count: bool)

var using_joypad := false
var using_touch := false

var game_paused := false
var hand_count := 0

var current_map_name := ""
var last_map_name := "" # last map name for displaying in rating menu

var focused_node: Control
var focused_menu: Menu # only use this as a last resort, currently exists to open setup menu from settings

func _ready():
	GLTFDocument.register_gltf_document_extension(GLTFApplyNodeVisibility.new())
	Profile.load(OS.get_data_dir().path_join("hurrycurry").path_join("profile"))
	Settings.load(OS.get_config_dir().path_join("hurrycurry").path_join("settings.json"))
	get_viewport().gui_focus_changed.connect(Sound.play_hover_maybe)
	get_viewport().gui_focus_changed.connect(func(node): focused_node = node)

func _input(event):
	if Input.is_action_just_pressed("fullscreen"):
		match Settings.read("graphics.fullscreen"):
			"keep":
				var fs = DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN
				if fs: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
				else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
			"always": Settings.write("graphics.fullscreen", "never")
			"never": Settings.write("graphics.fullscreen", "always")

	# Update using_joypad variable
	if event is InputEventMouseButton or event is InputEventKey:
		if using_joypad:
			using_joypad = false
			using_joypad_change.emit(using_joypad)
	elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
		if not using_joypad:
			using_joypad = true
			using_joypad_change.emit(using_joypad)
	
	# Update using_touch variable
	if Settings.read("ui.touch_controls") == "automatic": # Only if set to automatic
		if event is InputEventScreenTouch or event is InputEventScreenDrag:
			if not using_touch:
				using_touch = true
				using_touch_change.emit(using_touch)
		if event is InputEventKey or event is InputEventJoypadButton or event is InputEventJoypadMotion:
			if using_touch:
				using_touch = false
				using_touch_change.emit(using_touch)

func on_mobile() -> bool:
	var os_name := OS.get_name()
	return os_name == "Android" or os_name == "iOS"

func on_high_end() -> bool:
	if on_mobile():
		return false
	return on_vulkan()

func on_vulkan() -> bool:
	return ProjectSettings.get_setting("rendering/rendering_device/driver") == "vulkan"

static func interpolate(current, target, dt):
	return target + (current - target) * exp(-dt)

static func interpolate_linear(current, target, dt):
	if current > target: return max(target, current - dt)
	else: return min(target, current + dt)

static func interpolate_angle(current, target, dt) -> float:
	current = fmod(current, PI * 2)
	target = fmod(target, PI * 2)
	if target - current > PI: target -= PI * 2
	elif current - target > PI: current -= PI * 2
	return target + (current - target) * exp(-dt)

# TODO not working in all cases yet but there was an attempt
static func angle_closest_quarter(current, target) -> float:
	current = fmod(current, PI * 2)
	target = fmod(target, PI * 2)
	while abs(target - current) > PI / 4.:
		if target - current < 0: target += PI / 2
		else: target -= PI / 2
	return target

func find_menu(node: Node) -> Menu:
	if node is Menu: return node
	else: return find_menu(node.get_parent())

# TODO: move function into MessageParser
func get_message_str(m: Dictionary) -> String:
	if "text" in m: return m.text
	if "translation" in m: return tr(m.translation.id).format(m.translation.params.map(get_message_str))
	return "[unsupported message type]"

func language_list():
	var a = TranslationServer.get_loaded_locales()
	a.sort()
	a.insert(0, "system")
	return a

static func unordered_array_eq(a: Array, b: Array) -> bool:
	return unordered_array_subset(a, b) and unordered_array_subset(b, a)

static func unordered_array_subset(a: Array, b: Array) -> bool:
	return a.all(func(e): return b.has(e))

static func add_missing_keys(dict: Dictionary, reference: Dictionary):
	for k in reference.keys():
		if !dict.has(k) or typeof(dict[k]) != typeof(reference[k]):
			dict[k] = reference[k]
		else:
			if dict[k] is Dictionary:
				add_missing_keys(dict[k], reference[k])

func array_has_all(parent: Array, children: Array) -> bool:
	for i in children:
		if not i in parent:
			return false
	return true

func configure_viewport_aa(vp: Viewport) -> void:
	match Settings.read("graphics.aa"):
		"disabled":
			vp.msaa_3d = Viewport.MSAA_DISABLED
			vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
		"fx":
			vp.msaa_3d = Viewport.MSAA_DISABLED
			vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA
		"ms2x":
			vp.msaa_3d = Viewport.MSAA_2X
			vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
		"ms4x":
			vp.msaa_3d = Viewport.MSAA_4X
			vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED

static func index_to_hand(i):
	match i:
		0: return "left"
		1: return "right"
		_: return "unknown"

static func rem_euclid(i: int, mod: int) -> int:
	var num := i % mod
	if num < 0:
		num += mod
		num %= mod
	return num
