extends Control


class Results:
	var render_cpu := 0.0
	var render_gpu := 0.0
	var vtebench := {value = 0.0, variance = 0.0}


var terminal_exit_code := -1


func _ready():
	var timeout := 120
	var benchmark := ""

	var args = OS.get_cmdline_user_args()
	for arg in args:
		if arg.begins_with("--benchmark"):
			benchmark = arg.split("=")[1]

	if benchmark.is_empty():
		_quit_with_error("No benchmark specified")

	RenderingServer.viewport_set_measure_render_time(get_tree().root.get_viewport_rid(), true)

	var results := Results.new()
	var begin_time := Time.get_ticks_usec()
	var frames_captured := 0

	$Terminal.run_benchmark(benchmark)
	await $Terminal.started

	while terminal_exit_code == -1:
		await get_tree().process_frame

		if Time.get_ticks_usec() - begin_time > (timeout * 1e6):
			_quit_with_error("Benchmark took longer than %ss to run" % timeout)

		results.render_cpu += (
			RenderingServer.viewport_get_measured_render_time_cpu(
				get_tree().root.get_viewport_rid()
			)
			+ RenderingServer.get_frame_setup_time_cpu()
		)
		results.render_gpu += RenderingServer.viewport_get_measured_render_time_gpu(
			get_tree().root.get_viewport_rid()
		)

	if terminal_exit_code != 0:
		_quit_with_error("Terminal exited with error code: %d" % terminal_exit_code)

	results.render_cpu /= float(max(1.0, float(frames_captured)))
	results.render_gpu /= float(max(1.0, float(frames_captured)))

	results.vtebench = _process_dat_results("res://benchmark/results/%s.dat" % benchmark)

	var json_results = (
		JSON
		. stringify(
			[
				{
					name = benchmark,
					unit = "milliseconds",
					value = _round(results.vtebench.value),
					range = results.vtebench.range,
				},
				{
					name = "%s - render cpu" % benchmark,
					unit = "milliseconds",
					value = _round(results.render_cpu),
				},
				{
					name = "%s - render gpu" % benchmark,
					unit = "milliseconds",
					value = _round(results.render_gpu),
				}
			],
			"    "
		)
	)

	var file = FileAccess.open("res://benchmark/results/%s.json" % benchmark, FileAccess.WRITE)
	file.store_string(json_results)

	print(json_results)
	get_tree().quit(terminal_exit_code)


func _on_terminal_exited(exit_code: int):
	terminal_exit_code = exit_code


func _round(val: float, sig_figs := 4) -> float:
	return snapped(val, pow(10, floor(log(val) / log(10)) - sig_figs + 1))


func _process_dat_results(path: String) -> Dictionary:
	var file := FileAccess.open(path, FileAccess.READ)
	var samples := []

	file.get_line()  # Skip the first 'header' line.
	while !file.eof_reached():
		var line := file.get_line().strip_edges()
		if line.is_valid_float():
			samples.append(line.to_float())

	if samples.size() < 2:
		_quit_with_error("Not enough samples")

	var avg: float = (samples.reduce(func(acc, n): return acc + n, 0)) / samples.size()

	var std_dev := 0.0
	for sample in samples:
		std_dev += pow(sample - avg, 2)
	std_dev /= (samples.size() - 1)

	return {value = avg, range = "± %.2f" % _round(sqrt(std_dev))}


func _quit_with_error(error_msg: String):
	await get_tree().process_frame
	push_error(error_msg)
	get_tree().quit(1)