local p={}
local lib_arg={};
local ilh = {}
local TrackingCategory = {}
local yesno = {}
local lib_va = {}
local wiki_db = {}
local wikidata_entity_namespaces = {['e']='EntitySchema', ['l']='Lexeme', ['p']='Property', ['q']=''}
local wikidata_namespaces_support = {['p']=true, ['q']=true}
local project_table = {
	commons = ".wikimedia",
	meta = ".wikimedia",
	species = ".wikimedia"
}

function p.title2entity(frame)
	local args, working_frame
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame, {parentFirst=true})
        working_frame = frame
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        working_frame = mw.getCurrentFrame()
        if type(args) ~= type({}) then args = {frame} end
    end

	if type(yesno) ~= type(type) then yesno = require('Module:Yesno') end
    local with_nodata = yesno(args['with_nodata'] or args['with nodata'])
    local input_title = mw.text.trim(tostring(args['1'] or args[1] or ''))
    
    --如果內容為空,取消計算,回傳本模板所在頁面的維基數據項目
    if mw.text.trim(input_title) == '' then 
    	return mw.wikibase.getEntityIdForCurrentPage() or ''
    end
    local entity_id = mw.wikibase.getEntityIdForTitle(input_title)
    if entity_id then return tostring(entity_id) end
    local title_obj = mw.title.new(input_title)
    --如果條目存在
    if (title_obj or {}).exists == true then
    	--檢查原始頁面是否有維基數據項目
    	local title_id = mw.wikibase.getEntityIdForTitle(title_obj.prefixedText)
    	--如果有,則回傳
    	if title_id then return title_id end
    	--如果目標頁是重新導向頁
    	title_obj = title_obj.redirectTarget
    	--回傳目標頁的維基數據項目
    	if title_obj and type(title_obj)==type({"table"}) then 
    		entity_id = mw.wikibase.getEntityIdForTitle(title_obj.prefixedText) 
    	end
    	return entity_id
    end
    
    --如果本身已經是維基數據項目
    local start,tail = mw.ustring.find(input_title,'[qQ]%d+')
    if start then 
    	local in_id = 'Q' .. mw.ustring.sub(input_title,start+1,tail)
    	if mw.wikibase.getLabel(in_id) ~= nil then return in_id 
    	else return '' end
    end
    local try_parse = mw.ustring.gsub(input_title,'[.,]','')
    
	if with_nodata then
		--提供無值時,又不自我指向的數值
		if mw.ustring.lower(input_title) == "noentity" then return 'Q28343326' end--無值
	end
    
    if type(lib_va.redirect_target) ~= type(type) then lib_va = require('Module:Va') end
	--如果目標頁是繁簡差異的自動重新導向頁
	title_obj = mw.title.new(lib_va.redirect_target(input_title))
	--如果檢查繁簡差異後發現頁面存在
	if (title_obj or {}).exists == true then
		--嘗試取得存在之頁面的維基數據項目
		local title_id = mw.wikibase.getEntityIdForTitle(title_obj.prefixedText)
		--如果項目存在,則返回數值
		if title_id then return title_id end
	end
    
    --嘗試讀取數字是否為維基數據項目
    start,tail = mw.ustring.find(try_parse,'%d+')
    if start then 
    	local parseNum = tonumber(mw.ustring.sub(try_parse,start,tail) or '0') or 0
		if mw.wikibase.getLabel('Q'..parseNum) ~= nil then return 'Q'..parseNum
    	else return '' end
    end
    --是非空字串,但都不是的情況
    if mw.text.trim(try_parse) ~= '' then return '' end
    --如果以上條件都不滿足,回傳本模板所在頁面的維基數據項目
    return mw.wikibase.getEntityIdForCurrentPage() or ''
end
function _wikidata_ilh(arg)
	if type(ilh._ilh) ~= type(type) then ilh = require('Module:Ilh') end
	if type(yesno) ~= type(type) then yesno = require('Module:Yesno') end
	local context={}
	context["isMainPage"]=ilh.isMainPage()
	context["localPage"]=arg[1]
	context["foreignPage"]=arg[2] or arg[1] --如果{{{2}}}不传入的话
	local _d=arg["d"]
	local _1=arg[1] or arg["1"]
	local _3=arg[3] or arg["3"]
	local dpN1=_3 or _d
	context["displayName"]=(dpN1 and {dpN1} or {_1})[1]
	context["langCode"]=arg["lang-code"]
	context["lang"]=arg["lang"]
	context["nocat"]=yesno( arg["nocat"] , false )

	context["isExist"]= (arg["$exist$"] and arg["$exist$"]==1) or ilh.isExist(context["localPage"])
	
	local curPage_obj=mw.title.getCurrentTitle()
	context["isNoCatWithNamespace"]=curPage_obj:inNamespaces(2,828) --User,Module
	--context["curPageNamespace"]=curPage_obj.namespace

	return (context["isMainPage"] and ilh.onlyLink(context)) or ilh.functionLink(context)
