-- Categorizes content and display them.
-- Improvements are welcomed.

-- requires

-- forward declarations
local index;
local build_keys;
local build_values;
local as_key_content_function;
local as_value_content_function;
local as_fold_function;
local escape_replacement;
local unstrip_and_strip_nowiki_tags;

local d = {}

-- exposed: categorizes content and display them
-- 'folder', 'table_*', 'content'
index = function(frame)
	local args = build_args(frame)
	
	local folder = as_fold_function(args.folder)
	local keys = build_keys(args)
	local values = build_values(args.content)
	
	local result = ""
	for category, key in pairs(keys) do
		local value = values[category]
		if value == nil then
			value = function(category)
				return ""
			end
		end
		result = folder(result,
			key(
				value(category)
			)
		)
	end
	
	return mw.text.nowiki(mw.dumpObject({args = args, keys = keys, values = values, result = result, d = d}))
end

-- builds arguments
-- reads parent frame
build_args = function(frame)
	local result = {}
	
	local pframe = frame:getParent()
	for key, value in pairs(frame.args) do
		result[mw.text.trim(key)] = mw.text.trim(unstrip_and_strip_nowiki_tags(value))
	end
	for key, value in pairs(pframe.args) do
		result[mw.text.trim(key)] = mw.text.trim(unstrip_and_strip_nowiki_tags(value))
	end
	
	return result
end

-- builds key mapping
-- reads 'table_*'
build_keys = function(args)
	local result = {}
	for arg_k, arg_v in pairs(args) do
		local arg_k_trim = mw.text.trim(arg_k)
		if arg_k_trim:match("^table_") then
			local key = arg_k_trim:gsub("table_", "", 1)
			result[key] = as_key_content_function(arg_v)
		end
	end
	return result
end

-- builds values mapping
-- magic words: '__M_INDEX__'
-- syntax: '__M_INDEX__ [category1] [category2] ... [categoryN]'
build_values = function(text)
	-- need cleanup
	local result = {}
	local context = {}
	local content = ""
	
	for line_number, line in ipairs(mw.text.split(text, "\n", true)) do -- is \n sufficient
		if line:match("^__M_INDEX__") then
			-- finish result
			for index, category in ipairs(context) do
				local sub_result = result[category]
				if sub_result == nil then
 					result[category] = content
 				else
 					result[category] = sub_result .. content
 				end
			end
			content = ""
			
			-- change context
			context = {}
			for category in line:gmatch("%[([^%]]*)%]") do
				table.insert(context, category)
			end
		else
			-- append content
			content = content .. line .. "\n"
		end
	end
	
	-- one more round of finishing result
	for index, category in ipairs(context) do
		local sub_result = result[category]
		if sub_result == nil then
 			result[category] = content
 		else
 			result[category] = sub_result .. content
 		end
	end
	
	-- turn into content function
	for key, value in pairs(result) do
		result[key] = as_value_content_function(value)
	end
	
	return result
end

-- converts text to key content function
-- magic words: '__M_CONTENT__'
as_key_content_function = function(text)
	local function func(content)
		table.insert(d, {concatype = 'key', text = text, content = content, result = text:gsub("__M_CONTENT__", escape_replacement(content))})
		return text:gsub("__M_CONTENT__", escape_replacement(content))
	end
	return func
end

-- converts text to value content function
-- magic words: '__M_CATEGORY__'
as_value_content_function = function(text)
	local function func(category)
		table.insert(d, {concatype = 'value', text = text, category = category, result = text:gsub("__M_CATEGORY__", escape_replacement(category))})
		return text:gsub("__M_CATEGORY__", escape_replacement(category))
	end
	return func
end

-- converts text to folding function
-- magic words: '__M_LEFT__', '__M_RIGHT__'
as_fold_function = function(text)
	local function func(left, right)
		-- some improvements could be made here
		table.insert(d, {concatype = 'fold', text = text, left = left, right = right, result = text:gsub("__M_LEFT__", escape_replacement(left)):gsub("__M_RIGHT__", escape_replacement(right))})
		return text:gsub("__M_LEFT__", escape_replacement(left)):gsub("__M_RIGHT__", escape_replacement(right))
	end
	return func
end

-- escapes replacement, replace '%' with '%%'
escape_replacement = function(text)
	return text:gsub("%%", "%%%%")
end

unstrip_and_strip_nowiki_tags = function(text)
	return mw.text.unstripNoWiki(text):gsub("<nowiki>", ""):gsub("</nowiki>", "")
end

return {index = index}