local Maze = {}
Maze.__index = Maze

type Maze.@Self = {
	width = number,
	height = number,
	grid = {[0..inf] = number},
}

function Maze:__tostring()
	local out = {}
	table.insert(out, "Maze " .. self.width .. "x" .. self.height .. "\n")

	for y = 0, self.height - 1 do
		for x = 0, self.width - 1 do
			if self:Get(x, y) == 0 then
				table.insert(out, " ")
			else
				table.insert(out, "█")
			end
		end

		table.insert(out, "\n")
	end

	return table.concat(out)
end

function Maze:Get(x: number, y: number)
	return self.grid[y * self.width + x]
end

function Maze:Set(x: number, y: number, v: 1 | 0)
	self.grid[y * self.width + x] = v
end

function Maze:Build(seed: number)
	math.randomseed(seed)

	local function build(x: number, y: number)
		local r = math.random(0, 3)
		self:Set(x, y, 0)

		for i = 0, 3 do
			local d = (i + r) % 4
			local dx = 0
			local dy = 0

			if d == 0 then
				dx = 1
			elseif d == 1 then
				dx = -1
			elseif d == 2 then
				dy = 1
			else
				dy = -1
			end

			local nx = x + dx
			local ny = y + dy
			local nx2 = nx + dx
			local ny2 = ny + dy

			if self:Get(nx, ny) == 1 then
				if self:Get(nx2, ny2) == 1 then
					self:Set(nx, ny, 0)
					build(nx2, ny2)
				end
			end
		end
	end

	build(2, 2)
	self.grid[self.width + 2] = 0
	self.grid[(self.height - 2) * self.width + self.width - 3] = 0
end

local function constructor(_, width: number, height: number)
	local self = setmetatable({
		grid = {} as Maze.@Self.grid, --[[ lie to the typesystem since we're just about to fill the grid with numbers ]]
		width = width, 
		height = height, 
	}, Maze)

	for y = 0, height - 1 do
		for x = 0, width - 1 do
			self.grid[y * width + x] = 1
		end

		self.grid[y * width + 0] = 0
		self.grid[y * width + width - 1] = 0
	end

	for x = 0, width - 1 do
		self.grid[0 * width + x] = 0
		self.grid[(height - 1) * width + x] = 0
	end

	return self
end

setmetatable(Maze, {__call = constructor})
return Maze