end
local function getEntitySitelink(entity)
	local sitelinks = (entity or {}).sitelinks
	if sitelinks then
		local i = 0
		for _,__ in pairs(sitelinks) do i = i + 1 end
		if i <= 0 then return nil, nil end
		if not wiki_db.data then wiki_db = mw.loadData('Module:NUMBEROF/data') end
		local db_map, db_data = wiki_db.map, wiki_db.data
		if not db_map or not db_data then return nil, nil end
		local max_lang, max_sitelinkObj, max_article = nil, nil, -1
		for lang, sitelinkObj in pairs(sitelinks) do
			local lang_name = lang:match("^(.+)wiki$")
			if lang_name and lang_name ~= "commons" then
				local wikiCountObj = db_data[lang_name..(project_table[lang_name] or '.wikipedia')]
				if wikiCountObj then
					local wikiCount = tonumber(wikiCountObj[db_map.articles])
					if type(wikiCount) == type(0) then
						if wikiCount > max_article then
							max_article = wikiCount
							max_lang = lang_name
							max_sitelinkObj = sitelinkObj
						end
					end
				end
			end
		end
		if max_sitelinkObj then
			return max_lang, max_sitelinkObj.title
		end
	end
	return nil, nil
end
local function getEntityLabel(entity)
	local label = entity:getLabel()
	if not label then
		labels = entity.labels
		if not labels then return nil end
		local i = 0
		for _,__ in pairs(labels) do i = i + 1 end
		if i <= 0 then return nil end
		if not wiki_db.data then wiki_db = mw.loadData('Module:NUMBEROF/data') end
		local db_map, db_data = wiki_db.map, wiki_db.data
		if not db_map or not db_data then return nil end
		local max_lang, max_labelObj, max_article = nil, nil, -1
		for lang, labelObj in pairs(labels) do
			local wikiCountObj = db_data[lang..(project_table[lang_name] or '.wikipedia')]
			if wikiCountObj then
				local wikiCount = tonumber(wikiCountObj[db_map.articles])
				if type(wikiCount) == type(0) then
					if wikiCount > max_article then
						max_article = wikiCount
						max_lang = lang
						max_labelObj = labelObj
					end
				end
			end
		end
		return (max_labelObj or {}).value
	end
	return label
end

