local c = {
	white_underline = function(s: any)
		return `\27[1;4m{s}\27[0m`
	end,

	white = function(s: any)
		return `\27[37;1m{s}\27[0m`
	end,

	green = function(s: any)
		return `\27[32;1m{s}\27[0m`
	end,

	red = function(s: any)
		return `\27[31;1m{s}\27[0m`
	end,

	yellow = function(s: any)
		return `\27[33;1m{s}\27[0m`
	end,

	red_highlight = function(s: any)
		return `\27[41;1;30m{s}\27[0m`
	end,

	green_highlight = function(s: any)
		return `\27[42;1;30m{s}\27[0m`
	end,

	gray = function(s: any)
		return `\27[30;1m{s}\27[0m`
	end,
}


local ECS_PAIR_FLAG =                      0x8
local ECS_ID_FLAGS_MASK =                 0x10
local ECS_ENTITY_MASK =     bit32.lshift(1, 24)
local ECS_GENERATION_MASK = bit32.lshift(1, 16)

type i53 = number
type i24 = number

local function ECS_ENTITY_T_LO(e: i53): i24
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
end

local function ECS_GENERATION(e: i53): i24
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
end

local ECS_ID = ECS_ENTITY_T_LO

local function ECS_COMBINE(source: number, target: number): i53
	return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
end

local function ECS_GENERATION_INC(e: i53)
	if e > ECS_ENTITY_MASK then
		local flags = e // ECS_ID_FLAGS_MASK
		local id = flags // ECS_ENTITY_MASK
		local generation = flags % ECS_GENERATION_MASK

		local next_gen = generation + 1
		if next_gen > ECS_GENERATION_MASK then
			return id
		end

		return ECS_COMBINE(id, next_gen) + flags
	end
	return ECS_COMBINE(e, 1)
end

local function bl()
	print("")
end

local function pe(e)
	local gen = ECS_GENERATION(e)
	return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
end

local function dprint(tbl: {  [number]: number })
	bl()
	print("--------")
	for i, e in tbl do
		print("| "..pe(e).." |")
		print("--------")
	end
	bl()
end

local max_id = 0
local alive_count = 0
local dense = {}
local sparse = {}
local function alloc()
	if alive_count ~= #dense then
		alive_count += 1
		print("*recycled", pe(dense[alive_count]))
		return dense[alive_count]
	end
	max_id += 1
	local id = max_id
	alive_count += 1
	dense[alive_count] = id
	sparse[id] = {
		dense = alive_count
	}
	print("*allocated", pe(id))
	return id
end

local function remove(entity)
	local id = ECS_ID(entity)
	local r = sparse[id]
	local index_of_deleted_entity = r.dense
	local last_entity_alive_at_index = alive_count -- last entity alive
	alive_count -= 1
	local last_alive_entity = dense[last_entity_alive_at_index]
	local r_swap = sparse[ECS_ID(last_alive_entity)]
	r_swap.dense = r.dense
	r.dense = last_entity_alive_at_index
	dense[index_of_deleted_entity] = last_alive_entity
	dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
	print("*dellocated", pe(id))
end

local function alive(e)
	local r = sparse[ECS_ID(e)]

	return dense[r.dense] == e
end

local function pa(e)
	print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
end

local tprint = require("@testkit").print
local e1v0 = alloc()
local e2v0 = alloc()
local e3v0 = alloc()
local e4v0 = alloc()
local e5v0 = alloc()
pa(e1v0)
pa(e4v0)
remove(e5v0)
pa(e5v0)

local e5v1 = alloc()
pa(e5v0)
pa(e5v1)
pa(e2v0)
print(ECS_ID(e2v0))

dprint(dense)
remove(e2v0)
dprint(dense)
