local w=require(script.Parent.Parent.Parent.Shared.Util)local O={}function O.last(w)return w[#w]end function O.first(w)return w[1]end function O.truthy(w)for w,O in ipairs(w)do if O then return O end end end function O.falsy(w)for w,O in ipairs(w)do if not O then return O end end end function O.add(w)local O=0 for w,T in ipairs(w)do O=O+T end return O end function O.multiply(w)local O=1 for w,T in ipairs(w)do O=O*T end return O end function O.concatArray(O)return w.concat(unpack(O))end function O.collect(w)return w end function O.lowest(w)if#w==0 then return end return math.min(unpack(w))end function O.highest(w)if#w==0 then return end return math.max(unpack(w))end function O.mergeTable(O)return w.assign({},unpack(O))end function O.concatString(w)return function(O)return table.concat(O,w or"")end end function O.priorityValue(w)w=w or O.last return function(O)local T=-math.huge local I={}for w,O in ipairs(O)do if O.priority>T then I={O.value}elseif O.priorty==T then table.insert(I,O.value)end end return w(I)end end function O.structure(w,T)if T==nil then T=O.last end return function(O)local I={}for w,O in ipairs(O)do for w,O in pairs(O)do if I[w]==nil then I[w]={}end table.insert(I[w],O)end end local q={}for O,B in pairs(I)do q[O]=(w[O]or T)(B,I)end return q end end function O.map(w,...)return O.structure({},w,...)end function O.exactly(w)return function()return w end end function O.try(...)local w={...}return function(O)for w,T in ipairs(w)do local I=T(O)if I~=nil then return I end end return nil end end function O.compose(...)local w={...}return function(O)for w,T in ipairs(w)do O=T(O)end return O end end function O.thisOr(w,O)return function(T)local I=w(T)if I==nil then return O else return I end end end local function T(w)return function(T)return O.thisOr(w,T)end end O.truthyOr=T(O.truthy)O.falsyOr=T(O.falsy)O.lastOr=T(O.last)O.default=O.structure({})return O
local Util = require(script.Parent.Parent.Parent.Shared.Util)

local Reducers = {}

function Reducers.last(values)
	return values[#values]
end

function Reducers.first(values)
	return values[1]
end

function Reducers.truthy(values)
	for _, value in ipairs(values) do
		if value then
			return value
		end
	end
end

function Reducers.falsy(values)
	for _, value in ipairs(values) do
		if not value then
			return value
		end
	end
end

function Reducers.add(values)
	local reducedValue = 0

	for _, value in ipairs(values) do
		reducedValue = reducedValue + value
	end

	return reducedValue
end

function Reducers.multiply(values)
	local reducedValue = 1

	for _, value in ipairs(values) do
		reducedValue = reducedValue * value
	end

	return reducedValue
end

function Reducers.concatArray(values)
	return Util.concat(unpack(values))
end

function Reducers.collect(values)
	return values
end

function Reducers.lowest(values)
	if #values == 0 then
		return
	end

	return math.min(unpack(values))
end

function Reducers.highest(values)
	if #values == 0 then
		return
	end

	return math.max(unpack(values))
end

function Reducers.mergeTable(values)
	return Util.assign({}, unpack(values))
end

-- Utilities

function Reducers.concatString(delim)
	return function (values)
		return table.concat(values, delim or "")
	end
end

function Reducers.priorityValue(reducer)
	reducer = reducer or Reducers.last

	return function (values)

		local highestPriority = -math.huge
		local highestPriorityValues = {}

		for _, struct in ipairs(values) do
			if struct.priority > highestPriority then
				highestPriorityValues = {struct.value}
			elseif struct.priorty == highestPriority then
				table.insert(highestPriorityValues, struct.value)
			end
		end

		return reducer(highestPriorityValues)
	end
end

function Reducers.structure(reducers, default)
	if default == nil then
		default = Reducers.last
	end

	return function(values)
		local properties = {}

		for _, value in ipairs(values) do
			for propName, propValue in pairs(value) do
				if properties[propName] == nil then
					properties[propName] = {}
				end

				table.insert(properties[propName], propValue)
			end
		end

		local reducedValue = {}

		for propName, propValues in pairs(properties) do
			reducedValue[propName] =
				(reducers[propName] or default)(propValues, properties)
		end

		return reducedValue
	end
end

-- TODO: structure with unknown fields using one
function Reducers.map(reducer, ...)
	return Reducers.structure({}, reducer, ...)
end

function Reducers.exactly(value)
	return function ()
		return value
	end
end

function Reducers.try(...)
	local reducers = {...}

	return function (values)
		for _, reducer in ipairs(reducers) do
			local result = reducer(values)

			if result ~= nil then
				return result
			end
		end

		return nil
	end
end

function Reducers.compose(...)
	local reducers = {...}

	return function (values)
		for _, reducer in ipairs(reducers) do
			values = reducer(values)
		end

		return values
	end
end

--? Should this be removed in favor of Reducers.try?
function Reducers.thisOr(reducer, defaultValue)
	return function(values)
		local result = reducer(values)

		if result == nil then
			return defaultValue
		else
			return result
		end
	end
end

local function makeOr(func)
	return function (defaultValue)
		return Reducers.thisOr(func, defaultValue)
	end
end

Reducers.truthyOr = makeOr(Reducers.truthy)
Reducers.falsyOr = makeOr(Reducers.falsy)
Reducers.lastOr = makeOr(Reducers.last)
Reducers.default = Reducers.structure({})

return Reducers
