package secs


import "core:runtime"

Entity :: distinct u64

Container :: union($T: typeid) {
	T,
}

World :: struct {
	next_id:    u64,
	components: map[typeid]^runtime.Raw_Dynamic_Array,
}

NewWorld :: proc() -> ^World {
	world := new(World)
	world.next_id = 0
	world.components = make(map[typeid]^runtime.Raw_Dynamic_Array)
	return world
}

CreateEntity :: proc(world: ^World) -> Entity {
	current := world.next_id
	world.next_id += 1
	return Entity(current)
}

@(private)
AddComponent :: proc(world: ^World, entity: Entity, component: $C) {
	typid := typeid_of(C)
	if _, exists := world.components[typid]; !exists {
		world.components[typid] = cast(^runtime.Raw_Dynamic_Array)new([dynamic]Container(C))
	}

	m, _ := world.components[typid]
	assign_at(cast(^[dynamic]Container(C))m, int(entity), component)
}

GetComponent :: proc(world: ^World, entity: Entity, $C: typeid) -> (^C, bool) {
	typid := typeid_of(C)
	if components, exists := world.components[typid]; exists {
		components := cast(^[dynamic]Container(C))components
		if int(entity) < len(components) {
			if c := components[entity]; c == nil {
				return nil, false
			} else {
				return &components[entity].(C), true
			}
		}
	}

	return nil, false
}

@(private)
RemoveComponent :: proc(world: ^World, entity: Entity, $C: typeid) {
	typid := typeid_of(C)
	if components, exists := &world.components[typid]; exists {
		components := cast(^[dynamic]Container(C))components
		assign_at(components, int(entity), nil)
	}
}

RemoveEntity :: proc(world: ^World, entity: Entity) {
	for c in world.components {
		if components, exists := world.components[c]; exists {
			components := cast(^[dynamic]Container(type_of(c)))components
			if int(entity) < len(components) {
				assign_at(components, int(entity), nil)
			}
		}

	}
}