local jecs = require("@jecs")
local testkit = require("@testkit")

local BENCH, START = testkit.benchmark()

local function TITLE(s: string)
	print()
	print(testkit.color.white(s))
end

local N = 2 ^ 17

local pair = jecs.pair

do
	TITLE("create")
	local world = jecs.World.new()

	BENCH("entity", function()
		for i = 1, START(N) do
			world:entity()
		end
	end)

	local A = world:component()
	local B = world:component()

	BENCH("pair", function()
		for i = 1, START(N) do
			jecs.pair(A, B)
		end
	end)
end

do
	TITLE("set")

	local world = jecs.World.new()
	local A = world:component()

	local entities = table.create(N)

	for i = 1, N do
		entities[i] = world:entity()
	end

	BENCH("add", function()
		for i = 1, START(N) do
			world:set(entities[i], A, 1)
		end
	end)

	BENCH("set", function()
		for i = 1, START(N) do
			world:set(entities[i], A, 2)
		end
	end)

	BENCH("remove", function()
		for i = 1, START(N) do
			world:remove(entities[i], A)
		end
	end)
end

-- we have a separate benchmark for relationships.
-- this is due to that relationships have a very high id compared to normal
-- components, which cause them to get added into the hashmap portion.
do
	TITLE("set relationship")

	local world = jecs.World.new()
	local A = world:component()

	local entities = table.create(N)

	for i = 1, N do
		entities[i] = world:entity()
		world:set(entities[i], A, 1)
	end

	local pair = jecs.pair(A, world:entity())

	BENCH("add", function()
		for i = 1, START(N) do
			world:set(entities[i], pair, 1)
		end
	end)

	BENCH("set", function()
		for i = 1, START(N) do
			world:set(entities[i], pair, 2)
		end
	end)

	BENCH("remove", function()
		for i = 1, START(N) do
			world:remove(entities[i], pair)
		end
	end)
end

do
	TITLE("get")

	local world = jecs.World.new()
	local A = world:component()
	local B = world:component()
	local C = world:component()
	local D = world:component()
	local entities = table.create(N)

	for i = 1, N do
		entities[i] = world:entity()
		world:set(entities[i], A, 1)
		world:set(entities[i], B, 1)
		world:set(entities[i], C, 1)
		world:set(entities[i], D, 1)
	end

	BENCH("1", function()
		for i = 1, START(N) do
			world:get(entities[i], A)
		end
	end)

	BENCH("2", function()
		for i = 1, START(N) do
			world:get(entities[i], A, B)
		end
	end)

	BENCH("3", function()
		for i = 1, START(N) do
			world:get(entities[i], A, B, C)
		end
	end)

	BENCH("4", function()
		for i = 1, START(N) do
			world:get(entities[i], A, B, C, D)
		end
	end)
end

do
	TITLE("target")

	BENCH("1st target", function()
		local world = jecs.World.new()
		local A = world:component()
		local B = world:component()
		local C = world:component()
		local D = world:component()
		local entities = table.create(N)

		for i = 1, N do
			local ent = world:entity()
			entities[i] = ent

			world:set(ent, pair(A, A))
			world:set(ent, pair(A, B))
			world:set(ent, pair(A, C))
			world:set(ent, pair(A, D))
		end

		for i = 1, START(N) do
			world:target(entities[i], A, 0)
		end
	end)
end

--- this benchmark is used to view how fragmentation affects query performance
--- we use this by determining how many entities should fit per arcehtype, instead
--- of creating x amount of archetypes. this would scale better with any amount of
--- entities.
do
	TITLE(`query {N} entities`)

	local function view_bench(n: number)
		BENCH(`{n} entities per archetype`, function()
			local world = jecs.World.new()

			local A = world:component()
			local B = world:component()
			local C = world:component()
			local D = world:component()

			for i = 1, N, n do
				local ct = world:entity()
				for j = 1, n do
					local id = world:entity()
					world:set(id, A, true)
					world:set(id, B, true)
					world:set(id, C, true)
					world:set(id, D, true)
					world:add(id, ct)
				end
			end

			local q = world:query(A, B, C, D)
			START()
			for id in q do
			end
		end)

		BENCH(`inlined query`, function()
			local world = jecs.World.new()
			local A = world:component()
			local B = world:component()
			local C = world:component()
			local D = world:component()

			for i = 1, N, n do
				local ct = world:entity()
				for j = 1, n do
					local id = world:entity()
					world:set(id, A, true)
					world:set(id, B, true)
					world:set(id, C, true)
					world:set(id, D, true)
					world:add(id, ct)
				end
			end

			local archetypes = world:query(A, B, C, D):archetypes()
			START()
			for _, archetype in archetypes do
				local columns, records = archetype.columns, archetype.records
				local a = columns[records[A]]
				local b = columns[records[B]]
				local c = columns[records[C]]
				local d = columns[records[D]]
				for row in archetype.entities do
					local _1, _2, _3, _4 = a[row], b[row], c[row], d[row]
				end
			end
		end)
	end

	for i = 13, 0, -1 do
		view_bench(2 ^ i)
	end
end
