extends Node

# Per-frame profiling node for tactical-shooter.

const WARMUP_FRAMES := 60
const DEFAULT_CAPTURE_FRAMES := 600

var capture_frames := DEFAULT_CAPTURE_FRAMES
var frame_count := 0
var recording: Array[Dictionary] = []
var profiling_complete := false
var last_tick := 0

func _ready():
	# Try to read capture_frames from environment or metadata
	if OS.has_environment("PROFILE_FRAMES"):
		capture_frames = OS.get_environment("PROFILE_FRAMES").to_int()
	
	capture_frames = get_meta("capture_frames", capture_frames)
	
	print("=== Profiler Node ===")
	print("Capture frames: ", capture_frames)
	print("Warmup frames: ", WARMUP_FRAMES)
	print("")
	print("Frame,Delta(ms),GPU(ms),Physics(ms),DrawCalls,Tris,Vertices,VRAM(MB),RAM(MB),Objects")
	
	# Load the target scene from metadata or environment
	var target_scene = get_meta("target_scene", "")
	if target_scene.is_empty() and OS.has_environment("PROFILE_SCENE"):
		target_scene = OS.get_environment("PROFILE_SCENE")
	
	if not target_scene.is_empty():
		var packed = ResourceLoader.load(target_scene)
		if packed:
			var instance = packed.instantiate()
			add_child(instance)
			print("Loaded target scene: ", target_scene)
		else:
			printerr("Failed to load target scene: ", target_scene)
	
	last_tick = Time.get_ticks_usec()
	
	print("Ready complete, process() should fire now")
	set_process(true)

func _process(delta: float) -> void:
	print("_process called, frame=", frame_count)
	if profiling_complete:
		return
	
	frame_count += 1
	
	# Calculate real delta
	var now := Time.get_ticks_usec()
	var real_delta := (now - last_tick) / 1000000.0
	last_tick = now
	
	# Skip warmup frames
	if frame_count <= WARMUP_FRAMES:
		return
	
	# Check if done
	if frame_count > WARMUP_FRAMES + capture_frames:
		profiling_complete = true
		_finish()
		return
	
	var p := Performance
	
	var frame_delta := real_delta * 1000.0
	var gpu_time := float(RenderingServer.get_frame_setup_time_cpu()) * 1000.0
	var physics_time := p.get_monitor(p.TIME_PHYSICS_PROCESS) * 1000.0
	var draw_calls := int(p.get_monitor(p.RENDER_TOTAL_DRAW_CALLS_IN_FRAME))
	var tris := int(p.get_monitor(p.RENDER_TOTAL_PRIMITIVES_IN_FRAME))
	var verts := int(p.get_monitor(p.RENDER_TOTAL_VERTICES_IN_FRAME))
	var vram_mb := int(p.get_monitor(p.RENDER_VIDEO_MEM_USED)) / (1024 * 1024)
	var ram_mb := int(OS.get_static_memory_usage()) / (1024 * 1024)
	var objects := int(p.get_monitor(p.OBJECT_NODE_COUNT))
	
	recording.append({
		frame = frame_count - WARMUP_FRAMES,
		delta_ms = snapped(frame_delta, 0.01),
		gpu_ms = snapped(gpu_time, 0.01),
		physics_ms = snapped(physics_time, 0.01),
		draw_calls = draw_calls,
		tris = tris,
		verts = verts,
		vram_mb = vram_mb,
		ram_mb = ram_mb,
		objects = objects
	})

	print("%d,%.2f,%.2f,%.2f,%d,%d,%d,%d,%d,%d" % [
		frame_count - WARMUP_FRAMES,
		frame_delta, gpu_time, physics_time,
		draw_calls, tris, verts, vram_mb, ram_mb, objects
	])

func _finish() -> void:
	print("")
	print("=== Profile Summary ===")
	if recording.is_empty():
		print("No frames recorded!")
		get_tree().quit(1)
		return
	
	var deltas: Array[float] = []
	for r in recording:
		deltas.append(r.delta_ms)
	deltas.sort()
	
	var total := float(recording.size())
	var n := deltas.size()
	var p50: float = deltas[n * 50 / 100]
	var p95: float = deltas[n * 95 / 100]
	var p99: float = deltas[n * 99 / 100]
	var sum: float = 0.0
	for d in deltas:
		sum += d
	var avg: float = sum / total
	
	var max_dc := 0
	var max_tris := 0
	var max_vram := 0
	for r in recording:
		if r.draw_calls > max_dc: max_dc = r.draw_calls
		if r.tris > max_tris: max_tris = r.tris
		if r.vram_mb > max_vram: max_vram = r.vram_mb
	
	print("Frames captured: ", recording.size())
	print("Avg frame time:  %.2f ms" % avg)
	print("P50 frame time:  %.2f ms" % p50)
	print("P95 frame time:  %.2f ms" % p95)
	print("P99 frame time:  %.2f ms" % p99)
	print("Max draw calls:  %d" % max_dc)
	print("Max triangles:   %d" % max_tris)
	print("Peak VRAM:       %d MB" % max_vram)
	
	var pass_fail := "PASS"
	if p95 > 16.6:
		pass_fail = "FAIL (P95 > 16.6 ms)"
	elif p99 > 50.0:
		pass_fail = "FAIL (P99 spike > 50 ms)"
	print("Result: ", pass_fail)
	print("")
	print("=== Profile complete ===")
	get_tree().quit(0 if pass_fail == "PASS" else 1)
