class_name World extends Node
## Container scene for a game world instance.
##
## A world contains player(s) and the active level, manages player states,
## and transitions between active levels.

const SCENE := "res://src/world/world.tscn"

const FF_TIME_SCALE := 4.0

@export var initial_level: PackedScene

@export var manager: PlayManager

@export var spawn_group := "PlayerSpawn"

var fast_forward: bool:
	set = _set_fast_forward
var _spawns_available: Array[Node3D] = []

@onready var level: Node3D = %Level
@onready var ui: WorldUI = %UI

@onready var world_transition: AnimationPlayer = %WorldTransition
@onready var fast_forward_effect: Control = %FastForwardEffect

@onready var game: Game = get_tree().get_first_node_in_group(Game.group)

static var group := "WorldGroup"


func _ready() -> void:
	load_level(initial_level)

	manager.spawning.connect(_spawn_player)
	manager.winner.connect(_on_winner)

	manager.initialize()

	if not manager.players:
		push_warning("Warning: Starting game world with no players!")


func _random_spawn() -> Node3D:
	# Get random spawn point
	if not _spawns_available:
		_spawns_available.assign(get_tree().get_nodes_in_group(spawn_group))
	@warning_ignore("unsafe_cast")
	var spawn_point := _spawns_available.pick_random() as Node3D
	assert(spawn_point, "No nodes in spawn group `%s` found!" % spawn_group)
	_spawns_available.erase(spawn_point)
	return spawn_point


func _spawn_player(player: WorldPlayer) -> void:
	print_debug("Spawning player: ", player)
	var spawn_point := _random_spawn()
	var shot_setup := player.shot_setup
	shot_setup.global_transform = spawn_point.global_transform
	if not shot_setup.get_parent():
		spawn_point.add_sibling(shot_setup)


func _on_winner(_player: WorldPlayer) -> void:
	get_tree().paused = true
	ui.play_winner_sequence()
	# TODO announce winner?


## Set the state of the world-wide fast-forward feature
func _set_fast_forward(value: bool) -> void:
	if value == fast_forward:
		# Ignore if not a change from the previous value
		return

	fast_forward = value

	if fast_forward:
		Engine.time_scale = FF_TIME_SCALE
		Engine.physics_ticks_per_second = int(
			Game.settings.default_physics_ticks_per_second * FF_TIME_SCALE
		)
		fast_forward_effect.show()
	else:
		Engine.time_scale = 1.0
		Engine.physics_ticks_per_second = Game.settings.default_physics_ticks_per_second
		fast_forward_effect.hide()


func fade_to_title() -> void:
	world_transition.play("fade_to_title")


func _load_title() -> void:
	game.queue_scene(TitleScreen.SCENE)


## Instantiate and mantle the given level scene.
##
## This will free any currently-loaded level!
func load_level(level_scene: PackedScene) -> void:
	# Remove any currently-loaded level assets
	for c: Node in level.get_children():
		c.queue_free()

	# Load the level
	var instance: Node3D = level_scene.instantiate()
	level.add_child(instance)
