extends Node

class_name WeaponSystem

@export var animation_player: AnimationPlayer
@export var camera: PlayerCamera
@export var player: Player

var current_state: WeaponSubStateMachine
var last_slot: StringName
var disabled: bool

var slots: Dictionary[StringName,WeaponSubStateMachine] = {
	"primary": null,
	"secondary": null,
	"knife": null,
	"bomb": null,
	"ability_first": null,
	"ability_second": null,
	"ability_third": null,
	"ultimate": null
}

signal switched_to(state: WeaponSubStateMachine)
signal slots_updated(slots: Dictionary[StringName,WeaponSubStateMachine])
signal ammo_updated(ammo: int, remaining_ammo: int)

func _ready() -> void:
	$WeaponSpawner.spawn_function = pick_up_weapon

func get_speed_modifier() -> float:
	if current_state == null:
		return 1
	return current_state.speed_modifier

func can_add(slot: StringName) -> bool:
	return slots.has(slot) and slots[slot] == null

@rpc("call_local","reliable")
func add(state: WeaponSubStateMachine, slot: StringName,ignore_parent: bool = false) -> void:
	if can_add(slot) == false:
		return
	
	if ignore_parent == false:
		if state.get_parent() == null:
			add_child(state, true)
		if state.get_parent() != self:
			state.get_parent().remove_child(state)
			add_child(state,true)
			state.ready.emit()
	
	slots[slot] = state
	state.system = self
	state.animation_player = animation_player
	state.player_camera = camera
	state.player = player
	state.request_return.connect(return_to_previous)
	state.ammo_depleted.connect(check_for_empty)
	state.ammo_updated.connect(on_ammo_updated)
	slots_updated.emit(slots)
	
	if current_state == null:
		current_state = state
		ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)
		state.enter.call_deferred()

func switch(to: StringName, exit: bool = true):
	if slots.has(to) == false or slots[to] == null or slots[to] == current_state or is_multiplayer_authority() == false:
		return
	if current_state != null and exit:
		current_state.exit()
		if current_state.can_be_previous:
			last_slot = slots.find_key(current_state)
		else:
			last_slot = ""
	current_state = slots[to]
	current_state.enter()
	
	ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)
	switched_to.emit(current_state)

func return_to_previous(exit: bool = true):
	if last_slot != "":
		switch(last_slot, exit)
	else:
		switch("knife", exit)

func drop_current():
	drop(current_state)

func drop(weapon: WeaponSubStateMachine) -> void:
	if slots.find_key(weapon) == "knife":
		return
	var drop_data: Dictionary = {}
	drop_data.scene = weapon.droppable
	drop_data.ammo = weapon.ammo
	drop_data.remaining_ammo = weapon.remaining_ammo
	drop_data.slot = weapon.slot
	drop_data.position = camera.global_position
	drop_data.impulse = -camera.global_basis.z * 10 + player.velocity
	
	Session.spawn(drop_data)
	
	$"../PickupRange".start_temp_ignore()
	
	slots[slots.find_key(weapon)] = null
	slots_updated.emit(slots)
	weapon.queue_free()
	return_to_previous(false)

func drop_slot(slot: StringName):
	if slots.has(slot) == false or slots[slot] == null:
		return
	drop(slots[slot])

# Spawn function
# Data should be a dictionary with these keys:
# 	ammo
# 	remaining_ammo
# 	scene_file_path
func pick_up_weapon(data: Variant) -> Node:
	if data.has("scene_file_path") == false:
		return Node.new()
	if data.has_all(["ammo","remaining_ammo"]):
		var scene: WeaponSubStateMachine = load(data["scene_file_path"]).instantiate()
		scene.ammo = data["ammo"]
		scene.remaining_ammo = data["remaining_ammo"]
		scene.slot = data["slot"]
		scene.set_multiplayer_authority(get_multiplayer_authority())
		
		add(scene,scene.slot,true)
		
		return scene
	else:
		var scene: WeaponSubStateMachine = load(data["scene_file_path"]).instantiate()
		scene.set_multiplayer_authority(get_multiplayer_authority())
		
		add(scene,scene.slot,true)
		
		return scene

func check_for_empty() -> void:
	if is_multiplayer_authority() == false:
		return
	for child in get_children():
		if child is WeaponSubStateMachine and child.ammo == 0 and child.remaining_ammo == 0 and child.destroy_when_empty:
			child.queue_free()

func on_ammo_updated() -> void:
	ammo_updated.emit(current_state.ammo,current_state.remaining_ammo)

func disable() -> void:
	disabled = true

func _process(delta: float) -> void:
	if current_state == null or disabled:
		return
	current_state.update(delta)

func _physics_process(delta: float) -> void:
	if current_state == null or disabled:
		return
	current_state.physics_update(delta)
