模組:Namespace pagename

local yesno = require('Module:Yesno')
local mError = require('Module:Error')
local mNS = require('Module:Namespace')
local data = mw.loadData('Module:Namespace_pagename/data')

local lan = mNS._lan

local p = { _internal = {} }
p._raw = p._internal -- @deprecated

local function getNSbyID(key, lang) 
	return mNS._NamespacesDisplay({lang, key})
end

local function msg(group, key, lang) 
	return lan(data[tostring(group)][tostring(key)] or {}, lang)
end

local function makeError(msg)
	return mError.error{'[[Module:Namespace_pagename]]錯誤:' .. msg}
end

local function inNamespaces(title, namespaces)
	local namespacesMeta = getmetatable(namespaces)
	if table.unpack then
		-- lua 5.2+
		return title:inNamespaces(table.unpack(namespaces))
	elseif not namespacesMeta then
		-- lua 5.1 沒有 metatable.__index,unpack 無法運作
		return title:inNamespaces(unpack(namespaces))
	end
	for _, namespace in ipairs(namespaces) do
		if title:inNamespace(namespace) then
			return true
		end
	end
	return false
end

local function getDefaultSuffix(title, lang)
	if inNamespaces(title, data.NamespaceSuffixConfig.SuppressDefaultSuffix) then
		return ''
	elseif title.namespace == -1 then
		return lan({
			['en'] = ' page',
			['zh-hans'] = '页面',
			['zh-hant'] = '頁面'
		}, lang)
	elseif title.namespace == 2 then
		return lan({
			['en'] = ' page',
			['zh-hans'] = '页',
			['zh-hant'] = '頁',
			['zh-tw'] = '頁面' -- 使用者頁面
		}, lang)
	else
		return lan({
			['en'] = ' page',
			['zh-hans'] = '页',
			['zh-hant'] = '頁'
		}, lang)
	end
end

p._ispseudo = mNS._ispseudo
p.ispseudo = mNS.ispseudo

function p._internal.namespace(arg, nsid, lang)
	arg = arg or (nsid and '' or mw.title.getCurrentTitle().fullText)
	nsid = nsid or mw.title.getCurrentTitle().namespace
	if p._ispseudo(arg) then --偽命名空間
		return getNSbyID(nsid == 1 and 'Talk:' .. p._ispseudo(arg) or p._ispseudo(arg), lang)
	end
	local statue, namespace = pcall(getNSbyID, nsid, lang)
	if not statue then
		error(string.format(msg('Error', 'ns-error', lang), 'namespace number = ' .. nsid))
	end
	return namespace
end

local function lowerFirst(input)
	return mw.ustring.lower(mw.ustring.sub(input, 1, 1)) .. mw.ustring.sub(input, 2)
end

-- enum
local PageType = (function ()
	local enum = {
		testcases = 'testcases',
		doc = 'doc',
		sandbox = 'sandbox',
		temp = 'temp',
		intro = 'intro',
		editnotice = 'editnotice',
		-- mailnotice = 'mailnotice',
		-- shortcut = 'shortcut',
	}
	return setmetatable({}, {
		__index = function (t, k)
			if enum[k] then
				return k
			end
			error('Fail to found enum key: ' .. k)
		end,
		__newindex = function (t, k, v)
			error('Fail to modify enum key: ' .. k)
		end,
		__pairs = function (t)
			return function (_t, index)
				return next(enum, index)
			end, t, nil
		end
	})
end)()

