extends Control

var up_left: Vector2
var down_right: Vector2
var up_right: Vector2
var down_left: Vector2
var elapsed_time: float = 0.0

const STAGES_JSON = "res://data/spawn_stages.json"

var stages: Array[SpawnStage] = []

# _state keys: Vector2i(stage_idx, entry_idx)
# values: { "timer": float, "alive": int }
var _state: Dictionary = {}

func _ready() -> void:
	var camera: Camera2D = get_parent().get_node("Camera2D")
	var viewport_size = get_viewport_rect().size
	var world_size = viewport_size / camera.zoom
	var world_origin = camera.global_position

	up_left = world_origin
	down_right = world_origin + world_size
	up_right = Vector2(down_right.x, up_left.y)
	down_left = Vector2(up_left.x, down_right.y)

	_load_stages(STAGES_JSON)

	for si in stages.size():
		for ei in stages[si].entries.size():
			_state[Vector2i(si, ei)] = { "timer": 0.0, "alive": 0, "spawned_total": 0 }

func _load_stages(path: String) -> void:
	var file = FileAccess.open(path, FileAccess.READ)
	if file == null:
		push_error("spawn_control: cannot open " + path)
		return
	var data = JSON.parse_string(file.get_as_text())
	if not data is Array:
		push_error("spawn_control: invalid JSON in " + path)
		return
	for sd in data:
		var stage = SpawnStage.new()
		stage.time_start = float(sd["time_start"])
		stage.time_end = float(sd["time_end"])
		for ed in sd["entries"]:
			var entry = StageEntry.new()
			entry.enemy = load(ed["enemy"])
			entry.count_at_start = int(ed["count_at_start"])
			entry.count_at_end = int(ed["count_at_end"])
			entry.min_interval = float(ed["min_interval"])
			entry.max_spawns = int(ed.get("max_spawns", -1))
			stage.entries.append(entry)
		stages.append(stage)

func get_spawn_position() -> Vector2:
	var side = randi() % 4
	var spawn_x: float
	var spawn_y: float
	if side == 0:
		spawn_x = randf_range(up_left.x, up_right.x)
		spawn_y = up_left.y
	elif side == 1:
		spawn_x = up_right.x
		spawn_y = randf_range(up_right.y, down_right.y)
	elif side == 2:
		spawn_x = randf_range(up_left.x, up_right.x)
		spawn_y = down_left.y
	else:
		spawn_x = up_left.x
		spawn_y = randf_range(up_right.y, down_right.y)
	return Vector2(spawn_x, spawn_y)

func _process(delta: float) -> void:
	elapsed_time += delta

	for si in stages.size():
		var stage: SpawnStage = stages[si]
		if elapsed_time < stage.time_start:
			continue
		if stage.time_end != -1.0 and elapsed_time > stage.time_end:
			continue

		var t: float
		if stage.time_end == -1.0:
			t = 1.0
		else:
			t = clamp(
				(elapsed_time - stage.time_start) / (stage.time_end - stage.time_start),
				0.0, 1.0
			)

		for ei in stage.entries.size():
			var entry: StageEntry = stage.entries[ei]
			var state: Dictionary = _state[Vector2i(si, ei)]

			if entry.max_spawns != -1 and state["spawned_total"] >= entry.max_spawns:
				continue

			var target: int = roundi(lerpf(float(entry.count_at_start), float(entry.count_at_end), t))
			var deficit: int = target - state["alive"]
			if deficit <= 0:
				continue

			state["timer"] -= delta
			if state["timer"] <= 0.0:
				state["timer"] = max(entry.min_interval, 1.0 / float(deficit))
				_spawn_one(entry, state)

func _spawn_one(entry: StageEntry, state: Dictionary) -> void:
	var enemy = entry.enemy.instantiate()
	enemy.global_position = get_spawn_position()
	state["alive"] += 1
	state["spawned_total"] += 1
	enemy.tree_exited.connect(func(): state["alive"] -= 1)
	add_child(enemy)
