Modulo:Sandbox/The Polish

local m = {}

function m.Mow(frame)
	local current = mw.title.getCurrentTitle()
	local pf = frame:getParent()
	mw.log(current.rootText)
	mw.log(current.baseText)
	mw.log(current.subpageText)
	mw.log(current.text)
	if not mw.isSubsting() then
		if current.namespace ~= 10 then
			mw.log("Brak 'subst:' w wywołaniu szablonu");
			return "[[Kategoria:Błąd wywołania]]"
		end
	end

	local ip = pf.args.ip
	if ip == "tak" then
		ip = true
	elseif ip == "nie" then
		ip = false
	elseif ip then
		mw.log("Nieprawidłowy parametr 'ip': "..ip);
		return "[[Kategoria:Błąd wywołania]]"
	elseif (current.namespace == 3) and (frame.args.ip == "auto") then
		local rootText = current.rootText
		ip = mw.ustring.match(rootText, "^[1-9]%d?%d?%.[1-9]%d?%d?%.[1-9]%d?%d?%.[1-9]%d?%d?$")
			or  mw.ustring.match(rootText, "^%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?:%x%x?%x?%x?$")
	else
		ip = false
	end
	
	local base = frame.args.base
	if not base then
		mw.log("Oczekiwany parameter {{{base}}}");
		return "[[Kategoria:Błąd wywołania]]"
	end
	base = mw.text.trim(base)
	if #base == 0 then
		mw.log("Pusty parameter {{{base}}}");
		return "[[Kategoria:Błąd wywołania]]"
	end
	
	local message = pf.args[1] or frame.args.default
	if not message then
		mw.log("Oczekiwany parameter '{{{1}}}' z nazwą komunikatu");
		return "[[Kategoria:Błąd wywołania]]"
	end
	
	message = mw.text.trim(message)
	if #message == 0 then
		mw.log("Pusty parametr '{{{1}}}' z nazwą komunikatu");
		return "[[Kategoria:Błąd wywołania]]"
	end
	
	local messageTemplate = mw.title.makeTitle(10, base..message)
	if not messageTemplate.exists then
		mw.log("Nie istnieje szablon komunikatu '"..base..message.."'");
		return "[[Kategoria:Błąd wywołania]]"
	end
	
	message = mw.text.trim(mw.ustring.gsub(messageTemplate:getContent(), "<noinclude>.-</noinclude>", ""))
	if #message == 0 then
		mw.log("Pusty szablon komunikatu '"..base..message.."'");
		return "[[Kategoria:Błąd wywołania]]"
	end
	
	local custom = pf.args[2]
	if custom then
		custom = mw.text.trim(custom)
		if #custom == 0 then
			custom = " ~~~~"
		else
			custom = "\n\n"..custom.." ~~~~"
		end
	else
		custom = " ~~~~"
	end
	

	local result = {}
	if ip then
		mw.log("IP message")
		table.insert(result, frame:expandTemplate{ title = "tmbox", args = { typ = "zawartość", tekst = "Jeżeli nie edytowałeś jeszcze Wikipedii, zignoruj poniższy komunikat." } })
	end
	
	table.insert(result, message)
	table.insert(result, custom)

	return table.concat(result, "")
end

function m.szablon(frame)
	local result = {}
	local s = frame.args.szablon or nil
	if s then
		local lines = mw.text.split(s, '\n')
		for i, v in ipairs(lines) do
			local t = mw.text.trim(v)
			if #t > 0 then
				table.insert(result, t)
			end
		end
	end

	return table.concat(result, ", ")
end

function m.frexp(frame)
	local m, e = math.frexp(frame.args[1])
	return tostring(m or "nil")..", "..tostring(e or "nil")
end

function m.numtotext(frame)
	local textrounds = {
		{ min = 1000000000, div = 1000000000, suffix = " mld", },
		{ min = 1000000, div = 1000000, suffix = " mln", },
		{ min = 2000, div = 1000, suffix = " tys", },
	}
	
	local value = tonumber(frame.args[1])
	if (value) then
		local suffix = false
		for _, v in ipairs(textrounds) do
			if (value >= v.min) then
				value = value / v.div
				suffix = v.suffix
				break
			end
		end
	
		value = tonumber(string.format("%.2f", value)) -- rounding
		return suffix and mw.getContentLanguage():formatNum(value)..suffix  or tostring(value)
	end
end

function m.SimpleDuplicates(frame)
	local result = {}
	local wikitext = mw.title.getCurrentTitle():getContent()
	
	while true do
		local templateIterator = mw.ustring.gmatch(wikitext, "{{[^{}]+}}")
		while true do
			local template = templateIterator()
			if not template then
				-- no more templates
				break
			end
	
			mw.log(template)
			local parameters = {}
			local patch, _ = mw.ustring.gsub(template, "(%[%[[^%[%]|]+)|([^%[%]|]-%]%])", "%1<nowiki>&x7C;</nowiki>%2")
			local chunks = mw.text.split(patch, "|")
			local i = 2 -- skip first item which is template name
			local auto = 0
			local emited = false
			while i <= #chunks do
				local chunk = chunks[i]
				local name, value = mw.ustring.match(chunk, "%s*(.-)%s*=(.*)")
				if not name then
					auto = auto + 1
					name = tostring(auto)
					value = chunk
				end
				
				value = mw.ustring.gsub(value, "<nowiki>&x7C;</nowiki>", "|")
				
				if not parameters[name] then
					parameters[name] = { value }
				else
					-- duplicated parameter
					if not emited then
						emited = {}
						table.insert(emited, "<kbd>"..mw.text.nowiki(template).."</kbd>")
					end
					
					if #parameters[name] == 1 then
						table.insert(emited, name..": "..mw.text.nowiki(parameters[name][1]))
					end
						
					table.insert(parameters[name], value)
					table.insert(emited, name..": "..mw.text.nowiki(value))
				end

				i = i + 1
			end
			
			if emited then
				table.insert(result, table.concat(emited, "<br /> → "))
			end
		end
	
		local count = false
		wikitext, count = mw.ustring.gsub(wikitext, "{{[^{}]+}}", "€")
		if count == 0 then
			break
		end
	end

	return "<ul><li>"..table.concat(result, "</li><li>").."</li></ul>"
