-----------------------------------------------------------------------------------------------------------
-- API:

function API:GetDataStore(name, scope)
	assert(type(name) == "string", "DataStore name must be a string; got" .. type(name))
	assert(type(scope) == "string" or scope == nil, "DataStore scope must be a string; got" .. type(scope))
	scope = (scope or "global")
	if (allStores[scope] and allStores[scope][name]) then
		return allStores[scope][name]
	end
	local data = {}
	local d = {}
	local updateListeners = {}
	function d:SetAsync(k, v)
		assert(v ~= nil, "Value cannot be nil")
		data[k] = v
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(v) end)
			end
		end
	end
	function d:UpdateAsync(k, func)
		local v = func(data[k])
		assert(v ~= nil, "Value cannot be nil")
		data[k] = v
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(v) end)
			end
		end
	end
	function d:GetAsync(k)
		return data[k]
	end
	function d:RemoveAsync(k)
		data[k] = nil
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(nil) end)
			end
		end
	end
	function d:IncrementAsync(k, delta)
		if (delta == nil) then delta = 1 end
		assert(type(delta) == "number", "Can only increment numbers")
		self:UpdateAsync(k, function(num)
			if (num == nil) then
				return num
			end
			assert(type(num) == "number", "Can only increment numbers")
			return (num + delta)
		end)
	end
	function d:OnUpdate(k, onUpdateFunc)
		assert(type(onUpdateFunc) == "function", "Update function argument must be a function")
		if (not updateListeners[k]) then
			updateListeners[k] = {onUpdateFunc}
		else
			table.insert(updateListeners[k], onUpdateFunc)
		end
	end
	if (not allStores[scope]) then
		allStores[scope] = {}
	end
	allStores[scope][name] = d
	return d
end


function API:GetGlobalDataStore()
	return self:GetDataStore("global", "global")
end


function API:GetOrderedDataStore(name, scope)
	local dataStore = self:GetDataStore(name, scope)
	local allData = {}
	local d = {}
	function d:GetAsync(k)
		return dataStore:GetAsync(k)
	end
	function d:SetAsync(k, v)
		assert(type(v) == "number", "Value must be a number")
		dataStore:SetAsync(k, v)
		allData[k] = v
	end
	function d:UpdateAsync(k, func)
		dataStore:UpdateAsync(k, function(oldValue)
			local v = func(oldValue)
			assert(type(v) == "number", "Value must be a number")
			allData[k] = v
			return v
		end)
	end
	function d:IncrementAsync(k, delta)
		dataStore:IncrementAsync(k, delta)
		allData[k] = ((allData[k] or 0) + delta)
	end
	function d:RemoveAsync(k)
		dataStore:RemoveAsync(k)
		allData[k] = nil
	end
	function d:GetSortedAsync(isAscending, pageSize, minValue, maxValue)
		assert(type(pageSize) == "number" and math.floor(pageSize) > 0, "PageSize must be an integer and greater than 0")
		assert(minValue == nil or type(minValue) == "number", "MinValue must be a number")
		assert(maxValue == nil or type(maxValue) == "number", "MaxValue must be a number")
		if (minValue and maxValue) then
			assert(minValue <= maxValue, "MinValue must be less or equal to MaxValue")
		end
		local data = {}
		for k,v in pairs(allData) do
			local pass = ((not minValue or v >= minValue) and (not maxValue or v <= maxValue))
			if (pass) then
				table.insert(data, {key = k, value = v})
			end
		end
		table.sort(data,
			(isAscending and
				function(a, b) return (a.value < b.value) end
			or
				function(a, b) return (b.value < a.value) end
			)
		)
		pageSize = math.floor(pageSize)
		local pages = {IsFinished = false}
		for i,v in pairs(data) do
			local pageNum = math.ceil(i / pageSize)
			local page = pages[pageNum]
			if (not page) then
				page = {}
				pages[pageNum] = page
			end
			local index = (((i - 1) % pageSize) + 1)
			page[index] = v
		end
		do
			local currentPage = 1
			function pages:GetCurrentPage()
				return self[currentPage]
			end
			function pages:AdvanceToNextPageAsync()
				local numPages = #pages
				if (currentPage < numPages) then
					currentPage = (currentPage + 1)
				end
				self.IsFinished = (currentPage >= numPages)
			end
		end
		return pages
	end
	return d
end


function API:GetRequestBudgetForRequestType(requestType)
	return realDataStoreService:GetRequestBudgetForRequestType(requestType)
end