function p._internal.namespace_pagename(title, lang, skipCheckPageType)
	title = title or mw.title.getCurrentTitle()
	
	if title.isExternal then
		return msg('Type', 'External', lang)
	end

	local subpageName = lowerFirst(title.subpageText)
	
	local namespace = p._internal.namespace(title.fullText, title.namespace, lang)
	if p._ispseudo(title.fullText) then --偽命名空間
		return namespace
	end

	if not skipCheckPageType then
		local contentModel = title.contentModel

		if contentModel == 'css' or contentModel == 'sanitized-css' then
			return namespace .. msg('Type', 'style', lang)
		elseif contentModel == 'javascript' then
			return namespace .. msg('Type', 'script', lang)
		elseif contentModel == 'json' then
			return namespace .. msg('Type', 'JSON', lang)
		elseif title.isRedirect then
			return namespace .. msg('Type', 'redirect', lang)
		end

		local pageType = nil

		if title.namespace == 829 and subpageName == 'testcases' then
			pageType = PageType.testcases
		elseif inNamespaces(title, data.NamespaceSuffixConfig.SuppressPageType) then
			return namespace
		elseif title.namespace % 2 == 0 then
			local isAllowTemplateType = inNamespaces(title, data.NamespaceSuffixConfig.AllowTemplateType)
	
			if
				(title.namespace > 1 and title.namespace ~= 6 and title.text == '沙盒')
				or (title.namespace == 6 and mw.ustring.match(title.text, '^沙盒%.[A-Za-z]+$'))
				or (title.namespace == 10 and title.rootText == '沙盒')
				or (title.namespace == 10 and mw.ustring.match(title.text, '^X%d'))
			then
				pageType = PageType.sandbox
			end
	
			-- 先分析子頁面標題
			-- P.S. mw.title.rootText: If this is a subpage, the title of the root page without prefixes. Otherwise, the same as title.text.
			if not pageType and title.rootText ~= title.text then
				if
					(subpageName == 'doc')
					and isAllowTemplateType
				then
					pageType = PageType.doc
				elseif
					(subpageName == 'sandbox' or subpageName == '沙盒')
					and isAllowTemplateType
				then
					pageType = PageType.sandbox
				elseif
					(subpageName == 'testcases' or subpageName == '测试样例' or subpageName == '測試樣例')
					and isAllowTemplateType
				then
					pageType = PageType.testcases
				elseif
					(subpageName == 'temp' or subpageName == 'temporary')
					and inNamespaces(title, data.NamespaceSuffixConfig.AllowTempType)
				then
					pageType = PageType.temp
				elseif
					(subpageName == 'intro' or subpageName == 'introduction' or subpageName == '说明' or subpageName == '說明')
					and title.namespace == 10
				then
					pageType = PageType.intro
				elseif
					(title.rootText == 'Editnotices' and title.namespace == 10)
					or (title.subpageText == 'Editnotices' and title.rootText == title.baseText and title:inNamespaces(2, 3))
				then
					return msg('Type', 'editnotice', lang)
				elseif
					(title.subpageText == 'Emailnotice' and title.rootText == title.baseText and title.namespace == 2)
				then
					local user = title.rootText
					return '[[User:' .. user .. '|' .. user .. ']]' .. lan({
						['en'] = "'s ",
						['zh-hans'] = '的',
						['zh-hant'] = '的'
					}, lang) .. msg('Type', 'mailnotice', lang)
				end
			end
			
			-- 依然分析失敗再分析內文(減少不必要的嵌入)
			if not pageType then
				local content = title:getContent() or ''
	
				if
					mw.ustring.match(content, '%{%{[Ss]andbox')
					or mw.ustring.match(content, '%{%{[Tt]emplate[_%s]sandbox[_%s]notice')
					or mw.ustring.match(content, '%{%{請注意:請在這行文字底下進行您的測試,請不要刪除或變更這行文字以及這行文字以上的部份。')
					or mw.ustring.match(content, '%{%{请注意:请在这行文字底下进行您的测试,请不要删除或变更这行文字以及这行文字以上的部分。')
					or (title.namespace == 6 and mw.ustring.match(content, '%{%{[Pp]D%-self%/沙盒%|'))
				then
					pageType = PageType.sandbox
				elseif
					isAllowTemplateType
					and (
						mw.ustring.match(content, '%{%{[Dd]ocumentation[_%s]subpage')
						or mw.ustring.match(content, '%{%{[Tt]emplate[_%s]doc[_%s]inline')
						or mw.ustring.match(content, '%{%{[Tt]emplate[_%s]doc[_%s]viewed[_%s]directly')
					)
				then
					pageType = PageType.doc
				elseif 
					isAllowTemplateType
					and (
						mw.ustring.match(content, '%{%{[Tt]emplate[_%s]testcases[_%s]notice')
						or mw.ustring.match(content, '%{%{[Tt]est[_%s]cases[_%s]notice')
						or mw.ustring.match(content, '%{%{测试样例')
						or mw.ustring.match(content, '%{%{測試樣例')
					)
				then
					pageType = PageType.testcases
				elseif
					mw.ustring.match(content, '{{捷徑重定向([^}]+)}}')
					or mw.ustring.match(content, '{{捷径重定向([^}]+)}}')
					or mw.ustring.match(content, '{{快捷重定向([^}]+)}}')
					or mw.ustring.match(content, '{{快捷方式重定向([^}]+)}}')
					or mw.ustring.match(content, '{{RTL([^}]+)%|捷徑')
				then
					return string.format(msg('Type', 'shortcut', lang), wt)
				end
			end
		end

		if pageType then
			return namespace .. msg('Type', pageType, lang)
		end
	end

	local output = (namespace .. getDefaultSuffix(title, lang) or ''):gsub('talk page', 'talkpage')
	return output