end

local function stripText(text, stripTemplates)
	text, _ = mw.ustring.gsub(text, "<[Dd][Ii][Vv]>", "\n") -- remove <div>
	text, _ = mw.ustring.gsub(text, "<[Dd][Ii][Vv]%s.->", "\n") -- remove <div>
	text, _ = mw.ustring.gsub(text, "</[Dd][Ii][Vv]>", "\n") -- remove </div>
	text, _ = mw.ustring.gsub(text, "<[Ss][Pp][Aa][Nn]>", "") -- remove <span>
	text, _ = mw.ustring.gsub(text, "<[Ss][Pp][Aa][Nn]%s.->", "") -- remove <span>
	text, _ = mw.ustring.gsub(text, "</[Ss][Pp][Aa][Nn]>", "") -- remove </span>
	text, _ = mw.ustring.gsub(text, "<[Bb][Rr]%s*/?>", " ") -- remove <br/>

	text, _ = mw.ustring.gsub(text, "{|[^\n]-\n", "\n{|\n") -- remove table attributes
	text, _ = mw.ustring.gsub(text, "\n|%-[^\n]*\n", "\n|-\n") -- remove table raw attributes
	text, _ = mw.ustring.gsub(text, "\n|[^\n|]-|([^|])", "\n| |%1") -- remove table cell attributes
	if stripTemplates then
		text, _ = mw.ustring.gsub(text, "{{[^|}]*([|}])", "{{%1") -- remove template name
		text, _ = mw.ustring.gsub(text, "|[^|=]*=", "|") -- remove template field names
	end

	text, _ = mw.ustring.gsub(text, "%[%[[Pp]lik:[^\n]-%]%]", "") -- remove files
	text, _ = mw.ustring.gsub(text, "%[%[[Ff]ile:[^\n]-%]%]", "") -- remove files
	text, _ = mw.ustring.gsub(text, "%[%[[^%]|\n]+|([^%]|\n]+)%]%]", "%1") -- remove internal links
	text, _ = mw.ustring.gsub(text, "%[%[Kategoria:.-%]%]", "") -- remove categories
	text, _ = mw.ustring.gsub(text, "%[https?://[^%s%]]*([^\n]-)%]", "%1") -- remove external link
	text, _ = mw.ustring.gsub(text, "%[//[^%s%]]*([^\n]-)%]", "%1") -- remove external link
	
	text, _ = mw.ustring.gsub(text, "UNIQ[0-9a-zA-F%-]+QINU", "") -- remove nowiki
	text, _ = mw.ustring.gsub(text, "%W", " ") -- vanish non alpha nor digits
	text, _ = mw.ustring.gsub(text, "%s+", " ") -- compact spaces

	return text	
end

function m.plainText(frame)
	return stripText(frame.args[1], frame.args.templates == "tak")
end

function m.countPattern(frame)
	local text = frame.args[1]
	local pattern = frame.args.pattern
	if not text then
		return -1
	end
	
	if not pattern then
		return -2
	end
	
	local _, count = mw.ustring.gsub(text, pattern, "")
	return count
end

function m.countWords(frame)
	local text = frame.args[1]
	if not text then
		return -1
	end
	
	text = stripText(text)
	local _, count = mw.ustring.gsub(text, "%w+", "")
	return count
end

function m.countCharacters(frame)
	local text = frame.args[1]
	if not text then
		return -1
	end
	
	text = stripText(text)
	local _, count = mw.ustring.gsub(text, "%w", "")
	return count
end

function m.getTextSize(frame)
	local text = frame.args[1]
	if not text then
		return -1
	end
	
	local title = mw.title.new(text)
	if not title then
		return -2
	end
	
	local content = title:getContent()
	if not content then
		return -3
	end

	local full = mw.ustring.len(content)
	content, _ = mw.ustring.gsub(content, "<[Dd][Ii][Vv]%s+[^>]*>", "") -- remove <div>
	content, _ = mw.ustring.gsub(content, "</[Dd][Ii][Vv]>", "") -- remove </div>
	content, _ = mw.ustring.gsub(content, "<[Ss][Pp][Aa][Nn]%s+[^>]*>", "") -- remove <span>
	content, _ = mw.ustring.gsub(content, "</[Ss][Pp][Aa][Nn]>", "") -- remove </span>
	content, _ = mw.ustring.gsub(content, "<[Bb][Rr]%s*/?>", "") -- remove <br/>
	local html = mw.ustring.len(content)
	content, _ = mw.ustring.gsub(content, "\n{|.-\n", "\n{|\n") -- remove table attributes
	content, _ = mw.ustring.gsub(content, "\n|\\-.-\n", "\n|-\n") -- remove table raw attributes
	content, _ = mw.ustring.gsub(content, "{{[^|}]*([|}])", "{{%1") -- remove template name
	content, _ = mw.ustring.gsub(content, "|[^|=]*=", "|") -- remove template field names
	local temp = mw.ustring.len(content)
	content, _ = mw.ustring.gsub(content, "%[%[[^|%]]*|([^%]])", "%1") -- remove internal links
	content, _ = mw.ustring.gsub(content, "%[%[Kategoria:.-%]%]", "") -- remove categories
	content, _ = mw.ustring.gsub(content, "%[http[s]://%S*%s+.-%]", "") -- remove external link
	local link = mw.ustring.len(content)
	content, _ = mw.ustring.gsub(content, "%W", "") -- remove spaces
	local space = mw.ustring.len(content)
	mw.log(full.." (full), "..(html-full).." (html), "..(temp-html).." (template), "..(link-temp).." (link),  "..(space-link).." (space) => "..space.." (text)")
	return space