function p._getWikidataLink(wikidata_id, find_lang, display_text, articlename, templateflag) 
	local display_code = display_text or ''
	local display_linkcode = (mw.text.trim(display_code) ~= '')and ('|'..display_code) or display_code

	local namescape_end = mw.ustring.find(wikidata_id ,":")
	local g_namespace = namescape_end and mw.ustring.sub(wikidata_id, 1, namescape_end-1) or ''

	local namespace_head = mw.ustring.sub(wikidata_id,1,1):lower()
	local entity_namespace = wikidata_entity_namespaces[namespace_head] or ''
	local wikidata_pagename = entity_namespace .. ((mw.text.trim(entity_namespace)~='')and ':' or '') .. wikidata_id
	if mw.text.trim(g_namespace)~='' then 
		wikidata_pagename = wikidata_id 
		entity_namespace = g_namespace
		namespace_head = g_namespace
	end
	if wikidata_namespaces_support[mw.ustring.lower(namespace_head)] then
		if mw.wikibase.entityExists( wikidata_id ) then
			local entity = mw.wikibase.getEntity( wikidata_id )
			if mw.text.trim(entity:getSitelink() or '') ~= '' then 
				if templateflag == "link-wd" then
					--all pass to Module:ilh
				else
					return '[[' .. entity:getSitelink() .. display_linkcode .. ']]' 
				end
			end
			local interwiki = 'd'
			local interwikiPage = wikidata_pagename
			local force_wikidata = false
			for ____,value in ipairs(find_lang) do
				if mw.text.trim(entity:getSitelink( value ) or '') ~= '' then
					interwiki, interwikiPage = value, entity:getSitelink( value )
					break
				elseif mw.text.trim(entity:getSitelink( value .. 'wiki' ) or '') ~= '' then
					interwiki, interwikiPage = value, entity:getSitelink( value .. 'wiki' )
					break
				elseif value == 'wikidata' then 
					interwiki = 'd'
					force_wikidata = true
				end
			end
			if interwiki == 'd' then
				local l_interwikiPage = interwikiPage
				interwiki, interwikiPage = getEntitySitelink(entity)
				interwiki, interwikiPage = (interwiki or 'd'), (interwikiPage or l_interwikiPage)
				if force_wikidata then interwiki = 'd' end
				if interwiki == 'd' then interwikiPage = wikidata_pagename end
			end
			if interwiki ~= 'd' then
				local lang = mw.language.fetchLanguageName( interwiki, mw.getContentLanguage():getCode() )
				if (lang or '') ~= '' then
					return _wikidata_ilh({
						[1] = articlename or getEntityLabel(entity) or interwikiPage,
						[2] = interwikiPage,
						[3] = (mw.text.trim(display_code) ~= '')and display_code or nil,
						['lang'] = lang,
						['lang-code'] = interwiki
					})
				end
			end
			local eLabel = entity:getLabel()
			if mw.text.trim(eLabel or '') == '' then eLabel = interwikiPage end
			local result = _wikidata_ilh({
						[1] = articlename or eLabel,
						[2] = interwikiPage,
						[3] = (mw.text.trim(display_code) ~= '')and display_code or nil,
						['lang'] = '維基數據',
						['lang-code'] = interwiki
					})
			return mw.ustring.gsub(result,'(>維基數據)(<)','%1所列%2')
		else return wikidata_id end
	end
	local page_name = (type(find_lang) == type("string")) and find_lang or wikidata_id
	local result = _wikidata_ilh({
				[1] = articlename or page_name,
				[2] = wikidata_pagename,
				[3] = (mw.text.trim(display_code) ~= '')and display_code or nil,
				['lang'] = '維基數據',
				['lang-code'] = 'd'
			})
	return mw.ustring.gsub(result,'(>維基數據)(<)','%1所列%2')
end
function p.linkwikidata(frame)
	local args, working_frame
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame, {parentFirst=true})
        working_frame = frame
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        working_frame = mw.getCurrentFrame()
        if type(args) ~= type({}) then args = {frame} end
    end
    local input_article = args[1] or args['1'] or args.id or ''
    local find_lang_str = args[2] or args['2'] or args.lang or args.langs or 'en,de,fr,ja'
    local find_lang = mw.text.split(find_lang_str, ',') or {}
    if #find_lang <= 1 then find_lang[1] = find_lang_str end
	local result = mw.ustring.gsub(input_article,'([Qq][%d]+)',function(wikidata_id) 
		return p._getWikidataLink(wikidata_id, find_lang)
	end)
	return result
end

function p.linkwikidata_single(frame)
	local args, working_frame
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame, {parentFirst=true})
        working_frame = frame
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        working_frame = mw.getCurrentFrame()
        if type(args) ~= type({}) then args = {frame} end
    end
    local input_article = args[1] or args['1'] or args.id or ''
    local find_lang_str = args[3] or args['3'] or args.lang or args.langs or 'en,de,fr,ja'
    local input_display = args[2] or args['2']
    
    local articlename =	args.page or args.pagename or args.page_name or args['page name'] or 
    					args.articlename or args.article_name or args['article name'] or ''
    if mw.text.trim(articlename) == '' then articlename = nil end
    
    local find_lang = mw.text.split(find_lang_str, ',') or {}
    if #find_lang <= 1 then find_lang[1] = find_lang_str end
    
    local namescape_end = mw.ustring.find(input_article ,":")
	local g_namespace = namescape_end and mw.ustring.sub(input_article, 1, namescape_end-1) or ''

	local namespace_head = mw.ustring.sub(input_article,1,1):lower()
	if mw.text.trim(g_namespace)~='' then 
		namespace_head = g_namespace
	end
    local title_check = ((mw.text.trim(args.title or '')~='')and args.title or find_lang_str)

    local result=p._getWikidataLink(input_article, 
    	(((not wikidata_namespaces_support[mw.ustring.lower(namespace_head)]) or mw.text.trim(g_namespace)~='')
    		and not(({mw.ustring.find(title_check,',')})[1]))
    	and title_check or find_lang, input_display, articlename, "link-wd")
	return result
end
return p;