class_name EnemySpawner extends Node3D

signal enemy_spawned()

@export var land_enemy_scene: PackedScene
@export var leap_enemy_scene: PackedScene
@export var air_enemy_scene: PackedScene
@export var path: VisualizedPath
var astar: AStarGraph3D
@export var flow_field: FlowField
@export var own_id: int = 0
@export var type: Data.EnemyType
@export var dest: Node3D
@export var enemy_path: Node

var astar_point_id: int = 0
var enemy_died_callback: Callable
var enemy_reached_goal_callback: Callable
var current_wave: Array[EnemyCard]
var enemy_types_to_spawn: Dictionary = {}
var enemy_spawn_timers: Dictionary = {}
var enemies_spawned: Dictionary = {}
var enemies_to_spawn: int = 0
var done_spawning: bool = true
var enemy_id: int = 0
var new_path: Path3D
var path_polygon: PackedScene = preload("res://path_polygon.tscn")


func _ready() -> void:
	create_path()


func _process(delta: float) -> void:
	if enemies_to_spawn == 0:
		done_spawning = true
		return
	
	for enemy: Enemy in enemy_spawn_timers:
		if enemies_spawned[enemy] == enemy_types_to_spawn[enemy]:
			continue
		
		enemy_spawn_timers[enemy] += delta
		
		if enemy_spawn_timers[enemy] >= enemy.spawn_cooldown:
			if is_multiplayer_authority():
				if type == Data.EnemyType.LAND:
					networked_spawn_land_enemy.rpc(enemy.title, own_id, enemy_id)
				if type == Data.EnemyType.AIR:
					var radius: float = 10.0
					var random_dir: Vector3 = Vector3(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1))
					var random_pos: Vector3 = randf_range(0, radius) * random_dir.normalized()
					networked_spawn_air_enemy.rpc(enemy.title, random_pos, own_id, enemy_id)
			
			enemy_spawn_timers[enemy] -= enemy.spawn_cooldown
			enemy_spawned.emit()
			enemy_id += 1
			enemies_spawned[enemy] += 1
			enemies_to_spawn -= 1


#TODO: not sure enemies need all this info over the network
@rpc("reliable", "call_local")
func networked_spawn_land_enemy(enemy_stats: String, id1: int, id2: int) -> void:
	var e_stats: Enemy = null
	for enemy: Enemy in Data.enemies:
		if enemy.title == enemy_stats:
			e_stats = enemy
	var enemy: EnemyController
	enemy = e_stats.scene.instantiate()
	enemy.name = str(id1) + str(id2)
	enemy.stats = e_stats
	enemy.died.connect(enemy_died_callback)
	enemy.reached_goal.connect(enemy_reached_goal_callback)
	#enemy.movement_controller.path = path.curve
	#enemy.movement_controller.astar = astar
	enemy.movement_controller.flow_field = flow_field
	enemy.position = global_position
	enemy_path.add_child(enemy)


func create_path() -> void:
	if type != Data.EnemyType.LAND:
		return
	new_path = Path3D.new()
	new_path.curve = Curve3D.new()
	add_child(new_path)
	var polygon: CSGPolygon3D = path_polygon.instantiate()
	new_path.add_child(polygon)
	polygon.mode = CSGPolygon3D.MODE_PATH
	polygon.path_node = new_path.get_path()
	new_path.global_position = Vector3.ZERO
	update_path()



func update_path() -> void:
	if type != Data.EnemyType.LAND:
		return
		new_path.curve.add_point(global_position + Vector3(0, 0.5, 0))
	new_path.curve = Curve3D.new()
	var node: FlowNode = flow_field.get_closest_traversable_point(global_position)
	new_path.curve.add_point(node.global_position + Vector3(0, 0.5, 0))
	while node.best_path:
		node = node.best_path
		new_path.curve.add_point(node.global_position + Vector3(0, 0.5, 0))



@rpc("reliable", "call_local")
func networked_spawn_air_enemy(enemy_stats: String, pos: Vector3, id1: int, id2: int) -> void:
	var e_stats: Enemy = null
	for enemy: Enemy in Data.enemies:
		if enemy.title == enemy_stats:
			e_stats = enemy
	var enemy: EnemyController
	enemy = e_stats.scene.instantiate()
	enemy.name = str(id1) + str(id2)
	enemy.position = pos + global_position
	enemy.stats = e_stats
	enemy.died.connect(enemy_died_callback)
	enemy.reached_goal.connect(enemy_reached_goal_callback)
	enemy.movement_controller.goal = dest
	enemy_path.add_child(enemy)


func spawn_wave() -> void:
	enemies_to_spawn = 0
	enemy_spawn_timers = {}
	for card: EnemyCard in current_wave:
		match(card.rarity):
			Data.Rarity.COMMON:
				enemy_types_to_spawn[card.enemy] += card.enemy.common_group
				enemies_to_spawn += card.enemy.common_group
			Data.Rarity.UNCOMMON:
				enemy_types_to_spawn[card.enemy] += card.enemy.uncommon_group
				enemies_to_spawn += card.enemy.uncommon_group
			Data.Rarity.RARE:
				enemy_types_to_spawn[card.enemy] += card.enemy.rare_group
				enemies_to_spawn += card.enemy.rare_group
			Data.Rarity.EPIC:
				enemy_types_to_spawn[card.enemy] += card.enemy.epic_group
				enemies_to_spawn += card.enemy.epic_group
			Data.Rarity.LEGENDARY:
				enemy_types_to_spawn[card.enemy] += card.enemy.legendary_group
				enemies_to_spawn += card.enemy.legendary_group
		enemy_spawn_timers[card.enemy] = 0.0
		enemies_spawned[card.enemy] = 0
	current_wave = []
	done_spawning = false


func add_card(new_card: EnemyCard) -> void:
	current_wave.append(new_card)
	enemy_types_to_spawn[new_card.enemy] = 0