end

local function selectRandomItems(index, count)
	if #index <= count then
		return index
	end
 
	local result = {}
	while count > 0 do
		local i = math.random(1, #index)
		table.insert(result, index[i])
		table.remove(index, i)
		count = count - 1
	end
 
	return result
end
 
function splitListAndSelectFew(params)
	if not params.text then
		return nil
	end
	
	local lines = mw.text.split(params.text, params.splitter, false)
	if params.trimPrefix then
		if #lines[1] == 0 then
			table.remove(lines,1)
		elseif #params.trimPrefix > 0 then
			lines[1] = mw.ustring.gsub(lines[1], params.trimPrefix, "", 1)
		end
	end

	if (#lines > 1) and params.trimSuffix then
		lines[#lines] = mw.ustring.gsub(lines[#lines], params.trimSuffix, "", 1)
	end

	math.randomseed(params.seed)
	local result = selectRandomItems(lines, params.count)
	return params.prefix..table.concat(result, params.joiner)..params.suffix
end

m.kilka = function(frame)
	local f = frame:getParent()
	local params = {}
	params.seed = tonumber(f.args.los) or 0
	params.count = tonumber(f.args.ile) or 5
	params.text = f.args[1]
	params.splitter =  "%s-\n%*%s*"
	params.joiner = "\n*"
	params.prefix = "*"
	params.suffix = ""
	params.trimPrefix = "^%*%s*"
	params.trimSuffix = "%s+$"
	return splitListAndSelectFew(params)
end

m.replace = function(frame)
	local text = frame.args[1]
	if not text or (#text == 0) then
		return nil
	end
	local pattern = frame.args[2]
	if not pattern or (#pattern == 0) then
		return text
	end
	local replacement = frame.args[3] or ""
	local result, _ = mw.ustring.gsub(text, pattern, replacement)
	return result
end

m.template = function(frame)
	return mw.getCurrentFrame():preprocess("{{ikona|wikicytaty|16}}")
end

m["Numerowanie w tabeli"] = function(frame)
	local counter = 0
	local result = {}
	local pf = frame:getParent()
	local i = 1
 
	table.insert(result, "{") 
	while true do
		local object = pf:getArgument(i)
		if object then
			object = object:expand()
		else
			break
		end
 
		if object then
			table.insert(result, "|")

			local length = #object
			if length > 0 then
				if string.byte(object,1) == 35 then
	 				counter = counter + 1
					local counterText = counter..". "
					local userText = length > 1 and string.sub(object, 2, length) or ""
					object = counterText..userText
				end
			end
		
			table.insert(result, object)
			i = i + 1
		else
			break
		end
	end

	table.insert(result, "|}")
	return table.concat(result, "")
end

m.odnvalidator = function(frame)
	local pf = frame:getParent()
	local i = 1
	local problems = false
	local yeardetected = false
	while true do
		local arg = pf.args[i]
		if not arg then
			problems = i == 1 and "brak argumentów" or false
			break
		end
 
		if (i > 5) or yeardetected then
			problems = "za dużo argumentów pozycyjnych"
			break
		end
 
		if #arg == 0 then
			problems = "pusty argument"
			break
		end
 
		if arg ~= mw.text.trim(arg) then
			problems = "nieoczekiwane odstępy na początku lub końcu argumentu"
			break
		end
 
		if string.match(arg, "^%d+%l?$") then
			yeardetected = true
			if i == 1 then
				problems = "rok musi być ostatnim parametrem po nazwiskach autorów"
				break
			end
		elseif string.match(arg, "^s[%-%.:]%s*%d+") then
			problems = "prawdopodobnie nieprawidłowo podany numer strony"
			break
		elseif string.match(arg, "%s%s") then
			problems = "podwójne odstępy"
			break
		end
 
		i = i + 1
	end
 
	if not problems then
		local odn = pf.args.odn
		if odn and ((#odn ~= 1) or (odn < "a") or (odn > "z")) then
			problems = "nieoczekiwany parametr odn"
		end
	end
 
	if not problems then
		if pf.args.strona or pf.args.ss or pf.args.strony or pf.args.p or pf.args.page or pf.args.pp or pf.args.pages then
			problems = "przestarzały parametr z numerem strony"
		end
	end

 	if not problems then
 		return nil
 	end
 	
 	local result = mw.html.create("span"):addClass("problemy-w-odn")
 	if mw.title.getCurrentTitle().namespace == 0 then
 		result:css("display", "none")
 	else
 		result:css("color", "red")
 	end
 	result:wikitext("ODN: ", problems)
 	return tostring(result).."[[Kategoria:Szablon odn do sprawdzenia]]"
end

m.p = function(frame)
	local title = mw.title.getCurrentTitle()
	local pf = frame:getParent()
	local text = mw.text.trim(pf.args[1] or "")
	if #text == 0 then
		return title.namespace == 0 and "[[Kategoria:Szablon przypisu bez treści]]" or ""
	end

	local detectedReference = false
	local undetectedReferece = false
	local autofullstop = false
	local fullstop = false
	local references = {}
	local i = 2
	while true do
		local ref = pf.args[i]
		if not ref then
			break
		end
		
		i = i + 1
		
		ref = mw.text.trim(ref)
		local rlen = mw.ustring.len(ref)
		if rlen == 0 then
			-- empty full stop detected
			fullstop = ""
			break
		else
			local prefix = mw.ustring.sub(ref, 1, 2)
			if prefix == "p:" then
				-- simple text that must be placed in the <ref> tag
				ref = mw.ustring.sub(ref, 3, rlen)
				ref = frame:extensionTag("ref", ref)
			elseif prefix == "r:" then
				-- it is declared that the text is already wrapped in a <ref> tag
				ref = mw.ustring.sub(ref, 3, rlen)
			elseif prefix == "n:" then
				-- there is given name of the named <ref> tag
				ref = mw.ustring.sub(ref, 3, rlen)
				ref = frame:extensionTag("ref", "", { name = ref })
			elseif mw.ustring.match(ref, "^%p$") then
				-- full stop detected
				fullstop = ref
				break
			elseif string.match(ref, "^\127UNIQ[0-9a-f]+%-ref%-[0-9A-F]+%-QINU\127$") then
				-- simple reference is detected
				detectedReference = true
			else
				undetectedReference = true
				ref = frame:extensionTag("ref", ref)
			end
		
			table.insert(references, ref)
		end
	end

	if #references == 0 then
		return text.."[[Kategoria:Szablon przypisu bez źródeł]]"
	end
	
	if not fullstop then
		autofullstop = true
		local emptyfullstops = { "itp%.$", "itd%.$", "%.%.%.$", }
		for _, v in ipairs(emptyfullstops) do
			if mw.ustring.match(text, v) then
				fullstop = ""
				break
			end
		end
	
		if not fullstop then
			local tail = mw.ustring.match(text, "%p$")
			if tail then
				tlen = mw.ustring.len(text) - mw.ustring.len(tail)
				if tlen <= 0 then
					return "[[Kategoria:Szablon przypisu bez treści]]"
				end
				text = mw.ustring.sub(text, 1, tlen)
				fullstop = tail
			end
		end
	end
	
	-- build result text
	local result = {}
	table.insert(result, "<span class=\"cited-sentence\">")
	table.insert(result, text)
	for _, r in ipairs(references) do
		table.insert(result, r)
	end
	table.insert(result, fullstop)
	table.insert(result, "</span>")
	if detectedReference then
		table.insert(result, "[[Kategoria:Szablon przypisu bez deklaracji odnośnika]]")
	end
	if undetectedReference then
		table.insert(result, "[[Kategoria:Szablon przypisu bez deklaracji opisu]]")
	end
	if autofullstop then
		table.insert(result, "[[Kategoria:Szablon przypisu z automatycznym zakończeniem]]")
	end
	
	return table.concat(result, "")
end

m.see1 = function(frame)
	
	function see(a)
		local result = {}
		local quote = false
		for i = 1, #a do
			local b = string.byte(a, i)
			if (b >= 32) and (b < 127) then
				if not quote then
					if #result > 0 then
						table.insert(result, ", ")
					end
					quote = true
					table.insert(result, "\"")
				end
				table.insert(result, string.char(b))
			else
				if quote then
					table.insert(result, "\"")
					quote = false
				end
				if #result > 0 then
					table.insert(result, ", ")
				end
				table.insert(result, tostring(b))
			end
		end
		if quote then
			table.insert(result, "\"")
		end
		return mw.text.nowiki(table.concat(result, ""))
	end
	
	local a = frame.args[1] or ""
	local b = mw.text.unstrip(a)
	return a.." → "..see(a).." → "..see(b)
end

m.countLinks = function(frame)
	local text = frame.args[1] or ""
	local _, count = mw.ustring.gsub(text, "(%[%[[^%[%]]-%]%])", "%1")
	mw.log(tostring(count))
	local _, files = mw.ustring.gsub(text, "(%[%[Plik:[^%[%]]-%]%])", "%1")
	mw.log(tostring(files))
	count = count - files
	mw.log(tostring(count))
	local lang = mw.getContentLanguage()
	local number = lang:formatNum(count)
	local articles = lang:convertPlural(count, { "artykuł", "artykuły", "artykułów" })
	return number.."&nbsp;"..articles
end

m.select = function(frame)
	local pf = frame:getParent()
	local start = frame.args[1] or pf.args[1]
	if not start then
		return
	end
	
	local stop =  frame.args[2] or pf.args[2]
	if not stop then
		return
	end
	
	local content = mw.title.getCurrentTitle():getContent()
	if not content then
		return
	end
	
	local startPattern = "<!--"..start.."-->"
	local startPosition = string.find(content, startPattern, 1, true)
	if not startPosition then
		return
	end
	
	startPosition = startPosition + #startPattern
	
	local stopPattern = "<!--"..stop.."-->"
	local stopPosition = string.find(content, stopPattern, startPosition, true)
	if not stopPosition then
		return
	end
	
	stopPosition = stopPosition - 1
	if startPosition >= stopPosition then
		return
	end
	
	local text = string.sub(content, startPosition, stopPosition)
	local _, count = mw.ustring.gsub(text, "(%[%[[^%[%]]-%]%])", "%1")
	local _, files = mw.ustring.gsub(text, "(%[%[Plik:[^%[%]]-%]%])", "%1")
	count = count - files
	
	local threshold = tonumber(frame.args["próg"] or pf.args["próg"]) or 1
	if count < threshold then
		return
	end
	
	local lang = mw.getContentLanguage()
	local number = lang:formatNum(count)
	local articles = lang:convertPlural(count, { frame.args[4] or pf.args[4] or "artykuł", frame.args[5] or pf.args[6] or "artykuły", frame.args[6] or pf.args[6] or "artykułów" })

	local result, _ = string.gsub(frame.args[3] or pf.args[3] or "<small>($1)</small>", "$1", number.."&nbsp;"..articles)
	return result
end

m.goodCounter = function(frame)
	local fullText = frame.args[1]
	if not fullText then
		return nil
	end
	
	function testlink(text)
		local b1 = string.byte(text, 1)
		if (b1 ~= 60) and (b1 ~= 91) then
			return false
		end
		
		local prefix = string.sub(text, 1, 7)
		if prefix == "[[Plik:" then
			return false
		end
		
		return (string.sub(prefix, 1, 2) == "[[") or (string.sub(prefix, 1, 5) == "<u>[[")
	end
	
	local forms = { "artykuł", "artykuły", "artykułów" }
	local lang = mw.getContentLanguage()
	
	function showcounter(counter)
		local number = lang:formatNum(counter)
		local articles = lang:convertPlural(counter, forms)
		return "<small>("..number.." "..articles..")</small>\n"
	end
	
	local lines = mw.text.split(fullText, "\n", true)
	local counter = 0
	local result = {}
	for i, line in ipairs(lines) do
		if os.clock() > 30 then
			break
		end
		
		if testlink(line) then
			table.insert(result, line)
			counter = counter + 1
		else
			if counter > 1 then
				table.insert(result, showcounter(counter))
			end
			counter = 0
			table.insert(result, line)
		end
		table.insert(result, "\n")
	end
	
	if counter > 1 then
		table.insert(result, showcounter(counter))
	end

	return table.concat(result, "")
end

m.url = function(frame)
	local result = {}
	local pf = frame:getParent()
	local i = 1
	
	local replace = {
		["["] = "&#91;",
		["]"] = "&#93;",
		["{"] = "&#123;",
		["}"] = "&#125;",
		["|"] = "&#124;",
	}
	
	while true do
		local object = pf:getArgument(i)
		if object then
			object = object:expand()
		end

		if object then
			if i > 1 then
				table.insert(result, "&#124;")
			end

			local fixed = string.gsub(object, ".", replace)
			table.insert(result, fixed)
			i = i + 1
		else
			break
		end
	end

	return table.concat(result, "")
end

m.Match = function(frame)
	return string.match(frame.args[1] or "", frame.args[2] or "")
end

m.text = function(frame)
		local title = mw.title.getCurrentTitle()
		local pf = frame:getParent()
		local defaultDate = pf.args[resources.data.defaultDateIndex] or ""
 
		local complexCategories = {
			categories = function(result, date)
				local switch = title.nsText
				if (switch == "Pomoc") or (switch == "Wikipedia") or (switch == "Portal") or (switch == "Wikiproject") then
					table.insert(result, "[[Kategoria:WP?|"..title.text.."]]")
				elseif switch == "Kategoria" then
					table.insert(result, "[[Kategoria:?|"..title.text.."]]")
				elseif title.namespace == 0 then
					table.insert(result, "[[Kategoria:?]]")
				end
			end,
		}
 
		function prepareTemplates(reason, date, section, comments)
			local c = reason["?"] or {}
			local sourceTemplates = section and resources.sectionTemplates or ((title.namespace == 0) and resources.articleTemplates or resources.otherTemplates)
 
			local templates = {}
			for k, v in pairs(sourceTemplates) do
				templates[k] = v
			end
 
			templates[resources.data.dateTemplate] = date or ""
			templates[resources.data.talkNameTemplate] = title.talkPageTitle.fullText
			templates[resources.data.pageUrlTemplate] = title:fullUrl()
 			templates[resources.data.talkSectionTemplate] = pf.args[resources.data.talkPageSectionParamIndex] or ""
			local defaultComment, _ = string.gsub(c.default or "", resources.data.templatePattern, templates)
			templates[resources.data.infoTemplate] = #comments > 0 and ((c.prefix or "")..table.concat(comments, c.separator or ", ")..(c.suffix or "")) or defaultComment
 
			-- prepare "talk" hint
			local talk = ""
			if title.talkPageTitle.exists then
				talk, _ = string.gsub(reason.talk or "", resources.data.templatePattern, templates)
			end
			templates[resources.data.talkTemplate] = talk
			return templates
		end
 
		local mapParamToReason = {}
		for i, v in ipairs(resources.reasons) do
			for _, a in ipairs(v.aliases) do
				mapParamToReason[a] = i
			end
		end
 
		local reasonDates = {}
 
		-- scan well known reasons with date
		for k, i in pairs(mapParamToReason) do
			local object = pf:getArgument(k)
			if object then
				local currentDate = object:expand()
				if currentDate then
					local previousDate = reasonDates[i]
					if not previousDate or (previousDate == "") or ((currentDate ~= "") and (currentDate < previousDate)) then
						reasonDates[i] = currentDate
					end
				end
			end
		end
 
		local onlyReason = nil
		local allMessages = {}
		local customMessages = {}
 
		-- collect messages with dates
		for i, v in ipairs(resources.reasons) do
			local date = reasonDates[i]
			if date then
				table.insert(allMessages, (#date > 0) and string.format(resources.data.messageWithDatePrefix, date, v.message) or v.message)
				if not onlyReason then
					onlyReason = i
				else
					onlyReason = 0
				end
			end
		end
 
		-- scan reasons without dates
		local scanReasonWithoutDate = function(index)
			local object = pf:getArgument(index)
			if object then
				local reason = object:expand()
				if reason then
					reason = mw.text.trim(reason)
					if (index ~= resources.data.sectionParamIndex) or (reason ~= resources.data.sectionParamValue) then
						if #reason > 0 then
							local known = mapParamToReason[reason]
							if not known then
								table.insert(allMessages, reason)
								table.insert(customMessages, reason)
							else
								if not onlyReason then
									onlyReason = known
								elseif onlyReason ~= known then
									onlyReason = 0
								end
								if not reasonDates[known] then
									reasonDates[known] = defaultDate
									local v = resources.reasons[known].message
									table.insert(allMessages, (#defaultDate > 0) and string.format(resources.data.messageWithDatePrefix, defaultDate, v) or v)
								end
							end
						end
					end
				end
			
				return reason
			end
		end
		
		local sectionParam = scanReasonWithoutDate(resources.data.sectionParamValue)
		for i = 1, 10 do
			scanReasonWithoutDate(i)
		end

		-- collect categories
		local categories = {}
		for i, v in ipairs(resources.reasons) do
			local date = reasonDates[i]
			if date and v.cat then
				local complexCat = complexCategories[v.cat]
				if complexCat then
					complexCat(categories, date)
				elseif title.namespace == 0 then
					table.insert(categories, (v.catDate and (#date > 0)) and string.format(v.catDate, date) or v.cat)
				end
			end
		end
 
		if (#categories == 0) and (title.namespace == 0) then
			table.insert(categories, resources.data.defaultCategory)
		end
 
		-- prepare date hints
		local hints = {}
		local currentDate = nil
		for i, v in ipairs(resources.reasons) do
			local date = reasonDates[i]
			if date and (#date == 0) and v.catDate then
				if not currentDate then
					currentDate = mw.getContentLanguage():formatDate(resources.data.dateFormat, nil, true)
				end
				table.insert(hints, string.format(resources.data.dateExample, v.aliases[1], currentDate))
			end
		end
 
	 	local result = {}
		local section = (pf.args[resources.data.sectionParamIndex] == resources.data.sectionParamValue) or sectionParam
		local onlyReasonData = resources.reasons[onlyReason]
		local mainText = nil
		local templates = {}
		if onlyReasonData and onlyReasonData.simple and ((#customMessages == 0) or onlyReasonData["?"]) then
			local date = reasonDates[onlyReason]
			if #date > 0 then
				date = string.format(resources.data.dateInfix, date)
			end
			templates = prepareTemplates(onlyReasonData, date, section, customMessages)
			mainText, _ = string.gsub(onlyReasonData.simple, resources.data.templatePattern, templates)
		end
		if not mainText and not onlyReason then
			local date = pf.args[resources.data.defaultDateIndex] or ""
			if #date > 0 then
				date = string.format(resources.data.dateInfix, date)
			end
			templates = prepareTemplates(resources.default, date, section, customMessages)
			mainText, _ = string.gsub(resources.default.simple, resources.data.templatePattern, templates)
		end
		if not mainText then
			templates = prepareTemplates(resources.default, "", section, allMessages)
			mainText, _ = string.gsub(resources.default.message, resources.data.templatePattern, templates)
 		end
 
		table.insert(result, mainText)
	 	table.insert(result, table.concat(categories,""))
	 	if title.talkPageTitle.exists and ((#templates[resources.data.talkTemplate] == 0) or (#templates[resources.data.talkSectionTemplate] > 0))  then
	 		local talkPageMessage, _ = string.gsub((#templates[resources.data.talkSectionTemplate] == 0) and resources.data.talkPage or resources.data.talkSection, resources.data.templatePattern, templates)
 			table.insert(result, talkPageMessage)
	 	end
 
	 	local articleInstruction, _ = string.gsub(resources.data.articleInstruction, resources.data.templatePattern, templates)
	 	table.insert(result, articleInstruction)
		if #hints > 0 then
		 	table.insert(result, string.format(resources.data.templateInstruction, table.concat(hints, ", ")))
		end
 
		return table.concat(result, "")
	end


function m.IPA(frame)
	local arg = frame:getParent().args[1]
	if not arg  then
		-- missing arguments
		return nil
	end
	
	if #arg == 0 then
		-- empty argument
		return nil
	end
	
	if not mw.ustring.isutf8(arg) then
		-- invalid input is left as is
		return arg
	end
	
	local ipadata = mw.loadData( 'Moduł:IPA/data' )
	
	local split = function(text)
		local result = {}
		local joined = false
		local current = ""
		for code in mw.ustring.gcodepoint(text) do
			local c = mw.ustring.char(c0)
			if ipadata.joining[code] then
				current = current .. c
				joined = true
			elseif joined or ipadata.combining[code] then
				current = current .. c
				joined = false
			else
				if current ~= "" then
					table.insert(result, current)
				end
				current = c
			end
		end

		-- append last part
		if current ~= "" then
			table.insert(result, current)
		end
		
		return result
	end
	
	local decorate = function(parts)
		local currentLink = nil
		local result = {}
		local count = #parts
		table.insert(parts,"") -- append empty text to simplify loop below
		local i = 1
		while i <= count do
			local s, n = parts[i]..parts[i+1], 2
			local t = ipadata.sounds[s]
			if not t then
				s, n = parts[i], 1
				t = parts.sounds[s]
			end
			
			local link = nil
			if t then
				link = t.see and ipadata[t.see].link or t.link
			elseif mw.ustring.match(s,"%S") then
				link = ipadata[""].link
			end
			
			-- append the optional title
			if link ~= currentLink then
				if currentLink then
					table.insert(result,"]]")
					currentLink = nil
				end
				if link then
					table.insert(result,"[[")
					table.insert(result,link)
					table.insert(result,"|")
					currentLink = link
				end
			end
 
			-- append the sound
			table.insert(result,s)
 
			-- move to next sound in the input
			i = i + n
		end
		
		if currentLink then
			table.insert(result, "]]")
		end
		
		return table.concat(result,"")
	end
	
	local parts = split(arg)
	local result = decorate(parts)
	return result
end

function m.I(frame)
	local args = frame:getParent().args
	local arg1 = args[1]
	if not arg1 then
		-- missing arguments
		return nil
	end
	
	if not string.match(arg1,"^%d*$") then
		-- invalid argument type
		return nil
	end
	
	local num = tonumber(arg1)
	if not num then
		-- invalid argument value
		return nil
	end
	
	if tostring(num) ~= arg1 then
		-- unexpected leading zeros?
		return nil
	end

	local qid = "Q" .. arg1
	local sitelink = mw.wikibase.sitelink(qid)
	
	if sitelink then
		return ""--"[[Kategoria:Wikidane - istniejące artykuły]]"
	else
		return "<sup>[[d:"..qid.."#sitelinks-wikipedia|<span title=\"Poszukaj w innych wersjach językowych\">D</span>]]</sup>"--"[[Kategoria:Wikidane - brakujące artykuły]]"
	end
end

function m.Q(frame)
	local args = frame:getParent().args
	local arg1 = args[1]
	if not arg1 then
		-- missing arguments
		return nil
	end
	
	if not string.match(arg1,"^%d*$") then
		-- invalid argument type
		return nil
	end
	
	local num = tonumber(arg1)
	if not num then
		-- invalid argument value
		return nil
	end
	
	if tostring(num) ~= arg1 then
		-- unexpected leading zeros?
		return nil
	end

	local title1 = args[2]
	local title2 = args[3]
	
	-- reject empty texts
	if title1 and (#title1 == 0) then
		title1 = nil
	end
	if title2 and (#title2 == 0) then
		title2 = nil
	end

	local qid = "Q" .. arg1
	local sitelink = mw.wikibase.sitelink(qid)
	local category
	
	if sitelink then
		wikidata = ""
		category = ""--"[[Kategoria:Wikidane - istniejące artykuły]]"
	else
		sitelink = title1 or mw.wikibase.label(qid) or qid
		wikidata = "<sup>[[d:"..qid.."#sitelinks-wikipedia|<span title=\"Poszukaj w innych wersjach językowych\">*</span>]]</sup>"
		category = ""--"[[Kategoria:Wikidane - brakujące artykuły]]"
	end
	
	local label = title2 and ("|"..title2) or ""
			
	return "[["..sitelink..label.."]]"..wikidata..category
end

function m.TitleContents(frame)
	local ns = frame.args["ns"] or ""
	local title = frame.args[1]
	if not title then
		mw.log("missing article name")
		return nil
	end
	
	local title = mw.title.makeTitle(ns, title)
	if not title then
		mw.log("no title");
		return nil
	end
	
	local content = title:getContent()
	if not content then
		mw.log("no content");
		return nil
	end
	
	mw.log("there is content");
	return "<pre>" .. mw.text.nowiki(content) .. "</pre>"
end

function m.D(frame)
    local entity = mw.wikibase.getEntity()
    if not entity then
        return nil
    else
        return string.format(moduleData.wikidataItemLink, entity.id)
    end
end

local dump = function ( result, level, maxlevel, item, dump )
    local indent = string.rep("\t", level)
    if item == nil then
        return
    elseif type(item) ~= "table" then
        table.insert(result, indent .. tostring(item))
        return
    elseif level >= maxlevel then
        table.insert(result, indent .. "...")
        return
    end
    
    for k, v in pairs(item) do
        local ks
        if type(k) == "string" then
            ks = '"' .. k .. '"'
        else
            ks = tostring(k)
        end
        
        local vs
        if type(v) == "string" then
            vs = '"' .. v .. '"'
            table.insert(result, indent .. "[" .. ks .. "] = " .. vs)
        elseif type(v) == "table" then
            table.insert(result, indent .. "[" .. ks .. "] = {")
            dump(result, level+1, maxlevel, v, dump)
            table.insert(result, indent .. "}")
        else
            vs = tostring(v)
            table.insert(result, indent .. "[" .. ks .. "] = " .. vs)
        end
    end
end

function m.fmtNum(frame)
    local result, n = string.gsub(frame.args[1],"%.",",",1)
    return result
end

local function trim(s)
  return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
end

local function contents(item)
    if item == nil then
        return "<pre>nil</pre>"
    end
    if type(item) ~= "table" then
        return "<pre>" ..tostring(item) .. "</pre>"
    end
    local result = {}
    for k, v in pairs(item) do
        local ks
        if type(k) == "string" then
            ks = '"' .. k .. '"'
        else
            ks = tostring(k)
        end
        
        local vs
        if type(v) == "string" then
            vs = '"' .. v .. '"'
        else
            vs = tostring(v)
        end
        
        table.insert(result, string.format("[%s] = %s\r\n", ks, vs))
    end
    return "<pre>" .. table.concat(result, "") .. "</pre>"
end

function m.parentFrameContents(frame)
    local object = frame:getParent()
    local result = {}
    dump(result, 0, 4, object, dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.frameContents(frame)
    return contents(frame)
end

function m.tableContents(frame)
    return contents(table)
end

function m.stringContents(frame)
    return contents(string)
end

function m.mwContents(frame)
    return contents(mw)
end

function m.mwMessageContents(frame)
    return contents(mw.message)
end

function m.mwSiteContents(frame)
    return contents(mw.site)
end

function m.mwTitleContents(frame)
    return contents(mw.title)
end

function m.mwUriContents(frame)
    return contents(mw.uri)
end

function m.mwUStringContents(frame)
    return contents(mw.ustring)
end

function m.mwLanguageContents(frame)
    return contents(mw.language)
end

function m.mwWikibaseContents(frame)
    return contents(mw.wikibase)
end

function m.dump(frame)
    local bug = {}
    local object = _G
    for i, v in ipairs(frame.args) do
        table.insert(bug, v)
        if not object[v] then
            local n = tonumber(v)
            if n and object[n] then
                v = n
            end
        end
        object = object[v]
        if object == nil then
            return "<span class=\"error\">" .. table.concat(bug, "→") .. "</span>"
        end
    end
    -- return contents(object)
    local result = {}
    dump(result, 0, 4, object, dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.dump1(frame)
	return mw.text.nowiki(frame.args[1] or "")
end

function m.info(frame)
    local bug = {}
    local object = mw.wikibase.getEntity()
    if not object then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    for i, v in ipairs(frame.args) do
        table.insert(bug, v)
        if not object[v] then
            local n = tonumber(v)
            if n and object[n] then
                v = n
            end
        end
        object = object[v]
        if object == nil then
            return "<span class=\"error\">" .. table.concat(bug, "→") .. "</span>"
        end
    end
    return contents(object)
end

function m.current(frame)
	local title = mw.title.getCurrentTitle()
	local result = {}
    dump(result, 1, 4, title, dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.claims(frame)
    local result = {}
    local entity = mw.wikibase.getEntityObject()
    if not entity then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    for k, v in pairs(entity.claims) do
        local label = mw.wikibase.label(k)
        if label then
            table.insert(result, string.format("[%s] (%s) = {", k, label))
        else
            table.insert(result, string.format("[%s] = {", k))
        end
        dump(result, 1, 10, v, dump)
        table.insert(result, "}")
    end
    
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.entity(frame)
    local result = {}
    local entity = mw.wikibase.getEntity()
    if not entity then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    dump(result, 1, 10, entity, dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.entityObject(frame)
    local result = {}
    local entity = mw.wikibase.getEntityObject(frame.args[1])
    if not entity then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    dump(result, 1, 10, entity, dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.entityObjectProperties(frame)
    local result = {}
    local entity = mw.wikibase.getEntityObject()
    if not entity then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    dump(result, 1, 10, entity:getProperties(), dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.entityObjectProperty(frame)
	local prop = frame.args[1]
	if not prop then
		return "<span class=\"error\">Brak identyfikatora cechy</span>"
	end
	
    local entity = mw.wikibase.getEntityObject()
    if not entity then
        return "<span class=\"error\">Brak wikidanych</span>"
    end
    
    local result = {}
    dump(result, 1, 3, entity:formatPropertyValues(prop), dump)
    return "<pre>" .. table.concat(result,"\n") .. "</pre>"
end

function m.sitetitle(frame)
	local site = frame.args[1]
	if not site then
		return nil
	end
	
    local entity = mw.wikibase.getEntity()
    if not entity then
        return nil
    end

    local sitelinks = entity.sitelinks
    if not sitelinks then
        return nil
    end

    local result = sitelinks[site]
    if not result then
        return nil
    end

    return result.title
end

function m.commons(frame)
    local entity = mw.wikibase.getEntity()
    if not entity then
        return nil
    end

    local sitelinks = entity.sitelinks
    if not sitelinks then
        return nil
    end

    local result = sitelinks.commonswiki
    if not result then
        return nil
    end

    return result.title
end

function m.match(frame)
	local s = frame.args[1]
	if not s or (#s==0) then
		return nil
	end
	
	local p = frame.args[2]
	if not p or (#p == 0) then
		return nil
	end
	
	return mw.ustring.match(s, p)
end

function m.testDama(frame)
	local result = mw.ustring.match(frame.args[1] or "", "%f[%w][Dd][Aa][Mm][Aa]%f[%W]")
	mw.log(result)
	return result and result or "NIL"
end

return m