module json_benchmarks;
import std::io;
import std::encoding::json;
import std::collections::object;

struct Address
{
	String street;
	String city;
	int zip;
}

struct Record
{
	int id;
	String name;
	String email;
	bool active;
	double balance;
	int[] scores;
	Address address;
}

Record[] small_records;
Record[] medium_records;
Record[] large_records;

String small_json;
String medium_json;
String large_json;

Object* small_dom;
Object* medium_dom;
Object* large_dom;

fn Record[] make_records(Allocator allocator, sz count)
{
	Record[] records = alloc::new_array(allocator, Record, count);
	for (sz i = 0; i < count; i++)
	{
		int[] scores = alloc::new_array(allocator, int, 5);
		for (sz j = 0; j < 5; j++) scores[j] = (int)((i * 7 + j * 13) % 100);
		records[i] = {
			.id = (int)i,
			.name = string::format(allocator, "user_%d", i),
			.email = string::format(allocator, "user_%d@example.com", i),
			.active = (i & 1) == 0,
			.balance = i * 1.5,
			.scores = scores,
			.address = {
				.street = string::format(allocator, "%d Main Street", i),
				.city = "Springfield",
				.zip = (int)(10000 + i),
			},
		};
	}
	return records;
}

fn void bench_setup() @init
{
	set_benchmark_warmup_iterations(3);
	set_benchmark_max_iterations(100_000);

	set_benchmark_func_iterations($reflect(marshal_small).qname, 100_000);
	set_benchmark_func_iterations($reflect(marshal_medium).qname, 2_400);
	set_benchmark_func_iterations($reflect(marshal_large).qname, 120);

	set_benchmark_func_iterations($reflect(unmarshal_small).qname, 70_000);
	set_benchmark_func_iterations($reflect(unmarshal_medium).qname, 1_400);
	set_benchmark_func_iterations($reflect(unmarshal_large).qname, 65);

	set_benchmark_func_iterations($reflect(parse_dom_small).qname, 45_000);
	set_benchmark_func_iterations($reflect(parse_dom_medium).qname, 900);
	set_benchmark_func_iterations($reflect(parse_dom_large).qname, 25);

	set_benchmark_func_iterations($reflect(unmarshal_dom_small).qname, 120_000);
	set_benchmark_func_iterations($reflect(unmarshal_dom_medium).qname, 3_000);
	set_benchmark_func_iterations($reflect(unmarshal_dom_large).qname, 150);

	set_benchmark_func_iterations($reflect(marshal_dom_small).qname, 80_000);
	set_benchmark_func_iterations($reflect(marshal_dom_medium).qname, 2_000);
	set_benchmark_func_iterations($reflect(marshal_dom_large).qname, 100);

	set_benchmark_func_iterations($reflect(marshal_pretty_medium).qname, 1_800);
	set_benchmark_func_iterations($reflect(marshal_pretty_large).qname, 90);

	set_benchmark_func_iterations($reflect(marshal_stream_medium).qname, 3_000);
	set_benchmark_func_iterations($reflect(marshal_stream_large).qname, 150);

	small_records  = make_records(mem, 1);
	medium_records = make_records(mem, 50);
	large_records  = make_records(mem, 1000);

	small_json  = json::marshal_try(mem, small_records)!!;
	medium_json = json::marshal_try(mem, medium_records)!!;
	large_json  = json::marshal_try(mem, large_records)!!;

	small_dom  = json::parse(mem, small_json)!!;
	medium_dom = json::parse(mem, medium_json)!!;
	large_dom  = json::parse(mem, large_json)!!;
}

module json_benchmarks @benchmark;

import std::encoding::json, std::collections::object;

// 1. Compile-Time Marshaling
fn void marshal_small()   { json::marshal_try(tmem, small_records)!!; }
fn void marshal_medium()  { json::marshal_try(tmem, medium_records)!!; }
fn void marshal_large()   { json::marshal_try(tmem, large_records)!!; }

// 2. Compile-Time Unmarshaling
fn void unmarshal_small()  { json::unmarshal(tmem, small_json, Record[])!!; }
fn void unmarshal_medium() { json::unmarshal(tmem, medium_json, Record[])!!; }
fn void unmarshal_large()  { json::unmarshal(tmem, large_json, Record[])!!; }

// 3. Dynamic DOM Parsing
fn void parse_dom_small()  { json::tparse(small_json)!!; }
fn void parse_dom_medium() { json::tparse(medium_json)!!; }
fn void parse_dom_large()  { json::tparse(large_json)!!; }

// 4. Dynamic DOM-to-Struct mapping
fn void unmarshal_dom_small()
{
	Record[] decoded;
	object::unmarshal_to(tmem, small_dom, &decoded)!!;
}

fn void unmarshal_dom_medium()
{
	Record[] decoded;
	object::unmarshal_to(tmem, medium_dom, &decoded)!!;
}

fn void unmarshal_dom_large()
{
	Record[] decoded;
	object::unmarshal_to(tmem, large_dom, &decoded)!!;
}

// 5. Dynamic DOM Serialization
fn void marshal_dom_small()  { json::marshal_try(tmem, small_dom)!!; }
fn void marshal_dom_medium() { json::marshal_try(tmem, medium_dom)!!; }
fn void marshal_dom_large()  { json::marshal_try(tmem, large_dom)!!; }

// 6. Pretty-Printed Serialization
fn void marshal_pretty_medium() { json::marshal_try(tmem, medium_records, Indent.SPACE_4)!!; }
fn void marshal_pretty_large()  { json::marshal_try(tmem, large_records, Indent.SPACE_4)!!; }

// 7. Stream-Direct Serialization
fn void marshal_stream_medium()
{
	DString s;
	s.tinit();
	json::marshal_to(&s, medium_records)!!;
}

fn void marshal_stream_large()
{
	DString s;
	s.tinit();
	json::marshal_to(&s, large_records)!!;
}