local _, addon = ...

-- Creates a fixed size FIFO list that replaces the oldest entries with new ones when the list fills.
-- So it's actually more like a queue, but I didn't feel like renaming the object or the methods, lol

local Stack = {}
addon.Stack = Stack

local type, error = type, error
local wipe, tremove, tinsert = wipe, tremove, tinsert

-- Table cache
local tableHeap = {}

-- Initializes and returns a new list
function Stack.new(n)
	local t = tremove(tableHeap) or {}
	t._first = 0
	t._size = n or 0
	return t
end

function Stack.release(t)
	wipe(t)
	tinsert(tableHeap, t)
end

function Stack.resize(t, n)
	if n > t._size then
		-- grow
		while (t._size < n) do
			local pos = t._first - 1
			if pos > 0 then
				table.insert(t, pos, "")
				t._first = t._first + 1
			end
			if t._last and t._last > pos then
				t._last = t._last + 1
			end
			t._size = t._size + 1
		end
	else
		-- shrink
		while (t._size > n) do
			local pos = t._first - 1
			if pos < 1 then pos = t._size end
			table.remove(t, pos)
			if t._last and t._last > pos then
				t._last = t._last + 1
			end
			t._size = t._size - 1
		end
	end
end

-- Clears the elements of the list
function Stack.wipe(t)
	local n = t._size
	wipe(t)
	t._first = 0
	t._size = n
	return t
end

-- Returns if the list is empty
function Stack.isEmpty(t)
	return not t._last or t[t._first] == nil
end

-- Returns the length of the list
function Stack.len(t)
	if Stack.isEmpty(t) then return 0 end

	local len = 1 + t._last - t._first

	if t._last >= t._first then
		return len
	end
	return len + t._size
end

-- Returns the first element
function Stack.first(t)
	return not Stack.isEmpty(t) and t[t._first]
end

-- Returns the last element
function Stack.last(t)
	return not Stack.isEmpty(t) and t[t._last]
end

-- Add an element to the back of the list
function Stack.push(t, v)
	local last = (t._last or -1) + 1
	if last == t._size then
		last = 0
	end
	if t._last and t._first == last then
		t._first = t._first + 1
		if t._first == t._size then
			t._first = 0
		end
	end
	t._last = last
	t[last] = v
end

-- Remove and return an element from the front of the list
function Stack.pop(t)
	if Stack.isEmpty(t) then return end

	local v = t[t._first]
	t[t._first] = nil
	t._first = t._first + 1
	if t._first == t._size then
		t._first = 0
	end

	return v
end

-- List iterator
local function next(t)
	if Stack.isEmpty(t) or not t._iterator or t[t._iterator] == nil then
		return nil
	end

	local v = t[t._iterator]
	if t._iterator == t._last then
		t._iterator = nil
	else
		t._iterator = t._iterator + 1
		if t._iterator == t._size then
			t._iterator = 0
		end
	end

	return v
end

function Stack.next(t)
	if type(t) ~= "table" then error("bad argument to \"Stack.next\" (table expected, got "..type(t)..")", 2) end
	t._iterator = t._first
	return next, t, nil
end
