const builtin = @import("builtin");

const heap = @import("./heap.zig");

const map = @import("./map.zig");

const resource = @import("./resource.zig");

const std = @import("std");

const stack = @import("./stack.zig");

const system = @import("./system.zig");

thread_pool: ?*std.Thread.Pool = null,
thread_restricted_resources: [std.enums.values(ThreadRestriction).len]resource.Table,
event_systems: stack.Sequential(system.Schedule),

pub const Event = enum (usize) { _ };

const Self = @This();

pub const ThreadRestriction = enum {
	none,
	main,
};

pub fn create_event(self: *Self, label: []const u8) std.mem.Allocator.Error!Event {
	var systems = try system.Schedule.init(label);

	errdefer systems.deinit();

	const index = self.event_systems.len();

	try self.event_systems.push_grow(systems);

	return @enumFromInt(index);
}

pub fn deinit(self: *Self) void {
	for (&self.thread_restricted_resources) |*resources| {
		resources.deinit();
	}

	for (self.event_systems.values) |*schedule| {
		schedule.deinit();
	}

	if (self.thread_pool) |thread_pool| {
		thread_pool.deinit();
		heap.allocator.destroy(thread_pool);
	}

	self.event_systems.deinit();

	self.* = undefined;
}

pub fn get_resource(self: Self, thread_restriction: ThreadRestriction, comptime Resource: type) ?*Resource {
	return @ptrCast(@alignCast(self.thread_restricted_resources[@intFromEnum(thread_restriction)].get(Resource)));
}

pub fn set_get_resource(self: *Self, thread_restriction: ThreadRestriction, value: anytype) std.mem.Allocator.Error!*@TypeOf(value) {
	return self.thread_restricted_resources[@intFromEnum(thread_restriction)].set_get(value);
}

pub fn init(thread_count: u32) std.Thread.SpawnError!Self {
	var world = Self{
		.thread_restricted_resources = .{resource.Table.init(), resource.Table.init()},
		.event_systems = .{.allocator = heap.allocator},
	};

	if (thread_count != 0 and !builtin.single_threaded) {
		const thread_pool = try heap.allocator.create(std.Thread.Pool);

		try thread_pool.init(.{
			.allocator = heap.allocator,
			.n_jobs = thread_count,
		});

		world.thread_pool = thread_pool;
	}

	return world;
}

pub fn on_event(self: *Self, event: Event, action: *const system.Info, order: system.Order) std.mem.Allocator.Error!void {
	try self.event_systems.values[@intFromEnum(event)].then(self, action, order);
}

pub fn run_event(self: *Self, event: Event) anyerror!void {
	try self.event_systems.values[@intFromEnum(event)].run(self);
}

pub fn set_resource(self: *Self, thread_restriction: ThreadRestriction, value: anytype) std.mem.Allocator.Error!void {
	try self.thread_restricted_resources[@intFromEnum(thread_restriction)].set(value);
}