end

function p._namespace(page_or_nsid, lang)
	if type(page_or_nsid) == type(0) then
		return p._internal.namespace(nil, page_or_nsid, lang)
	end
	local title = type(page_or_nsid) == type({}) and page_or_nsid or mw.title.new(page_or_nsid)
	return p._internal.namespace(title, title.namespace, lang)
end

function p._namespace_pagename(title, lang, skipCheckPageType)
	return p._internal.namespace_pagename(type(title) == type({}) and title or mw.title.new(title), lang, skipCheckPageType)
end

function p._main(lang, arg, skipCheckPageType)
	if type(lang) == type({}) then
		arg = lang[2] or lang['2'] or nil
		skipCheckPageType = lang['skipCheckPageType'] or false
		lang = lang[1] or lang['1'] or nil
	end
	local success, title
	local nsid
	if arg then
		if arg == '' then
			return getNSbyID(0, lang)
		elseif arg:match('^([Pp]age):') then -- 兼容:早期版本
			arg = arg:gsub('^[Pp]age:', '')
			success, title = pcall(mw.title.new, arg)
			if not success or not title then
				return makeError(string.format(msg('Error', 'pg-error', lang), arg))
			end
			nsid = title.namespace
		elseif arg:match('^([Nn]ame):') then -- 兼容:早期版本
			title = nil
			arg = arg:gsub('^([Nn]ame):', '')
			nsid_info = mw.site.namespaces[tonumber(arg)] or mw.site.namespaces[arg]
			if mw.text.split(arg, ':')[2] and not nsid_info and not p._ispseudo(arg) then
				return makeError(string.format(msg('Error', 'ns-error', lang), arg))
			end
			nsid = nsid_info and nsid_info.id
		else
			success, title = pcall(mw.title.new, arg)
			local ns = mw.site.namespaces[tonumber(arg)] or mw.site.namespaces[arg]
			if not success or not title then -- args2 無法被mediawiki正確解析(aka 標題不可能存在)
				return makeError(string.format(msg('Error', 'pg-error', lang), arg))
			elseif ns then -- args2 是{{ns:}}能解析的
				title = nil
				nsid = ns.id
			else -- 其他正常標題,無效的命名空間也被當條目處理
				nsid = title.namespace
			end
		end
	else -- 以標題讀取
		arg = mw.title.getCurrentTitle().fullText
		title = mw.title.getCurrentTitle()
		nsid = title.namespace
	end


	local statue, output
	if title then
		statue, output = pcall(p._internal.namespace_pagename, title, lang, skipCheckPageType)
	else
		statue, output = pcall(p._internal.namespace, arg, nsid, lang)
	end
	return statue and output or makeError(output)
end

function p.main(frame) 
	local args = require('Module:Arguments').getArgs(frame, {
		valueFunc = function (key, value)
			if key == 2 then
				return type(value) == 'string' and mw.text.trim(value) or value
			elseif value then
				value = mw.text.trim(value)
				if value ~= '' then
					return value
				end
			end
			return nil
		end
	})
	return p._main(args)
end

return p