local lang = {}

local function langData(code)
	if not code or (#code == 0) then
		-- brak kodu języka
		--mw.log("Pusty kod języka: "..code);
		return
	end
	
	local data = mw.loadData( 'Module:lang/data' )

	-- bezbłędny kod
	local lang = data[code]
	if lang then
		return lang, code
	end
	
	-- może kod jest podany dużymi literami lub ma spacje przed i po
	local lcode = mw.text.trim(string.lower(code)) -- dobre kody języka są tylko w ASCII
	lang = data[lcode]
	if lang then
		--mw.log('kod "'..code..'" został zamieniony na "'..lcode..'"')
		return lang, lcode
	end
	
	-- może kod jest podany z variantem
	local scode, vcode = string.match(code, "%s*([a-zA-Z]+)%-([^%s]+)%s*$")
	if not scode then
		scode, vcode = string.match(code, "%s*([a-zA-Z]+)_([^%s]+)%s*$")
	end
	
	if scode then
		--mw.log('kod "'..code..'" został rozdzielony na "'..scode..'" i wariant "'..vcode..'"')
		lang = data[scode]
		if not lang then
			--mw.log('kod "'..scode..'" został zamieniony na "'..string.lower(scode)..'"')
			scode = string.lower(scode)
			lang = data[scode]
		end
		
		if lang then
			-- test variant
			if lang.warianty then
				local lvcode = string.lower(vcode)
				for k, v in pairs(lang.warianty) do
					if (type(k) == "number") and (type(v) == "string") and (lvcode == string.lower(v)) then
						--mw.log('kod "'..code..'" w formie kanonicznej "'..scode..'-'..v..'"')
						return lang, scode, scode.."-"..v
					elseif (type(k) == "string") and (type(v) == "table") and (lvcode == string.lower(k)) then
						--mw.log('zindywizualizowany kod "'..code..'" w formie kanonicznej "'..scode..'-'..k..'"')
						local fields = { "hasło", "mianownik", "dopełniacz", "miejscownik", "skrót" }
						local vlang = {}
						for _, f in ipairs(fields) do
							vlang[f] = v[f] or lang[f]
						end
						return vlang, scode, scode.."-"..k
					end
				end
			end
		
			--mw.log('kod "'..code..'" z nieznanym wariantem')
			return lang, scode
		end
	end

	-- poddaje się
	--mw.log('Nieznany kod języka: "'..code..'"');
	return false, code
end

function lang.istnieje(frame)
    local languageData, code, _ = langData(frame.args[1])
    return languageData and code or nil
end

function lang.kursywa(frame)
	local langCode = frame.args[1]
    local languageData, code, _ = langData(langCode)
    if langCode and (langCode ~= code) and string.match(langCode, "^%a+%-[Ll][Aa][Tt][Nn]$") or string.match(langCode, "^%a+%-[Ll][Aa][Tt][Nn]%-") then
    	-- Latn script
    	return "''"
    end
    
    return (languageData and (languageData.kursywa ~= false)) and "''" or ""
end

lang["hasło"] = function(frame)
    local languageData, _, _ = langData(frame.args[1])
    return languageData['hasło']
end
 
lang["skrót"] = function(frame)
    local languageData, _, _ = langData(frame.args[1])
    return languageData['skrót'] or languageData['mianownik']
end

function lang.mianownik(frame)
    local languageData, _, _ = langData(frame.args[1])
    return languageData['mianownik']
end
 
lang["dopełniacz"] = function(frame)
    local languageData, _, _ = langData(frame.args[1])
    return languageData['dopełniacz']
end

function lang.miejscownik(frame)
    local languageData, _, _ = langData(frame.args[1])
    return languageData['miejscownik']
end

function list(frame, multi, showPL)
    -- konwersja kodów języków na kod wiki text opisujący kody
    local items = {}       -- tabela na skonwertowane kody
    
    -- flagi informujące, że na liście znajdują się podejrzane kody
    local invalid = false  -- nieznany kod języka
    local warning = false  -- język z listy częstych błędów
    local hiddenpl = false -- ukryty język polski
    local modified = false -- kod języka z automatyczną korektą
    local duplicated = false -- wielokrotnie podany (prawie) taki sam kod języka

    local args = frame.args;
    if not args[1] then
        -- brak argumentów sugeruje wywołanie z szablonu
        local parent = frame:getParent()
        if parent then
            args = parent.args
        end
    end

    if not args[1] then
        return "<span style=\"color:red\">'''Błąd! Brak kodu języka.'''</span>"
    end
    
    -- języki wpadające do [[Kategoria:Częste błędy kodu w szablonie lang]]
    local warnings = {
    	ee = true,
    	am = true,
    	an = true,
    	ang = true,
    	ng = true,
    	kr = true,
    	se = true,
    	si = true
    }
    
    local formatLanguageItem = function(langCode, languageData, alt)
    	local class = alt and "lang-link-warning" or "" 
    	if #class > 0 then
    		class = 'class="'..class..'"'
    	end
        local skrot = languageData['skrót'] or languageData['mianownik']
        local nativeName = mw.language.fetchLanguageName(langCode)
        if nativeName and #nativeName > 0 and langCode ~= "pl" then
            return string.format("<abbr %s title=\"Treść w języku %s (%s)\">%s</abbr>", class, languageData['miejscownik'], nativeName, skrot)
        else
            return string.format("<abbr %s title=\"Treść w języku %s\">%s</abbr>", class, languageData['miejscownik'], skrot)
        end
    end

	local parsedLanguages = {}
    local countGood = 0
    
    local formatItem = function(languageCode)
	    local languageData, langCode, langTag = langData(languageCode)
	    local altFormat = false
	    if langCode and (languageCode ~= langCode) then
	    	altFormat = languageCode ~= langTag
	    	modified = modified or altFormat
	    end

    	if languageData then
    		if warnings[langCode] then
                warning = true
            end
 
 			local item = formatLanguageItem(langCode, languageData, altFormat)
 			if parsedLanguages[langCode] then
 				duplicated = true
 				--mw.log("Powtórne przywołanie języka ("..languageCode..")")
 			else
 				parsedLanguages[langCode] = item;
				if (langCode == "pl") and not showPL then
					-- pomiń język polski
					hiddenpl = true
					--mw.log("Ukrywam język polski")
				else
	 				table.insert(items, item)
	 				countGood = countGood + 1
		 		end
 			end
        elseif not langCode or #langCode == 0 then
            if #items == 0 then
                -- pierwszy kod nie może być pusty, reszta może, bo jest przekazywana jako pusta w szablonach cytowania
                invalid = true
                table.insert(items, "<span style=\"color:red\">'''Błąd! Brak kodu języka.'''</span>")
            end
        else
            invalid = true
            local item = string.format("<span style=\"color:red\">'''Błąd! Nieznany kod języka: %s. Sprawdź [[Pomoc:Interwiki|listę kodów]].'''</span>", languageCode)
 			if parsedLanguages[languageCode] then
 				duplicated = true
 				--mw.log("Powtórne przywołanie języka ("..languageCode..")")
 			else
 				parsedLanguages[languageCode] = item;
 				table.insert(items, item)
 			end
        end
    end
    
    if args[2] or multi then
        -- wiele argumentów sugeruje przekazywnie każdego kodu języka w oddzielnych argumentach
        for i, v in ipairs(args) do
            if #v > 0 then
                formatItem(v)
            end
        end
    else
        -- jeden argument pozwala także wysłać wszystkie kody oddzielone spacją
        for languageCode in string.gmatch(args[1], "%S+") do
            formatItem(languageCode)
        end
    end
    
    if #items == 0 then
    	if hiddenpl then
    		-- podano tylko język polski, który jest ukryty
    	else
	        -- pusta lista kodów
	        invalid = true
	        table.insert(items, "<span style=\"color:red\">'''Błąd! Brak kodu języka.'''</span>")
        end
    end

    -- ostateczne formatowanie wyniku
    local result = {}
    
    if #items > 0 then
    	if (countGood > 0) and hiddenpl then
    		-- przywracam język polski jeśli lista zawiera więcej niż jeden język 
    		table.insert(items, 1, parsedLanguages.pl)
    		hiddenpl = false
    	end
    	
	    table.insert(result, '<span class="lang-list')
	    if (#items == 1) and parsedLanguages.pl then
	    	table.insert(result, " tylko-pl")
	    end
	    
	    table.insert(result, '">(')
	    table.insert(result, table.concat(items, "&nbsp;•&nbsp;"))
	    table.insert(result, ")</span>")
	end

    if mw.title.getCurrentTitle().namespace == 0 then
        if warning then
            table.insert(result, "[[Kategoria:Częste błędy kodu w szablonie lang]]")
        end
        if invalid then
            table.insert(result, "[[Kategoria:Nierozpoznany kod języka w szablonie lang]]")
        end
        if hiddenpl then
            table.insert(result, "[[Kategoria:Szablon lang z ukrytym językiem polskim]]")
        end
        if modified or duplicated then
            table.insert(result, "[[Kategoria:Szablon lang z kodem języka wymagającym poprawki]]")
        end
    end

    return table.concat(result, "")
end

function lang.lang(frame)
	return list(frame, true, true)
end

lang["język"] = function(frame)
	return list(frame, false, true)
end

return lang