--[[
	Licensed according to the included 'LICENSE' document
	Author: Thomas Harning Jr <harningt@gmail.com>
]]
local setmetatable = setmetatable
local assert, loadstring = assert, rawget(_G or {}, "loadstring") or load

local _ENV = nil

-- Key == weak, if main key goes away, then cache cleared
local outputCache = setmetatable({}, {__mode = 'k'})
-- TODO: inner tables weak?

local function buildFunction(nextValues, innerValue, valueWriter, innerWriter)
	local putInner = ""
	if innerValue and innerWriter then
		-- Prepare the lua-string representation of the separator to put in between values
		local formattedInnerValue = ("%q"):format(innerValue)
		-- Fill in the condition %WRITE_INNER% and the %INNER_VALUE% to actually write
		putInner = innerWriter:gsub("%%WRITE_INNER%%", "%%1"):gsub("%%INNER_VALUE%%", formattedInnerValue)
	end
	-- Template-in the value writer (if present) and its conditional argument
	local functionCode = nextValues:gsub("PUTINNER(%b())", putInner)
	-- %VALUE% is to be filled in by the value-to-write
	valueWriter = valueWriter:gsub("%%VALUE%%", "%%1")
	-- Template-in the value writer with its argument
	functionCode = functionCode:gsub("PUTVALUE(%b())", valueWriter)
	functionCode = [[
		return function(composite, ret, encode, state)
	]] .. functionCode .. [[
		end
	]]
	return assert(loadstring(functionCode))()
end

local function prepareEncoder(cacheKey, nextValues, innerValue, valueWriter, innerWriter)
	local cache = outputCache[cacheKey]
	if not cache then
		cache = {}
		outputCache[cacheKey] = cache
	end
	local fun = cache[nextValues]
	if not fun then
		fun = buildFunction(nextValues, innerValue, valueWriter, innerWriter)
		cache[nextValues] = fun
	end
	return fun
end

local output_utility = {
	prepareEncoder = prepareEncoder
}

return output_utility
