Documentația acestui modul poate fi creată la Modul:Wikidata/test/doc

local getArgs = require('Module:Arguments').getArgs
local SepEntries = require('Module:Separated entries')
local StringUtils = require('Modul:StringUtils')
local DateUtils = require('Modul:DateUtils')
local GregorianDate = require('Modul:GregorianDate')
local Citation = require('Modul:Citation/CS1')
local TableTools = require('Modul:TableTools')
local LangUtils = require('Modul:LangUtils')
local p = {}
local lang = mw.language.getContentLanguage()
local libraryUtil = require('libraryUtil')

local wdReferences = {}

-- internationalisation
local i18n = {
    ["errors"] = {
        ["property-not-found"] = "Proprietatea nu a fost găsită.",
        ["entity-not-found"] = "Itfemul Wikidata nu a fost găsit.",
        ["unknown-claim-type"] = "Tip de afirmație negăsit.",
        ["unknown-snak-type"] = "Tip de Snak negăsit.",
        ["unknown-datavalue-type"] = "Tip de date negăsit.",
        ["unknown-entity-type"] = "Tip de item negăsit.",
        ["qualifier-not-found"] = "Calificator negăsit.",
        ["site-not-found"] = "Proiect Wikimedia negăsit.",
    },
    ["somevalue"] = "O valoare",
    ["novalue"] = "Nicio valoare",
    ["datetime"] =
	{
		-- $1 is a placeholder for the actual number
		[0] = "$1 miliarde de ani",		-- precision: billion years
		[1] = "$100 milioane de ani",	-- precision: hundred million years
		[2] = "$10 milioane de ani",	-- precision: ten million years
		[3] = "$1 milioane de ani",		-- precision: million years
		[4] = "$100.000 de ani",	-- precision: hundred thousand years
		[5] = "$10.000 de ani",		-- precision: ten thousand years
		[6] = "mileniul $1", 	-- precision: millennium
		[7] = "secolul $1",	-- precision: century
		[8] = "deceniul anilor $1",				-- precision: decade
		-- the following use the format of #time parser function
		[9]  = "Y",					-- precision: year, 
		[10] = "F Y",				-- precision: month
		[11] = "j F Y",			-- precision: day
		[12] = 'j F Y, "orele" G',	-- precision: hour
		[13] = "j F Y G:i",		-- precision: minute
		[14] = "j F Y G:i:s",		-- precision: second
		["beforenow"] = "acum $1",	-- how to format negative numbers for precisions 0 to 5
		["afternow"] = "peste $1",		-- how to format positive numbers for precisions 0 to 5
		["bc"] = '$1 "î.e.n."',		-- how print negative years
		["ad"] = "$1"				-- how print positive years
	},
	["monolingualtext"] = '<span lang="%language">%text</span>',
	["warnDump"] = "[[Categorie:Apel de funcție 'Dump' din modulul Wikidata]]"
}

local function propAndEntity(arg1, arg2)
	local propId, entId
	for _,eachArg in ipairs({arg1, arg2}) do
		if mw.ustring.match(eachArg, 'Q%d+') == eachArg then
			entId = eachArg
		elseif mw.ustring.match(eachArg, 'P%d+') == eachArg then
			propId = eachArg
		end
	end
	return propId, entId
end

local function wrapInLangSpan(text, lang)
	if not text then return nil end
	if not lang or lang == 'ro' then return text end
	local langSpan = mw.html.create('span'):attr('lang', lang):wikitext(text)
	return tostring(langSpan)
end

local function isClaimTrue(claim)
	return mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] >= mw.wikibase.entity.claimRanks.RANK_NORMAL
end
p.isClaimTrue = isClaimTrue

--- returns true if the claim has a better (or equal) rank than the threshhold specified
--- threshholdRank can be "normal", "preferred" etc.
local function hasBetterRank(claim, threshholdRank)
	return (not threshholdRank
			or mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] >= mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(threshholdRank)])
				and isClaimTrue(claim)
end
p.hasBetterRank = hasBetterRank

function p.descriptionIn(frame)
	local langcode = frame.args[1]
	local id = frame.args[2]
	-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
	local entity = mw.wikibase.getEntityObject(id)
	if entity then
		return entity.descriptions[langcode or lang.code].value
	end
	return ""
end

function p.getEntityId(frame)
	return mw.wikibase.getEntityIdForCurrentPage()
end

function p.labelIn(frame)
	local langcode = frame.args[1]
	local id = frame.args[2]
	-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
	local entity
	if id then
		entity = mw.wikibase.getEntityObject(id)
	else 
		entity = mw.wikibase.getEntityObject()
	end
	if entity then
		return entity.labels[langcode or lang.code] and entity.labels[langcode or lang.code].value
	end
	return ""
end

local function isValueSnak(snak)
	return snak and snak.snaktype == 'value'
end
p.isValueSnak = isValueSnak

local function hasValueSnak(claim)
	return claim and claim.type == 'statement' and isValueSnak(claim.mainsnak)
end
p.hasValueSnak = hasValueSnak

local function hasRankAtLeastNormal(claim)
	return claim and claim.rank and mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] > mw.wikibase.entity.claimRanks.RANK_DEPRECATED
end
p.claimHasRankAtLeastNormal = hasRankAtLeastNormal

local function computeLinkToItem(entityId, capitalize, callFunction, plainCallFunction)
	local qIfiedEntityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	local sitelink = mw.wikibase.sitelink(qIfiedEntityId)
	local label = nil
	local labelLang = nil
	local object = nil
	local out = ""
	local description = nil
	if plainCallFunction and type(plainCallFunction) == 'function' then
		label, labelLang = plainCallFunction(qIfiedEntityId)
	elseif callFunction and type(callFunction) == 'function' then
		object = mw.wikibase.getEntityObject(qIfiedEntityId)
		label, labelLang = callFunction(object)
	end
	if label == nil or (labelLang and labelLang ~= lang.code) then 
		label = mw.wikibase.label(qIfiedEntityId)
		description = mw.wikibase.description(qIfiedEntityId)
	end
	
	if not label and not labelLang then
		for _,eachLangProp in ipairs({'P103', 'P407', 'P37'}) do
			for __,eachLangClaim in ipairs(mw.wikibase.getBestStatements(qIfiedEntityId, eachLangProp)) do
				if hasValueSnak(eachLangClaim) then
					local langId = eachLangClaim.mainsnak.datavalue.value.id
					for ___,eachLangCodeClaim in ipairs(mw.wikibase.getBestStatements(langId, 'P424')) do
						if not labelLang and hasValueSnak(eachLangCodeClaim) then
							labelLang = eachLangCodeClaim.mainsnak.datavalue.value
						end
					end
					if labelLang then break end
				end
			end
			if labelLang then break end
		end
	end
	if not label and not labelLang then
		for _,eachCountryProp in ipairs({'P17', 'P27'}) do
			for __,eachCountry in ipairs(mw.wikibase.getBestStatements(qIfiedEntityId, eachCountryProp)) do
				if hasValueSnak(eachCountry) then
					for ___,eachOffLang in ipairs(mw.wikibase.getBestStatements(eachCountry.mainsnak.datavalue.value.id, 'P37')) do
						if hasValueSnak(eachOffLang) then
							for ____,eachLangCodeClaim in ipairs(mw.wikibase.getBestStatements(eachOffLang.mainsnak.datavalue.value.id, 'P424')) do
								if not labelLang and hasValueSnak(eachLangCodeClaim) then
									labelLang = eachLangCodeClaim.mainsnak.datavalue.value
								end
							end
							if labelLang then break end
						end
					end
					if labelLang then break end
				end
			end
		end
	end
	labelLang = labelLang or 'en'
	if label == nil and object ~= nil then
		label = object:getLabel(labelLang)
		description = object:getDescription(labelLang)
	elseif label == nil then
		label = mw.wikibase.getLabelByLang(qIfiedEntityId, labelLang)
		description = mw.wikibase.getDescription(qIfiedEntityId)
	end
	if not label then
		label = qIfiedEntityId
		labelLang = nil
	end
	if description then description = "(" .. description .. ")" end
	if label == qIfiedEntityId then
		out = "[[Categorie:Articole cu legături către elemente fără etichetă]] "
	elseif labelLang == 'en' then
		out = "[[Categorie:Articole cu legături către elemente fără etichetă în limba română]] "
	end
	
	if capitalize then
		label = lang:ucfirst(label)
	end
	if labelLang and labelLang ~= 'ro' then
		label = wrapInLangSpan(label, labelLang)
	end
	description = description or ""
	if sitelink then
		return out .. "[[:" .. sitelink .. "|" .. label .. "]]"
	else
		local coextensiveWithPageId = p.loadOneValueInChain({qIfiedEntityId, 'P3403', 'raw'})
		local listPageId = p.loadOneValueInChain({qIfiedEntityId, 'P2354', 'raw'})
		local listPageSitelink = listPageId and mw.wikibase.sitelink(listPageId) or coextensiveWithPageId and mw.wikibase.sitelink(coextensiveWithPageId)
		if listPageSitelink then return "[[:" .. listPageSitelink .. "|" .. label .. "]]" end
		return out .. "[[:d:" .. qIfiedEntityId .. "|" .. label .. "]]<abbr title='Articolul încă nu există în acest wiki'>[*]</abbr>" .. mw.getCurrentFrame():preprocess("{{invizibil|1=[[" .. label .." " .. description .. "|&#8203;]]}}")
	end
end

p.findLinkToItemWithLabel = function(entityId, label)
	local sitelink = mw.wikibase.sitelink(StringUtils._prependIfMissing({tostring(entityId), 'Q'}))
	if sitelink then
		return "[[:" .. sitelink .. "|" .. label .. "]]"
	else
		return computeLinkToItem(entityId, false, nil, function() return label end)
	end
end

p.findLinkToItemWithCallback = function(entityId, capitalize, callBack, plainCallBack)
	return computeLinkToItem(entityId, capitalize, callBack, plainCallBack)
end

p.findLinkToItemWithLabelFromAnyClaim = function(entityId, capitalize, propertyIdsTable)
	local callFunction = nil
	if propertyIdsTable then
		callFunction = function(object)
			local returnValue = nil
			for propIdIdx,propertyId in pairs(propertyIdsTable) do
				if object and object.claims and object.claims[propertyId] and returnValue == nil then
					for eachClaimIdx, eachClaim in pairs(object.claims[propertyId]) do
						if hasValueSnak(eachClaim) and eachClaim.mainsnak.datavalue then
							if eachClaim.mainsnak.datavalue.type == 'monolingualtext' then
								if eachClaim.mainsnak.datavalue.value.language == 'ro' then returnValue = eachClaim.mainsnak.datavalue.value.text end
							elseif eachClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
								returnValue = mw.wikibase.label(StringUtils._prependIfMissing({tostring(eachClaim.mainsnak.datavalue.value['numeric-id']), 'Q'}))
							elseif eachClaim.mainsnak.datavalue.type == 'string' then
								returnValue = eachClaim.mainsnak.datavalue.value
							end
						end
					end
				end
			end
			return returnValue
		end
	end
	return computeLinkToItem(entityId, capitalize, callFunction)
end

p.findLinkToItemWithLabelFromClaim = function(entityId, capitalize, propertyId)
	return p.findLinkToItemWithLabelFromAnyClaim(entityId, capitalize, { propertyId })
end

p.findLinkToItem = function(entityId, capitalize, feminine, shortestAlias)
	if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
	if capitalize == nil then capitalize = false end
	local label
	local returnedLang = nil
	local bestRanks = { ["ro"] = nil, ["en"] = nil }
	
	if shortestAlias then
		local returnedAlias = nil
		local bestShortNameClaims = mw.wikibase.getAllStatements(entityId, 'P1813')
		if bestShortNameClaims then
			local shortNameEn = nil
			for shortNameIdx,shortNameClaim in pairs(bestShortNameClaims) do
				if shortNameClaim.mainsnak.datavalue.value.language == 'en' and hasBetterRank(shortNameClaim, bestRanks["en"]) then 
					shortNameEn = shortNameClaim.mainsnak.datavalue.value.text
					bestRanks["en"] = shortNameClaim.rank
				end
				if shortNameClaim.mainsnak.datavalue.value.language == 'ro' and hasBetterRank(shortNameClaim, bestRanks["ro"]) then
					returnedAlias = shortNameClaim.mainsnak.datavalue.value.text
					bestRanks["ro"] = shortNameClaim.rank
				end
			end
			if returnedAlias then
				returnedLang = 'ro'
			elseif shortNameEn then
				returnedAlias = shortNameEn
				returnedLang = 'en'
			end
			returnedAlias = capitalize and StringUtils._capitalize({returnedAlias}) or returnedAlias
		end
		label = wrapInLangSpan(returnedAlias, returnedLang)
	end
	if feminine then
		local feminineForms = mw.wikibase.getBestStatements(entityId, 'P2521')
		if feminineForms then
			for _idx, eachFForm in pairs(feminineForms) do
				if hasValueSnak(eachFForm) and eachFForm.mainsnak.datatype == 'monolingualtext' and eachFForm.mainsnak.datavalue.type == 'monolingualtext' and eachFForm.mainsnak.datavalue.value and eachFForm.mainsnak.datavalue.value.language == 'ro' then
					label = eachFForm.mainsnak.datavalue.value.text
					returnedLang = eachFForm.mainsnak.datavalue.value.language
				end
			end
		end
	end
	return computeLinkToItem(entityId, capitalize, nil, function() return label, returnedLang end)
end

p.getLinkToItem = function(frame)
	local args = getArgs(frame, {frameOnly=true})
	local entityId = args[1]
	return p.findLinkToItem(entityId, false, false, false)
end

local function printRawValue(snak)
	if (isValueSnak(snak)) then
		if snak.datavalue.type == "wikibase-entityid" then
			return snak.datavalue.value.id
		elseif snak.datavalue.type == "time" then
			return snak.datavalue.value.time
		elseif snak.datavalue.type == "monolingualtext" then
			return snak.datavalue.value.text
		elseif snak.datavalue.type == "quantity" then
			return snak.datavalue.value.amount .. ' ' .. snak.datavalue.value.unit
		else
			return snak.datavalue.value
		end
	end
	return ''
end

local function snakToString(snak, feminine)
	if (isValueSnak(snak)) then
		if (snak.datavalue.type == "wikibase-entityid") then
			return p.findLinkToItem(snak.datavalue.value["numeric-id"], nil, feminine)
		end
		if (snak.datavalue.type == "time") then
			return formatDate(snak.datavalue.value.time, snak.datavalue.value.precision, snak.datavalue.value.timezone)
		end
		if (snak.datavalue.type == "monolingualtext") then
			return snak.datavalue.value.text
		end
		if (snak.datavalue.type == "quantity") then
			local unit = '1'
			local amount = snak.datavalue.value.amount
			local unitQ = mw.ustring.match(snak.datavalue.value.unit, 'Q%d+')
			if unitQ ~= nil then
				unit = p.findLanguageText('P5061', 'ro', unitQ) or p.findLanguageText('P5061', 'ro', unitQ)
				if not unit then 
					unit = p.findOneValueNoRef('P558', unitQ)
					if not unit or mw.ustring.len(unit) == 0 then
						unit = p.findLabel(unitQ, nil)
					end
				end
				unit = unit or '1'
			end
			if tonumber(amount) ~= nil then
				amount = mw.language.new(lang.code):formatNum(tonumber(amount))
			end
			
			return amount .. (unit ~= '1' and (mw.text.decode('&nbsp;') .. unit) or '')
		end
		return snak.datavalue.value
	end
	return ""
end

p.printSnak = function(snak)
	return snakToString(snak)
end

p.outputReferences = function (claim)
	local refMapping = {}
	local authorityPropertiesMap = {}
	local frame = mw.getCurrentFrame()
	
	refMapping["P1433"] = "publisher"
	refMapping["P123"] = "publisher"
	refMapping["P143"] = "publisher"
	refMapping["P1476"] = "contribution"
	refMapping["P854"] = "url"
	refMapping["P813"] = "accessdate"
	refMapping["P50"] = "author"
	refMapping["P577"] = "date"
	refMapping["P248"] = "title"
	refMapping["P304"] = "page"
	refMapping["P958"] = "contribution"
	refMapping["P1810"] = "contribution"
	
	authorityPropertiesMap['Q20666306'] = 'P268' -- BnF identifier
	authorityPropertiesMap['Q15241312'] = 'P646' -- Freebase identifier
	authorityPropertiesMap['Q36578'] = 'P227' -- GND identifier
	authorityPropertiesMap['Q37312'] = 'P345' -- IMDb identifier
	authorityPropertiesMap['Q278487'] = 'P662' -- PubChem identifier
	authorityPropertiesMap['Q1139587'] = 'P2334' -- Swedish film database identifier
	
	
	local primaryProperties = {}
	primaryProperties["P1433"] = "publisher"
	primaryProperties["P123"] = "publisher"
	primaryProperties["P1476"] = "contribution"
	primaryProperties["P248"] = "title"
	primaryProperties["P854"] = "url"
	primaryProperties["P50"] = "author"
	local out = ""
	local referencesCount = 0
	
	if claim then
		for ref in pairs(claim.references or {}) do
			local citationArguments = {}
			local citationArgumentsFound = false
			local refname = ""
			for snakkey, snakval in pairs(claim.references[ref].snaks) do
				if refMapping[snakkey] ~= nil then
					local snakData = {}
					for snakIndex,snakValElement in ipairs(snakval) do
						local formattedSnakValElement = snakToString(snakValElement)
						if isValueSnak(snakValElement) and snakValElement.datatype == 'time' then
							formattedSnakValElement = DateUtils.formatDate(DateUtils.parseWikidataDate(snakValElement.datavalue.value.time, snakValElement.datavalue.value.precision), false , true)
						elseif isValueSnak(snakValElement) and snakkey == 'P1476' and snakValElement.datatype == 'monolingualtext' and snakValElement.datavalue.value.language ~= 'ro' then
							citationArguments['language'] = snakValElement.datavalue.value.language
						end
						
						table.insert(snakData, formattedSnakValElement)
					end
					citationArguments[refMapping[snakkey]] = table.concat(snakData, ", ", 1, #snakData)
					refname = refname .. '_' .. refMapping[snakkey] .. '_' .. mw.ustring.gsub(mw.ustring.gsub(mw.text.nowiki(citationArguments[refMapping[snakkey]]), '&#%d+;', ''), ' ', '')
					if primaryProperties[snakkey] then
						citationArgumentsFound = true
					end
					
					if snakkey == 'P248' or snakkey == 'P143' then -- "stated in" or "imported from", can be an authority file
						if snakval[1].datavalue and snakval[1].datavalue.type == 'wikibase-entityid' then
							local statedInWbId = snakval[1].datavalue.value.id
							
							-- if Wikimedia project, ignore
							for _idx, eachStatedInInstanceOfClaim in pairs(mw.wikibase.getAllStatements(statedInWbId, 'P31')) do
								if eachStatedInInstanceOfClaim.mainsnak and eachStatedInInstanceOfClaim.mainsnak.datavalue and eachStatedInInstanceOfClaim.mainsnak.datavalue.type == 'wikibase-entityid' and 
								(eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 10876391 or eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 14827288) then
									citationArgumentsFound = false
									break
								end
							end
							for _idx, eachStatedInInstanceOfClaim in pairs(mw.wikibase.getAllStatements(statedInWbId, 'P1269')) do
								if eachStatedInInstanceOfClaim.mainsnak and eachStatedInInstanceOfClaim.mainsnak.datavalue and eachStatedInInstanceOfClaim.mainsnak.datavalue.type == 'wikibase-entityid' and 
								eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 16222597 then
									citationArgumentsFound = false
									break
								end
							end
							-- extract data about authority file
							local authorityWbEntityId = authorityPropertiesMap[StringUtils._prependIfMissing({statedInWbId, 'Q'})]
							if authorityWbEntityId then
								local authorityUrlFormatClaims = mw.wikibase.getBestStatements(authorityWbEntityId, 'P1630')
								if authorityUrlFormatClaims and 0 < #authorityUrlFormatClaims then
								 	local formatterUrl = snakToString(authorityUrlFormatClaims[1].mainsnak)
								 	local crtEntityId = mw.wikibase.getEntityIdForCurrentPage()
								 	if crtEntityId then
								 		local entityAuthIdClaims = mw.wikibase.getBestStatements(crtEntityId, authorityWbEntityId)
								 		if entityAuthIdClaims and 0 < #entityAuthIdClaims and hasValueSnak(entityAuthIdClaims[1]) then
								 			citationArguments['chapterurl'] = mw.ustring.gsub(formatterUrl, '%$1', entityAuthIdClaims[1].mainsnak.datavalue.value)
								 			citationArguments['chapter'] = p.labelIn({args = {'ro'}}) or p.labelIn({args = {'en'}})
							 			end
						 			end
				 				end
	 						elseif citationArgumentsFound == true then
	 							--extract URL from target entity
	 							local statedInUrl = nil
	 							-- the order of the array is important, as we only search the first occurence
	 							local onlinelinksprops = {'P953', 'P854', 'P856', 'P1581', 'P2397','P1065'}
								for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
									local linkId = p.findOneValueNoRef(eachOnlineLinkProp, statedInWbId)
									
									if linkId then
										local linkFormatter = p.findOneValueNoRef('P1630', eachOnlineLinkProp) or '$1'
										if linkFormatter then
											linkId = mw.ustring.gsub(linkId, '%%', '%%%%')
											statedInUrl = mw.ustring.gsub(linkFormatter, '$1', linkId)
											break
										else
											statedInUrl = linkId
											break
										end
									end
								end
	 							if statedInUrl then
	 								citationArguments[refMapping[snakkey]] = StringUtils._capitalize({p.findTitleOfWork(statedInWbId)})
	 								citationArguments['url'] = statedInUrl
	 							else
	 								local props = p.getEntityIdsList(statedInWbId, 'P1687')
	 								if props then for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
		 								for _,prop in ipairs(props) do
		 									if eachOnlineLinkProp == 'P' .. prop then 
		 										citationArguments[refMapping[snakkey]] = StringUtils._capitalize({p.findTitleOfWork(statedInWbId)})
		 										citationArguments['url'] = p.findOneValueNoRef(eachOnlineLinkProp) 
		 									end
		 									break
		 								end
		 							end end
						        end
					        end
			 			end
					end
				end
			end
			if citationArgumentsFound then
				
				--post-process citation arguments:
				--1. if publisher is missing and contribution and title are present, change title to publisher and contribution to title
				if citationArguments['publisher'] == nil and citationArguments['contribution'] and citationArguments['title'] then
					citationArguments['publisher'] = citationArguments['title']
					citationArguments['title'] = citationArguments['contribution']
					citationArguments['contribution'] = nil
				end
				--2. if contribution is present but title is not, change contribution to title
				if citationArguments['contribution'] and citationArguments['title'] == nil then
					citationArguments['title'] = citationArguments['contribution']
					citationArguments['contribution'] = nil
				end
				--3. if title is a wikilink, break it down and assign article link separately
				if citationArguments['title'] then 
					local titlink, titplain = mw.ustring.match(citationArguments['title'], '%[%[(.*)|(.*)%]%]')
					if titlink and titplain then
						if not citationArguments['url'] then citationArguments['titlelink'] = titlink end
						citationArguments['title'] = titplain
					end
				end

				local citationHash = mw.hash.hashValue('md5', refname)
				if referencesCount < 4 then
					if wdReferences[citationHash] then
						out = out .. frame:extensionTag{name="ref", args={name = citationHash}}
					else
						local refText = Citation.citationFromArgs(citationArguments, {CitationClass = 'citation'}, false)
						out = out .. frame:extensionTag("ref", refText, { name = citationHash })
						wdReferences[citationHash] = true
					end
					referencesCount = referencesCount + 1
				end
			end
		end
	end
	return out
end

p.getValueList = function(entityId, propertyId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil end
	local claims = mw.wikibase.getAllStatements(entityId, propertyId)
	if not claims then return nil end
	if #claims == 0 then return {} end
	local rez = {}
	local feminine = false
	local genders = mw.wikibase.getBestStatements(entityId, 'P21')
	if genders and #genders > 0 and genders[1] and hasValueSnak(genders[1]) and genders[1].mainsnak.datatype == 'wikibase-item' and genders[1].mainsnak.datavalue.type == 'wikibase-entityid' and genders[1].mainsnak.datavalue.value['numeric-id'] == 6581072 then
		feminine = true
	end
	for k, v in pairs(claims) do
		if hasValueSnak(v) then
			table.insert(rez, snakToString(v.mainsnak, feminine) .. p.outputReferences(v))
		end
	end
	return rez
end

local function findTimestampInTable(tbl)
	local crtEntry = ''
	if tbl then
		if tbl['P585'] then
			crtEntry = crtEntry .. snakToString(tbl['P585'][1].mainsnak or tbl['P585'][1])
		elseif tbl['P580'] then
			if tbl['P582'] then
				local starttime = snakToString(tbl['P580'][1].mainsnak or tbl['P580'][1])
				local endtime = snakToString(tbl['P582'][1].mainsnak or tbl['P582'][1])
				if starttime == endtime then
					crtEntry = crtEntry .. starttime
				else
					crtEntry = crtEntry .. starttime .. mw.text.decode('&ndash;', true) .. endtime
				end
			else 
				crtEntry = crtEntry .. 'din ' .. snakToString(tbl['P580'][1].mainsnak or tbl['P580'][1])
			end
		elseif tbl['P582'] then
			local endtime = DateUtils.extractDateFromWikidataSnak(tbl['P582'][1].mainsnak or tbl['P582'][1])
			if endtime then
				crtEntry = crtEntry .. 'până ' .. ((endtime.precision == 9 or endtime.precision == 10) and 'în' or 'la') .. ' ' .. DateUtils.formatDate(endtime)
			end
		end
	end
	return crtEntry
end

p.printTimestampForClaim = function(claim)
	if claim and claim.qualifiers then
		return tostring(mw.html.create('small'):wikitext(' (' .. findTimestampInTable(claim.qualifiers) .. ')'))
	end
	return ''
end

p.findTimestamp = function(entityId)
	if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
	if type(entityId) == 'table' then entityId = entityId.id end
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	
	if entityId then
		local tbl = {}
		for _,propId in ipairs({'P580', 'P582', 'P585'}) do
			local propClaims = mw.wikibase.getAllStatements(entityId, propId)
			if propClaims and #propClaims > 0 then
				tbl[propId] = propClaims
			end
		end
		return findTimestampInTable(tbl)
	end
	return ''
end

p.getTimestamp = function(frame)
	return p.findTimestamp(frame and frame.args and frame.args[1] or nil)
end


p.findValueListWithQualifiersInBrackets = function(entityId, propertyId, best, bracketTemplates, seps)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if (type(bracketTemplates) == 'string') then
		bracketTemplates = { bracketTemplates }
	end
	seps = seps or {}
	table.insert(seps, 1, '')
	if #seps < #bracketTemplates then for idx=#seps + 1,#bracketTemplates do table.insert(seps, ', ') end end
	if propertyId == nil or entityId == nil then return nil end
	local claims
	
	local qualProps = {}
	for _,bracketTemplate in ipairs(bracketTemplates) do
		mw.ustring.gsub(bracketTemplate, '%$(P%d+)', function(qualProp) 
			table.insert(qualProps, qualProp)
			return nil
		end)
	end
	
	if best then
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	else
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	local rez = {}
	if claims and claims[1] then
		local rawclaims = {}
		for k, v in pairs(claims) do
			if hasValueSnak(v) and hasRankAtLeastNormal(v) then
				table.insert(rawclaims, v)
			end
		end
		for k, v in pairs(rawclaims) do
			local crtEntry = snakToString(v.mainsnak)
			if v.qualifiers and #qualProps > 0 then
				local qualPropValues = {}
				for _,eachQualProp in pairs(qualProps) do
					if v.qualifiers[eachQualProp] then
						local qualPropValuesList = {}
						for __,eachQualSnak in ipairs(v.qualifiers[eachQualProp]) do
							if isValueSnak(eachQualSnak) then
								table.insert(qualPropValuesList, snakToString(eachQualSnak))
							end
						end
						qualPropValues[eachQualProp] = mw.text.listToText(qualPropValuesList, ', ', ' și ')
					else
						qualPropValues[eachQualProp] = ''
					end
				end
				local bracketData = {}
				local usedSeps = {}
				for bracketTemplateIdx,bracketTemplate in ipairs(bracketTemplates) do
					local substedBracketTemplate = mw.ustring.gsub(bracketTemplate, '%$(P%d+)', function(qualProp) 
						return qualPropValues[qualProp]
					end)
					if substedBracketTemplate ~= bracketTemplate and substedBracketTemplate ~= "" then
						bracketData[bracketTemplateIdx] = substedBracketTemplate
						usedSeps[bracketTemplateIdx] = seps[bracketTemplateIdx]
					end
				end
				bracketData = TableTools.compressSparseArray(bracketData)
				usedSeps = TableTools.compressSparseArray(usedSeps)

				local bracketText = ''
				for bracketDatumIdx=1,#bracketData do
					bracketText = bracketText .. (bracketDatumIdx == 1 and '' or usedSeps[bracketDatumIdx])
					bracketText = bracketText .. bracketData[bracketDatumIdx]
				end
				crtEntry = StringUtils._appendToString({crtEntry, tostring(mw.html.create('small'):wikitext(StringUtils._encloseString({bracketText, '&#32;(', ')'})))})
			end
			
			crtEntry = crtEntry .. p.outputReferences(v)
			table.insert(rez, crtEntry)
		end
	end
	return rez
end	

p.getTimestampedValueList = function(entityId, propertyId, best)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil end
	local claims
	if best then
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	else
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	local rez = {}
	if claims and claims[1] then
		local rawclaims = {}
		for k, v in pairs(claims) do
			if hasValueSnak(v) then
				table.insert(rawclaims, v)
			end
		end
		for k, v in pairs(rawclaims) do
			local crtEntry = snakToString(v.mainsnak)
			crtEntry = crtEntry .. p.printTimestampForClaim(v)
			crtEntry = crtEntry .. p.outputReferences(v)
			table.insert(rez, crtEntry)
		end
	end
	return rez
end	

p._getValueListWithSeparator = function(funcArgs)
	local separator = mw.text.trim(funcArgs[1] or ", ")
	local propertyId
	local entityId
	propertyId, entityId = propAndEntity(funcArgs[2], funcArgs[3])
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	local limit = funcArgs[4] or funcArgs['limit']
	local limitVal = 0
	if limit and limit == mw.ustring.match(limit, '%d+') then
		limitVal = tonumber(limit)
	end
	local values = p.getValueList(entityId, propertyId)
	if not values then return nil end
	local extraSpec = nil
	if 0 < limitVal and #values > limitVal + 1 then
		local hiddenVals = #values - limitVal
		while #values > limitVal do
			table.remove(values, limitVal + 1)
		end
		extraSpec = '[[:d:' .. entityId .. '#' .. propertyId .. "|...''încă " .. tostring(limitVal) .. "'']]"
		table.insert(values, extraSpec)
	end
	return values and table.concat(values, separator)
end

p.getValueListWithSeparator = function(frame)
	local funcArgs = getArgs(frame, {frameOnly = true})
	return p._getValueListWithSeparator(funcArgs)
end

p.getValue = function(frame)
	local args = getArgs(frame)
	return p._getValueListWithSeparator({', ', args[1], mw.wikibase.getEntityIdForCurrentPage()})
end

p._getTimestampedValueListWithSeparator = function(funcArgs)
	local separator = funcArgs[1] or ", "
	local propertyId = funcArgs[2]
	local entityId = funcArgs[3]
	local best = funcArgs['best'] == 'y' or funcArgs['best'] == 'yes'
	local values = p.getTimestampedValueList(entityId, propertyId, best)
	return values and table.concat(values, separator)
end

p.getTimestampedValueListWithSeparator = function(frame)
	local funcArgs = getArgs(frame, {frameOnly = true})
	return p._getTimestampedValueListWithSeparator(funcArgs)
end

local function extractWikidataIdsFromClaimList(claims)
	local rez = {}
	if claims and #claims > 0 then
		for k, v in pairs(claims) do
			if hasValueSnak(v) and v.mainsnak.datavalue.type == 'wikibase-entityid' then
				table.insert(rez, v.mainsnak.datavalue.value['numeric-id'])
			end
		end
	end
	return rez
end

p.getEntityIdsList = function(entityId, propertyId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil end
	local claims = nil
	if entityId then
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	return extractWikidataIdsFromClaimList(claims)
end

p.getBestEntityIdsList = function(entityId, propertyId)
	local claims = nil
	if propertyId == nil then return nil end
	if type(entityId) == 'table' then
		claims = entityId:getBestStatements(propertyId)
	else
		if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
		if type(propertyId) == 'number' then propertyId = StringUtils._prependIfMissing({tostring(propertyId), 'P'}) end
		entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
		if propertyId == nil or entityId == nil then return nil end
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	end
	if not claims then return nil end
	return extractWikidataIdsFromClaimList(claims)
end

local timestampComparator = function(p1, p2)
	if p1 and p2 then
		local q1 = p1.qualifiers
		local q2 = p2.qualifiers
		local d1 = nil
		local d2 = nil
		if q1 and q2 then
			if q1['P580'] and q1['P580'][1] and q1['P580'][1].datavalue then
				d1 = p.extractDateFromClaim(q1['P580'][1])
			end
			if q2['P580'] and q2['P580'][1] and q2['P580'][1].datavalue then
				d2 = p.extractDateFromClaim(q2['P580'][1])
			end
			if d1 and d2 then return DateUtils.compare(d1, d2) > 0
			elseif d1 then return true
			elseif d2 then return false end
		elseif q1 then return true
		elseif q2 then return false
		end
	else
		if p1 then return true end
		if p2 then return false end
	end
	return false
end

p.findBestClaimsForProperty = function(entity, propertyId)
	if propertyId == nil then return nil end
	if entity == nil then entity = mw.wikibase.getEntityIdForCurrentPage() end
	if type(entity) == 'table' then return entity:getBestStatements(propertyId) end
	if type(entity) == 'number' then entity = StringUtils._prependIfMissing({tostring(entity), 'Q'}) end
	if entity == nil then return nil end
	return mw.wikibase.getBestStatements(entity, propertyId)
end

p.findClaimsForProperty = function(entity, propertyId)
	if propertyId == nil then return nil end
	if entity == nil then entity = mw.wikibase.getEntityIdForCurrentPage() end
	if type(entity) == 'table' and entity.claims then return entity.claims[propertyId] end
	if type(entity) == 'number' then entity = StringUtils._prependIfMissing({tostring(entity), 'Q'}) end
	if entity == nil then return nil end
	return mw.wikibase.getAllStatements(entity, propertyId)
end

p.findSortedClaimsForProperty = function(entity, propertyId)
	local rawClaims = p.findClaimsForProperty(entity, propertyId)
	if rawClaims == nil then return {} end
	
	table.sort(rawClaims, timestampComparator)
	return rawClaims
end

p.findQualifierValueListForClaim = function(claim, qualifierId)
	if claim == nil or claim.qualifiers == nil or qualifierId == nil then return {} end
	local quals = {}
	local wikidataQualifiers = claim.qualifiers[qualifierId]
	if wikidataQualifiers then
		for qualK, qualV in pairs(wikidataQualifiers) do
			if isValueSnak(qualV) then
				table.insert(quals, snakToString(qualV))
			end
		end
	end
	return quals
end

p.getQualifierValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local entity = mw.wikibase.getEntity()
	if entity and entity.claims ~= nil and entity.claims[propertyID] ~= nil then
		local out = {}
		for k, v in pairs(entity.claims[propertyID]) do
			if v.qualifiers and v.qualifiers[qualifierID] then
				for k2, v2 in pairs(v.qualifiers[qualifierID]) do
					if isValueSnak(v2) == 'value' then
						table.insert(out, snakToString(v2))
					end
				end
			end
		end
		return table.concat(out, ", ")
	else
		return ""
	end
end

-- This is used to get a value like 'male' (for property p21) which won't be linked and numbers without the thousand separators
p.getRawValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local input_parm = mw.text.trim(frame.args[2] or "")
	local entity = mw.wikibase.getEntityObject()
	local claims = nil
	if entity and entity.claims then
		claims = entity.claims[propertyID]
	end
	if claims then
		local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
	
		-- if number type: remove thousand separators
		if (hasValueSnak(claims[1]) and claims[1].mainsnak.datavalue.type == "quantity") then
			result = mw.ustring.gsub(result, "(%d),(%d)", "%1%2")
		end
		return result
	else
		return ""
	end
end

p.getRawQualifierValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local input_parm = mw.text.trim(frame.args[3] or "")
	if input_parm == "FETCH_WIKIDATA" then
		local entity = mw.wikibase.getEntity()
		if entity.claims[propertyID] ~= nil then
			local out = {}
			for k, v in pairs(entity.claims[propertyID]) do
				for k2, v2 in pairs(v.qualifiers[qualifierID]) do
					if isValueSnak(v2) then
						if v2.datavalue.value["numeric-id"] then
							out[#out + 1] = mw.wikibase.label("Q" .. v2.datavalue.value["numeric-id"])
						else
							out[#out + 1] = v2.datavalue.value
						end
					end
				end
			end
			local ret = table.concat(out, ", ")
			return string.upper(string.sub(ret, 1, 1)) .. string.sub(ret, 2)
		else
			return ""
		end
	else
		return input_parm
	end
end

p.extractDateFromClaim = function(claimOrSnak)
	local claim
	if claimOrSnak.mainsnak then claim = claimOrSnak.mainsnak else claim = claimOrSnak end
	if claim.datatype == 'time' and isValueSnak(claim) then
		local d = {}
		local iSOTimeSign = mw.ustring.sub(claim.datavalue.value.time, 1, 1)
		d.precision = tonumber(claim.datavalue.value.precision)
		d.calendarmodel = claim.datavalue.value.calendarmodel
		wdDate = DateUtils.parseWikidataDate(claim.datavalue.value.time, d.precision)
		if wdDate ~= nil then
			d.month = wdDate.month
		    d.day = wdDate.day
		else
			d.month = 1
			d.day = 1
		end
		if d.precision <= 9 then
			d.year = tonumber(mw.ustring.match(claim.datavalue.value.time, "%d+"))
			if iSOTimeSign == '-' then d.year = -d.year end
		elseif wdDate ~= nil then
			d.year = wdDate.year
		else
			d.year = 1
		end
		d.claim = claimOrSnak
		return d
	end
	return nil
end

p.findDateValues = function(propertyId, entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	
	if entityId then
		local bestclaims = mw.wikibase.getBestStatements(entityId, propertyId)
		local bestdates = {}
		
		if bestclaims then for k, v in pairs(bestclaims) do
			if hasValueSnak(v) and v.mainsnak.datatype == 'time' then
				local d = p.extractDateFromClaim(v)
				table.insert(bestdates, d)
			elseif hasValueSnak(v) then
				local d = {}
				d.claim = v
				table.insert(bestdates, d)
			end
		end end
		return bestdates
	end
end

-- This is used to get a date value for date_of_birth (p569), etc. which won't be linked -- consolidate by testing if entity.claims[propertyID].mainsnak.datavalue.type is "time"
-- Dates are stored as 28 characters if the year  >99 -- e.g. +00000002014-01-01T00:00:00Z for 2014
-- Dates are stored as 26 characters if the year =<99 -- e.g. +000000050-01-01T00:00:00Z   for 50 CE
p.getDateValue = function(frame)
	local args = getArgs(frame, { frameOnly = true })
	local propertyId = args[1]
	local entityId = args[2] or mw.wikibase.getEntityIdForCurrentPage()
	if entityId then
		local claims = mw.wikibase.getAllStatements(entityId, propertyId)
		local out = {}
		if claims then for _,eachClaim in pairs(claims) do
			if hasValueSnak(eachClaim) then
				local extractedDate = p.extractDateFromClaim(eachClaim)
				table.insert(out, GregorianDate.displayDualDateIfInInterval(extractedDate, true))
			end
		end end
		return table.concat(out, ", ")
	else
		return ""
	end
end

p.getQualifierDateValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local input_parm = mw.text.trim(frame.args[3] or "")
	local date_format = mw.text.trim(frame.args[4] or "dmy")
	if input_parm == "FETCH_WIKIDATA" then
		local entity = mw.wikibase.getEntity()
		if entity.claims[propertyID] ~= nil then
			local out = {}
			local dt = {}
			for k, v in pairs(entity.claims[propertyID]) do
				for k2, v2 in pairs(v.qualifiers[qualifierID]) do
					if isValueSnak(v2) then
						local d = v2.datavalue.value.time
						if #d > 26 then
							dt.year = string.sub(d, 9, 12)
							dt.month = string.sub(d, 14, 15)
							dt.day = string.sub(d, 17, 18)
						else
							dt.year = string.sub(d, 9, 10)
							dt.month = string.sub(d, 12, 13)
							dt.day = string.sub(d, 15, 16)
						end
						if date_format == "mdy" then
							out[#out + 1] = os.date("%B %e, %Y", os.time(dt))
						elseif date_format == "my" then
							out[#out + 1] = os.date("%B %Y", os.time(dt))
						elseif date_format == "y" then
							out[#out + 1] = os.date("%Y", os.time(dt))
						else
							out[#out + 1] = os.date("%e %B %Y", os.time(dt))
						end
					end
				end
			end
			return table.concat(out, ", ")
		else
			return ""
		end
	else
		return input_parm
	end
end

-- returns the value of the specified qualifier for the specified preferred value of the property of the specified entity (or the current entity)
p.findQualifierForPreferredPropertyValue = function(entityId, propertyId, qualifierId, separator)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return nil end
	local claims = mw.wikibase.getAllStatements(entityId, propertyId)
	local out = {}
	if claims and claims[1] then
		for claimK, claimV in pairs(claims) do
			if claimV.rank == 'preferred' then
				local qual = claimV.qualifiers[qualifierId]
				if qual and qual.mainsnak then table.insert(out, snakToString(qual)) end
			end
		end
	end
	return table.concat(out, separator)
end

p.getQualifierForPreferredPropertyValue = function(frame)
	local args = getArgs(frame)
	local entityId = args['entityId'] or args[1]
	local propertyId = args['propertyId'] or args[2]
	local qualifierId = args['qualifierId'] or args[3]
	local separator = args['separator'] or args[4] or ', '
	
	return p.findQualifierForPreferredPropertyValue(entityId, propertyId, qualifierId, separator)
end

-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm
-- uses the newer mw.wikibase calls instead of directly using the snaks
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string
p.getTAValue = function(frame)
	local ent = mw.wikibase.getEntityObject()
	local props = ent:formatPropertyValues('P1323')
	local out = {}
	local t = {}
	for k, v in pairs(props) do
		if k == 'value' then
			t = mw.text.split( v, ", ")
			for k2, v2 in pairs(t) do
				out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"
			end
		end
	end
	ret = table.concat(out, "<br> ")
	if #ret == 0 then
		ret = "Invalid TA"
	end
	return ret
end

-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
p.pageId = p.getEntityId

-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
-- use these as the second parameter and this function instead of the built-in "pairs" function
-- to iterate over all qualifiers and snaks in the intended order.
local function orderedpairs(array, order)
	if not order then return pairs(array) end
 
	-- return iterator function
    local i = 0
    return function()
        i = i + 1
        if order[i] then
            return order[i], array[order[i]]
        end
    end	
end

-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
local function normalizeDate(date)
	date = mw.text.trim(date, "+")
	-- extract year
	local yearstr = mw.ustring.match(date, "^\-?%d+")
	local year = tonumber(yearstr)
	-- remove leading zeros of year
	return year .. mw.ustring.sub(date, #yearstr + 1), year
end

function formatDate(indate, precision, timezone)
	precision = precision or 11
	date, year = normalizeDate(indate)
	if year == 0 and precision <= 9 then return "" end
 
 	-- precision is 10000 years or more
	if precision <= 5 then
		local factor = 10 ^ ((5 - precision) + 4)
		local y2 = math.ceil(math.abs(year) / factor)
		local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
		if year < 0 then
			relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
		else
			relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
		end			
		return relative
	end
 
 	-- precision is decades, centuries and millennia
	local era
	if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
	if era then
		if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
		elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
		return era
	end
 
 	if precision >= 7 then
 		return DateUtils.formatDate(DateUtils.parseWikidataDate(indate, precision))
 	end
end

local function printDatavalueEntity(data, parameter)
	-- data fields: entity-type [string], numeric-id [int, Wikidata id]
	local id = "Q" .. data["numeric-id"]
	if parameter then
		if parameter == "link" then
			return "[[" .. (mw.wikibase.sitelink(id) or (":d:" .. id))  .. "|" ..  (mw.wikibase.label(id) or id)  .. "]]"
		else
			return data[parameter]
		end
	else
		if data["entity-type"] == "item" then return mw.wikibase.label("Q" .. data["numeric-id"]) or id else printError("unknown-entity-type") end
	end
end

local function printDatavalueTime(data, parameter)
	-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
	--   precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
	--   calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar]
	if parameter then
		if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
		elseif parameter == "time" then data.time = normalizeDate(data.time) end
		return data[parameter]
	else
		return formatDate(data.time, data.precision, data.timezone)
	end
end

function findClaims(entity, property)
	if not property or not entity or not entity.claims then return end
 
	if mw.ustring.match(property, "^P%d+$") then
		-- if the property is given by an id (P..) access the claim list by this id
		return entity.claims[property]
	else
		property = mw.wikibase.resolvePropertyId(property)
		if not property then return end

		return entity.claims[property]
	end
end

function getSnakValue(snak, parameter)
	-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data
	if snak.snaktype == "novalue" then return i18n["novalue"]
	elseif snak.snaktype == "somevalue" then return i18n["somevalue"]
	elseif snak.snaktype ~= "value" then return nil, printError("unknown-snak-type")
	end
 
	-- call the respective snak parser
	if snak.datavalue.type == "string" then return snak.datavalue.value
	elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
	else return nil, printError("unknown-datavalue-type")
	end
end
 
function getQualifierSnak(claim, qualifierId)
	-- a "snak" is Wikidata terminology for a typed key/value pair
	-- a claim consists of a main snak holding the main information of this claim,
	-- as well as a list of attribute snaks and a list of references snaks
	if qualifierId then
		-- search the attribute snak with the given qualifier as key
		if claim.qualifiers then
			local qualifier = claim.qualifiers[qualifierId]
			if qualifier then return qualifier[1] end
		end
		return nil, printError("qualifier-not-found")
	else
		-- otherwise return the main snak
		return claim.mainsnak
	end
end
 
function getValueOfClaim(claim, qualifierId, parameter)
	local error
	local snak
	snak, error = getQualifierSnak(claim, qualifierId)
	if snak then
		return getSnakValue(snak, parameter)
	else
		return nil, error
	end
end

function p.claim(frame)
	local property = frame.args[1] or ""
	local id = frame.args["id"]	-- "id" must be nil, as access to other Wikidata objects is disabled in Mediawiki configuration
	local qualifierId = frame.args["qualifier"]
	local parameter = frame.args["parameter"]
	local list = frame.args["list"]
	local references = frame.args["references"]
	local showerrors = frame.args["showerrors"]
	local default = frame.args["default"]
	if default then showerrors = nil end
 
	-- get wikidata entity
	local entity = mw.wikibase.getEntityObject(id)
	if not entity then
		if showerrors then return printError("entity-not-found") else return default end
	end
	-- fetch the first claim of satisfying the given property
	local claims = findClaims(entity, property)
	if not claims or not claims[1] then
		if showerrors then return printError("property-not-found") else return default end
	end
 
	-- get initial sort indices
	local sortindices = {}
	for idx in pairs(claims) do
		sortindices[#sortindices + 1] = idx
	end
	-- sort by claim rank
	local comparator = function(a, b)
		local rankmap = { deprecated = 2, normal = 1, preferred = 0 }
		local ranka = rankmap[claims[a].rank or "normal"] ..  string.format("%08d", a)
		local rankb = rankmap[claims[b].rank or "normal"] ..  string.format("%08d", b)
		return ranka < rankb
 	end
	table.sort(sortindices, comparator)
 
	local result
	local error
	if list then
		local value
		-- iterate over all elements and return their value (if existing)
		result = {}
		for idx in pairs(claims) do
			local claim = claims[sortindices[idx]]
			value, error =  getValueOfClaim(claim, qualifierId, parameter)
			if not value and showerrors then value = error end
			if value and references then value = value .. p.outputReferences(claim) end
			result[#result + 1] = value
		end
		result = table.concat(result, list)
	else
		-- return first element	
		local claim = claims[sortindices[1]]
		result, error = getValueOfClaim(claim, qualifierId, parameter)
		if result and references then result = result .. p.outputReferences(claim) end
	end
 
	if result then return result else
		if showerrors then return error else return default end
	end
end

p.getPreferredValue = function(frame)
	local args = getArgs(frame)
	local propertyID = args[1] or args['propertyId'] or args['pid']
	local entityID = args[2] or args['entityId'] or args['qid'] or mw.wikibase.getEntityIdForCurrentPage()
	if entityID == nil or propertyID == nil then return nil end
	local claims = mw.wikibase.getAllStatements(entityID, propertyID)
	if claims then
		-- if wiki-linked value output as link if possible
		if hasValueSnak(claims[1]) then
			local out = {}
			for k, v in pairs(claims) do
				if (v.rank == 'preferred') then
					local snakText = snakToString(v.mainsnak) .. p.outputReferences(v)

					if snakText ~= "" then
						out[#out + 1] = snakText
					end
				end
			end
			return table.concat(out, ", ")
		end
	else
		return ""
	end
	return ""
end


-- look into entity object
function p.ViewSomething(frame)
	local data = mw.wikibase.getEntityObject()
	if not data then
		return nil
	end

	local f = frame.args[1] and frame or frame:getParent()

	local i = 1
	while true do
		local index = f.args[i]
		if not index then
			return tostring(data)
		end
		
		data = data[index] or data[tonumber(index)]
		if not data then
			return
		end
		
		i = i + 1
	end
end

function p.Dump(frame)
	local data = mw.wikibase.getEntityObject()
	if not data then
		return i18n.warnDump
	end

	local f = frame.args[1] and frame or frame:getParent()

	local i = 1
	while true do
		local index = f.args[i]
		if not index then
			return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump
		end

		data = data[index] or data[tonumber(index)]
		if not data then
			return i18n.warnDump
		end

		i = i + 1
	end
end

-- Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din alte module
function p.findOneValue(propertyID, entity)
	local entityId = nil
	if type(entity) == 'table' then entityId = entity.id
	elseif type(entity) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entity), 'Q'})
	elseif type(entity) == 'string' then entityId = entity
	end
		
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local claims = mw.wikibase.getBestStatements(entityId, propertyID)
	if claims and 0 < #claims then
		for _,eachClaim in ipairs(claims) do
			if hasValueSnak(eachClaim) then
				return snakToString(eachClaim.mainsnak) .. p.outputReferences(eachClaim)
			end
		end
	end
	return nil
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt pentru o proprietate specificată
-- De apelat din alte module
function p.findOneValueNoRef(propertyID, entity)
	local claims = nil
	if type(entity) == 'table' then
		claims = entity:getBestStatements(propertyID)
	else
		if type(entity) == 'number' then entity = 'Q' .. tostring(entity) end
		entity = entity or mw.wikibase.getEntityIdForCurrentPage()
		if not entity then return nil end
		claims = mw.wikibase.getBestStatements(entity, propertyID)
	end
	if claims and 0 < #claims then
		for _,eachClaim in ipairs(claims) do
			if hasValueSnak(eachClaim) then
				return snakToString(eachClaim.mainsnak)
			end
		end
	end
	return nil
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt pentru o proprietate specificată
-- De apelat din formate
p.getOneValueNoRef = function(frame)
	local propertyID, entityID
	propertyID, entityID = propAndEntity(frame.args[1], frame.args[2])
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end

	if entity == nil then return nil end
	return p.findOneValueNoRef(propertyID, entity)
end

-- Returnează o singură valoare din cele cu rangul cel mai înalt și cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din formate
p.getOneValue = function(frame)
	local args = getArgs(frame)
	local propertyID, entityID
	propertyID, entityID = propAndEntity(args[1], args[2])
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	
	if entity == nil then return nil end
	return p.findOneValue(propertyID, entity)
end

local function findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId)
	if entity == nil then
		entity = mw.wikibase.getEntityObject()
	end
	if entity == nil then return '' end
	local claims = entity:getBestStatements(propertyId)
	if claims then
		if claims[1] then
			if claims[1].qualifiers then
				if claims[1].qualifiers[qualifierId] then
					local allQualifiers = {}
					for qk, qv in pairs(claims[1].qualifiers[qualifierId]) do
						table.insert(allQualifiers, qv)
					end
					local qIndex = 1
					local maxQTime = nil
					while maxQTime == nil and qIndex <= #allQualifiers do
						if allQualifiers[qIndex].datatype == 'time' then
							maxQTime = allQualifiers[qIndex]
						end
						qIndex = qIndex + 1
					end
					if maxQTime ~= nil then
						for i=qIndex,#allQualifiers do
							local msCrtQTime = tonumber(mw.language.new(lang.code):formatDate('U', allQualifiers[i].datavalue.value.time, nil))
							local msMaxQTime = tonumber(mw.language.new(lang.code):formatDate('U', maxQTime.datavalue.value.time, nil))
							if msCrtQTime > msMaxQTime then
								maxQTime = allQualifiers[i]
							end
						end
						if isValueSnak(maxQTime) then
							return formatDate(maxQTime.datavalue.value.time, maxQTime.datavalue.value.precision, maxQTime.datavalue.value.timezone)
						end
					end
					return ""
				else
					return ""
				end
			else
				return ""
			end
		else
			return ""
		end
	else 
		return ""
	end
end

-- Returnează valoarea cea mai recentă a qualifierului specificat pentru proprietea specificată a entității curente
-- De apelat din alte module
p.getLatestQualifierDateValueForOneProperty = function(frame)
 	local propertyId = mw.text.trim(frame.args[1] or "")
	local qualifierId = mw.text.trim(frame.args[2] or "")
	local entity = mw.wikibase.getEntityObject()
	
	return findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId)
end

-- Returnează valoarea cea mai recentă a qualifierului specificat pentru proprietea specificată a entității curente
-- De apelat din formate
p.findLatestQualifierDateValueForOneProperty = function(propertyId, qualifierId)
	local entity = mw.wikibase.getEntityObject()
	return entity ~= nil and findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId) or ''
end

-- Returnează un text ce conține referința pentru o proprietate; de apelat din formate
p.getReferenceForOneProperty = function(frame)
	local entity = mw.wikibase.getEntityObject()
	local propertyId = mw.text.trim(frame.args[1])
	local claims = entity:getBestStatements(propertyId)

	return p.outputReferences(claims[1])
end

-- Returnează o listă cu obiecte value asociate unei proprietăți a unei entități
-- Creată pentru a obține lista de coordonate a unui oraș în formă pură (listă cu obiectele value asociate)
-- Funcționează pentru: coordonate
-- TODO - de extins la mai multe tipuri de date, de exemplu texte multilingve, date calendaristice, itemuri wikibase
p.findDataValueObjects = function(entityId, propertyId)
	if propertyId == nil then return {} end
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return {} end
	local ret = {}
	local claims = mw.wikibase.getAllStatements(entityId, propertyId)
	if claims == nil then return ret end
	for claimIdx = 1, #claims do
		local snak = claims[claimIdx].mainsnak
		if isValueSnak(snak) then
			if snak.datatype == 'globe-coordinate' then
				table.insert(ret, snak.datavalue.value)
			end
		end
	end
	
	return ret		
end

-- Returnează labelul unei entități în limba specificată; dacă nu se găsește, atunci se încearcă în limba wikiului și apoi în limba engleză
p.findLabel = function(entityId, languageCode, default)
	local entity, ret
	if type(entityId) == 'string' then 
		ret = mw.wikibase.label(entityId)
		if ret then return ret end
		entity = mw.wikibase.getEntityObject(entityId)
	else
		entity = entityId
	end
	if entityId == nil then 
		entity = mw.wikibase.getEntityObject()
	end
	if not entity then return entityId or default or '' end
	if not languageCode or languageCode == '' then languageCode = lang.code end
	ret = entity:getLabel(languageCode) or entity:getLabel(lang.code) or default or entity:getLabel('en')
	return ret
end

p.getLabel = function(frame)
	local args = getArgs(frame, {frameOnly=true})
	local entityId = args[1]
	local languageCode = args[2] or lang.code
	local default = args['default']
	return p.findLabel(entityId, languageCode, default)
end

function formatCoordinates(entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return '' end
	local coordClaims = mw.wikibase.getAllStatements(entityId, 'P625')
	local formattedCoordsList = {}
	if coordClaims and coordClaims[1] then
		for coordIdx=1,#coordClaims do
			if coordClaims[coordIdx].mainsnak.datatype == 'globe-coordinate' then
				local latDir = coordClaims[coordIdx].mainsnak.datavalue.value.latitude < 0 and 'S' or 'N'
				local lonDir = coordClaims[coordIdx].mainsnak.datavalue.value.longitude < 0 and 'V' or 'E'
				local coordsText = mw.getCurrentFrame():expandTemplate{title='Coord', args={tostring(math.abs(coordClaims[coordIdx].mainsnak.datavalue.value.latitude)), latDir, tostring(math.abs(coordClaims[coordIdx].mainsnak.datavalue.value.longitude)), lonDir}}

				table.insert(formattedCoordsList, coordsText)
			end
		end
	end
	return table.concat(formattedCoordsList, ', ')
end

p.getFormattedCoordinates = function(frame)
	local origArgs = getArgs(frame)
	return formatCoordinates(origArgs[1] or origArgs['entityId'])
end

-- Returnează o numărul de valori cu cel mai mare rang pentru o proprietate specificată
-- De apelat din alte module
function p.countBestValues(propertyID, entity)
	if type(entity) == 'table' then
		entity = entity.id
	elseif type(entity) == 'number' then
		entity = 'Q' .. tostring(entity)
	elseif entity == nil then
		entity = mw.wikibase.getEntityIdForCurrentPage()
		if entity == nil then return 0 end
	end
	local claims = mw.wikibase.getBestStatements(entity, propertyID)
	local outList = {}
	if not claims then return 0 end
	return #claims
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din alte module
function p.findBestValues(propertyID, entity)
	if type(entity) == 'table' then
		entity = entity.id
	elseif type(entity) == 'number' then
		entity = 'Q' .. tostring(entity)
	end
	if entity == nil then
		entity = mw.wikibase.getEntityIdForCurrentPage()
		if entity == nil then return {} end
	end
	if not StringUtils._emptyToNil({propertyID}) then return nil end
	local claims = mw.wikibase.getBestStatements(entity, propertyID)
	local outList = {}
	if claims then for claimIdx, actualClaim in pairs(claims) do
		if hasValueSnak(actualClaim) then
			table.insert(outList, snakToString(actualClaim.mainsnak) .. p.outputReferences(actualClaim))
		end
	end end
	return outList
end	

p._getBestValuesWithSeparator = function(entity, propertyID, sep)
	if propertyID == nil then return '' end
	local valueList = p.findBestValues(propertyID, entity)
	return valueList and table.concat(valueList, sep)
end

-- Returnează o listă a valorilor cu rangul cel mai înalt și cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din formate
p.getBestValuesWithSeparator = function(frame) 
	local sep = frame.args[1]
	if sep == nil or sep == '' then sep = ', ' end
	local propertyID = mw.text.trim(frame.args[2] or "")
	local entityID = mw.text.trim(frame.args[3] or "")
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	
	return p._getBestValuesWithSeparator(entity, propertyID, sep)
end

function p.findLanguageText(propertyId, langcode, entityId)
	-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local returnValue = nil
	langcode = langcode or 'ro'
	for eachClaimIdx, eachClaim in pairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if hasValueSnak(eachClaim) and eachClaim.mainsnak.datavalue then
			if eachClaim.mainsnak.datavalue.type == 'monolingualtext' then
				if eachClaim.mainsnak.datavalue.value.language == langcode then returnValue = eachClaim.mainsnak.datavalue.value.text end
			elseif eachClaim.mainsnak.datavalue.type == 'string' then
				returnValue = eachClaim.mainsnak.datavalue.value
			elseif eachClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
				returnValue = p.findLabel(eachClaim.mainsnak.datavalue.value.id, langcode)
			end
		end
	end
		
	return returnValue
end

function p.getLanguageText(frame)
	return p.findLanguageText(frame.args[1], frame.args[2] or 'ro', frame.args[3]) or ''
end

function p.findBestValuesForLocalLanguage(propertyId, entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local bestValues = {}
	local bestRank = nil
	for eachClaimIdx, eachClaim in pairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if (hasValueSnak(eachClaim) and eachClaim.qualifiers and eachClaim.qualifiers['P407'] and 0 < #(eachClaim.qualifiers['P407'])) then
			if hasBetterRank(eachClaim, bestRank) then
					for _,eachLangQual in ipairs(eachClaim.qualifiers['P655']) do
						if isValueSnak(eachLangQual) and eachLangQual.datavalue.value.id == 'Q7913' then
							bestRank = eachLangQual.rank
							if not bestRank or mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(eachClaim.rank)] > mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(bestRank)] then
								bestValues = {}
							end
							table.insert(bestValues, snakToString(eachClaim.mainsnak))
						end
					end
			end
		end
	end
	return bestValues
end

function p.getBestValuesForLocalLanguageWithSeparator(frame)
	return table.concat(p.findBestValuesForLocalLanguage(frame.args[2], frame.args[3]) or {}, frame.args[1])
end

p.findShortestAlias = function(entityId)
	return p.findLinkToItem(entityId, false, false, true)
end

p.findImageAndCaption = function(entityId)
	local wikidataImages = p.findBestClaimsForProperty(entityId, 'P2716')
	if not wikidataImages or #wikidataImages == 0 then
		wikidataImages = p.findBestClaimsForProperty(entityId, 'P18')
	end
	local imageName, imageCaption
	if wikidataImages and #wikidataImages > 0 then
		local wikidataImage = wikidataImages[1]
		if hasValueSnak(wikidataImage) and wikidataImage.mainsnak.datavalue then
			imageName = wikidataImage.mainsnak.datavalue.value
			local roDescr = p.findLabel(entityId, nil)
			if wikidataImage.qualifiers and wikidataImage.qualifiers['P2096'] then
				for _,eachImageDescrLangString in pairs(wikidataImage.qualifiers['P2096']) do
					if isValueSnak(eachImageDescrLangString) and eachImageDescrLangString.datavalue and eachImageDescrLangString.datavalue.value.language == 'ro' then
						roDescr = eachImageDescrLangString.datavalue.value.text or roDescr
					end
				end
			end
			imageCaption = roDescr
		end
	end
	return imageName, imageCaption
end

p.findLinkToWikidataItem = function(itemId, prefix)
	libraryUtil.checkType('findLinkToWikidataItem', 2, prefix, 'string', false)
	local qId = StringUtils._prependIfMissing({itemId, prefix})
	local itemLabel = p.findLabel(qId)
	return '[[:d:' .. (prefix == 'P' and 'Property:' or '') .. qId .. '|' .. (itemLabel or '') .. ' ' .. tostring(mw.html.create('small'):wikitext('(' .. qId .. ')')) .. ']]'
end

p.getLinkToWikidataItem = function(frame)
	local args = getArgs(frame)
	local prefix = args['prefix'] or 'Q'
	if not args[1] then return "''id de " .. (prefix == 'P' and 'proprietate' or 'item') .. " Wikidata nespecificat''" end
	return p.findLinkToWikidataItem(args[1], prefix)
end

p.formatExternalLink = function(pId, qId, text)
	local formatterUrl = p.findOneValueNoRef('P1630', pId) or '$1'
	local urlComponent = text or p.findOneValueNoRef(pId, qId)
	if not urlComponent then return nil end
	local formattedLink = mw.ustring.gsub(formatterUrl, '%$1', text or urlComponent)
	return formattedLink
end

p.getFormattedExternalLink = function(frame)
	local args = getArgs(frame)
	local pId = args['pid'] or args[1]
	local qId = args['qid'] or args[2]
	local text = args['text']
	
	return p.formatExternalLink(pId, qId, text)
end
p.isFemale = function(qId)
	local entityId = qId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return false end
	local genders = mw.wikibase.getBestStatements(entityId, 'P21')
	local feminineIds = { 6581072, 1052281, 43445 }
	
	if genders then for genderIdx,eachGender in ipairs(genders) do
		if hasValueSnak(eachGender) then for _,eachFemId in ipairs(feminineIds) do
			if eachFemId == eachGender.mainsnak.datavalue.value['numeric-id'] then return true end
		end end
	end end
	return false
end

p.findClaimForTimestamp = function(entityId, propertyId, timestamp, lang)
	local propClaims = p.findSortedClaimsForProperty(entityId, propertyId)
	local bestClaim = nil
	if propClaims then for _,eachPropClaim in ipairs(propClaims) do
		if hasValueSnak(eachPropClaim) and isClaimTrue(eachPropClaim) then
			
			if eachPropClaim.mainsnak.datavalue.type ~= 'monolingualtext' or eachPropClaim.mainsnak.datavalue.value.language == (lang or 'ro') then
				local before = nil
				local after = nil
				if eachPropClaim.qualifiers then
					if eachPropClaim.qualifiers['P580'] and eachPropClaim.qualifiers['P580'][1] and isValueSnak(eachPropClaim.qualifiers['P580'][1]) then
						after = GregorianDate.convertToGregorianIfInInterval(DateUtils.extractDateFromWikidataSnak(eachPropClaim.qualifiers['P580'][1]))
					end
					if eachPropClaim.qualifiers['P582'] and eachPropClaim.qualifiers['P582'][1] and isValueSnak(eachPropClaim.qualifiers['P582'][1]) then
						before = GregorianDate.convertToGregorianIfInInterval(DateUtils.extractDateFromWikidataSnak(eachPropClaim.qualifiers['P582'][1]))
					end
				end
				if timestamp then
					--if before and DateUtils.compare(timestamp, before) > 0 then --the claim list is sorted by before
					--	break
					--else
						if after == nil and before and DateUtils.compare(timestamp, before) < 0 then
							return eachPropClaim
						elseif after and before and DateUtils.compare(timestamp, before) < 0 and DateUtils.compare(timestamp, after) > 0 then 
							return eachPropClaim
						elseif after and before == nil and DateUtils.compare(timestamp, after) > 0 then
							return eachPropClaim
						end
					--end
				end
				if not bestClaim or mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(bestClaim.rank)] < mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(eachPropClaim.rank)] 
						or (mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(bestClaim.rank)] < mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(eachPropClaim.rank)] and not after) then
					bestClaim = eachPropClaim
				end
			end
		end
	end end

	if bestClaim then
		return bestClaim
	else
		return nil
	end
end

p.formatOnlineLink = function(linkProp, entityId)
	local linkId = p.findOneValueNoRef(linkProp, entityId)
	if linkId then
		local linkFormatter = p.findOneValueNoRef('P1630', linkProp) or '$1'
		if linkFormatter then
			linkId = mw.ustring.gsub(linkId, '%%', '%%%%')
			local link = mw.ustring.gsub(linkFormatter, '$1', linkId)
			return link
		end
	end
	return nil
end

p.getFormattedOnlineLink = function(frame)
	local args = getArgs(frame)
	return p.formatOnlineLink(args[1], args[2] or mw.wikibase.getEntityIdForCurrentPage())
end

p.findOnlineLinks = function(onlinelinksprops, entityId)
	local onlinelinks = {}
	local onlinelinksprops = onlinelinksprops and #onlinelinksprops > 0 and onlinelinksprops or {'P856', 'P1581', 'P2013', 'P2002', 'P2847', 'P345', 'P3265', 'P3579', 'P3435', 'P2003', 'P2471', 'P2397', 'P953', 'P1065', 'P4173', 'P4431', 'P1968', 'P6634'}
	for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
		local link = p.formatOnlineLink(eachOnlineLinkProp)
		if link then
			local linkentities = p.getBestEntityIdsList(eachOnlineLinkProp, 'P1629')
			if linkentities and #linkentities > 0 then
				local linklabel = p.findLabel(StringUtils._prependIfMissing({linkentities[1], 'Q'}))
				table.insert(onlinelinks, '[' .. link .. ' ' .. linklabel .. ']')
			end
		end
	end
	return onlinelinks
end

p.getOnlineLinks = function(frame)
	local args = getArgs(frame)
	local _,entityId = propAndEntity(StringUtils._prependIfMissing({args['qid'], 'Q'}))
	local onlinelinksprops = {}
	local passedProps = TableTools.compressSparseArray(args)
	for __,eachProp in ipairs(passedProps) do
		if mw.ustring.match(eachProp, 'P%d+') == eachProp then
			table.insert(onlinelinksprops, eachProp)
		end
	end
	
	return table.concat(p.findOnlineLinks(onlinelinksprops, entityId), tostring(mw.html.create('br')))
end

p.findLanguageIndexedLabelsFromProperty = function(entityId, propertyId)
	local entity = nil
	if entityId then
		entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else
		entityId = mw.wikibase.getEntityIdForCurrentPage()
	end
	local titles = {}
	for _,eachTitleClaim in ipairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if hasValueSnak(eachTitleClaim) then
			titles[eachTitleClaim.mainsnak.datavalue.value.language] = eachTitleClaim.mainsnak.datavalue.value.text
		end
	end
	return titles
end

p.findTitleOfWork = function(entityId)
	local entity = nil
	if entityId then
		entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else
		entityId = mw.wikibase.getEntityIdForCurrentPage()
	end

	--- this comes first just for performance reasons
	if entityId then
		local localLabel, localLang = mw.wikibase.getLabelWithLang(entityId)
		if localLabel and localLang == 'ro' then
			return localLabel
		end
	else
		return nil
	end

	local langCode = nil
	for _,eachProperty in ipairs({'P364', 'P407'}) do
		for _,eachLangClaim in ipairs(mw.wikibase.getAllStatements(entityId, eachProperty)) do
			if not langCode and hasValueSnak(eachLangClaim) then
				local langEntityId = eachLangClaim.mainsnak.datavalue.value.id
				if LangUtils[langEntityId] then
					langCode = LangUtils[langEntityId]
					break
				end
				if langEntityId then
					for _,eachLangCodeClaim in ipairs(mw.wikibase.getAllStatements(langEntityId, 'P424')) do
						if not langCode and hasValueSnak(eachLangCodeClaim) and isClaimTrue(eachLangCodeClaim) then
							langCode = eachLangCodeClaim.mainsnak.datavalue.value
						end
					end
				end
			end
		end
	end
	
	local titles = p.findLanguageIndexedLabelsFromProperty(entityId, 'P1476')

	return titles['ro'] or wrapInLangSpan(titles[langCode], langCode) or wrapInLangSpan(mw.wikibase.getEntity(entityId):getLabel(langCode), langCode) or wrapInLangSpan(mw.wikibase.getEntity(entityId):getLabel('en'), 'en')
end

p.findTitlesOfWorksFromProperty = function(propertyId, entityId, limit)
	if limit == nil then limit = 10 end
	if entityId then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else entityId = mw.wikibase.getEntityIdForCurrentPage() end

	if not entityId then return nil end
	
	local ret = {}
	local bestWorksClaims = mw.wikibase.getBestStatements(entityId, propertyId)
	for workIdx = 1,math.min(limit, #bestWorksClaims) do
		local eachWorkClaim = bestWorksClaims[workIdx]
		if hasValueSnak(eachWorkClaim) then
			local workId = eachWorkClaim.mainsnak.datavalue.value.id
			table.insert(ret, computeLinkToItem(workId, true, nil, function() return p.findTitleOfWork(workId) end))
		end
	end
	return ret
end


p.getTitleOfWorkFromPropertyWithSeparator = function(frame)
	local args = getArgs(frame)
	local sep = args[1]
	local propertyId, entityId = propAndEntity(args[2], args[3])
	local limitStr = args['limit']
	local limit = tonumber(limitStr or '10')
	local retListRaw = p.findTitlesOfWorksFromProperty(propertyId, entityId, limit) or {}
	local retList = {}
	for _,eachRetVal in ipairs(retListRaw) do
		table.insert(retList, "''" .. eachRetVal .. "''")
	end
	return table.concat(retList, sep)
end

p.loadOneValueInChain = function(chain)
	local crtChainElementId = mw.wikibase.getEntityIdForCurrentPage()
	local crtChainClaim = nil
	local crtChainSnak = nil
	local coordComponent = nil
	local raw = false
	local label = false
	local ref = false
	for _,eachChainRing in ipairs(chain) do
		if mw.ustring.match(eachChainRing, 'Q%d+') == eachChainRing then
			crtChainElementId = eachChainRing
		elseif mw.ustring.match(eachChainRing, '_P%d+') and crtChainSnak then
			local qualPropertyId = mw.ustring.sub(eachChainRing, 2, mw.ustring.len(eachChainRing))
			if crtChainClaim.qualifiers and crtChainClaim.qualifiers[qualPropertyId] and #(crtChainClaim.qualifiers[qualPropertyId]) > 0 then
				local qualIdx = 1
				local propertyQuals = crtChainClaim.qualifiers[qualPropertyId]
				while qualIdx <= #propertyQuals and not isValueSnak(propertyQuals[qualIdx]) do
					qualIdx = qualIdx + 1
				end
				if qualIdx <= #propertyQuals and isValueSnak(propertyQuals[qualIdx]) then
					local firstValueSnakIdx = qualIdx
					if propertyQuals[qualIdx].datavalue.type == 'monolingualtext' then
						while qualIdx <= #propertyQuals and (not isValueSnak(propertyQuals[qualIdx]) or propertyQuals[qualIdx].datavalue.value.language ~= 'ro') do
							qualIdx = qualIdx + 1
						end
					end
					crtChainSnak = qualIdx <= #propertyQuals and isValueSnak(propertyQuals[qualIdx]) and propertyQuals[qualIdx] or propertyQuals[firstValueSnakIdx]
				else 
					return nil
				end
			else 
				return nil
			end
		elseif mw.ustring.match(eachChainRing, 'P%d+') == eachChainRing then
			if isValueSnak(crtChainSnak) and crtChainSnak.datavalue.type == 'wikibase-entityid' then
				crtChainElementId = crtChainSnak.datavalue.value.id
				crtChainClaim = nil
				crtChainSnak = nil
			end
			if crtChainElementId and not crtChainSnak then
				local propertyClaims = mw.wikibase.getBestStatements(crtChainElementId, eachChainRing)
				if #propertyClaims < 1 then return nil end
				local claimIdx = 1
				
				if propertyClaims[claimIdx].mainsnak.datavalue and propertyClaims[claimIdx].mainsnak.datavalue.type == 'monolingualtext' then
					while claimIdx <= #propertyClaims and (not hasValueSnak(propertyClaims[claimIdx]) or propertyClaims[claimIdx].mainsnak.datavalue.value.language ~= 'ro') do
						claimIdx = claimIdx + 1
					end
				else
					while claimIdx <= #propertyClaims and not hasValueSnak(propertyClaims[claimIdx]) do
						claimIdx = claimIdx + 1
					end
				end
				if claimIdx <= #propertyClaims and hasValueSnak(propertyClaims[claimIdx]) then
					crtChainClaim = propertyClaims[claimIdx]
					crtChainSnak = crtChainClaim.mainsnak
				else 
					return nil
				end
			end
		elseif eachChainRing == 'lat' or eachChainRing == 'long' then
			coordComponent = eachChainRing .. 'itude'
		elseif eachChainRing == 'raw' then
			raw = true
		elseif eachChainRing == 'label' then
			if crtChainSnak then crtChainElementId = crtChainSnak.datavalue.value.id end
			crtChainClaim = nil
			crtChainSnak = nil
			label = true
		elseif eachChainRing == 'ref' then
			ref = true
		end
	end
	if ref and crtChainClaim then
		return p.outputReferences(crtChainClaim)
	end
	if crtChainSnak then
		if coordComponent and crtChainSnak.datavalue.value[coordComponent] then return crtChainSnak.datavalue.value[coordComponent] end
		return raw and printRawValue(crtChainSnak) or snakToString(crtChainSnak)
	end
	if crtChainElementId then return raw and crtChainElementId or label and p.findLabel(crtChainElementId) or p.findLinkToItem(crtChainElementId) end
	return nil
end

p.getOneValueInChain = function(frame)
	local args = getArgs(frame)
	return p.loadOneValueInChain(args)
end

local function isA(entityId, typeIds)
	local entityTypes = p.findClaimsForProperty(entityId, 'P31')
	entityId = type(entityId) == 'number' and ('Q' .. tostring(entityId)) or entityId
	if entityTypes then for _,eachEntityType in ipairs(entityTypes) do
		if isClaimTrue(eachEntityType) and hasValueSnak(eachEntityType) then
			if TableTools.contains(typeIds, eachEntityType.mainsnak.datavalue.value.id) then
				return true
			end
			local recurseSuccess, recursivelyFoundType = pcall(mw.wikibase.getReferencedEntityId, eachEntityType.mainsnak.datavalue.value.id, 'P279', typeIds)
			if recurseSuccess and recursivelyFoundType then
				return true
			end
			if not recurseSuccess then
				--manually recurse one level
				local superTypeClaims = mw.wikibase.getBestStatements(eachEntityType.mainsnak.datavalue.value.id, 'P279')
				if superTypeClaims then for __,eachSuperTypeClaim in ipairs(superTypeClaims) do
					if hasValueSnak(eachSuperTypeClaim) and TableTools.contains(typeIds, eachSuperTypeClaim.mainsnak.datavalue.value.id) then
						return true
					end
				end end
			end
		end
	end end
	return false
end
p.isA = isA

p.findAnniversary = function(propertyId, entityId, step)
	local _date = p.findDateValues(propertyId, entityId)
	if _date and _date[1] then 
		-- no year 0
		if _date[1].year < 0 then _date[1].year = _date[1].year + 1 end
		if (os.date("*t").year - _date[1].year) % step == 0 then
			return (os.date("*t").year - _date[1].year)
		end
	else
		return 0
	end
end

p.getAnniversary = function(frame)
	local args = getArgs(frame)
	return p.findAnniversary(args[1], args[2], args[3])
end

p.findLocationChain = function(entity)
	local q = entity or mw.wikibase.getEntityIdForCurrentPage()
	if not q then return nil end
	local countryQId = p.loadOneValueInChain({q, 'P17', 'raw'})
	local adminUnitQId = p.loadOneValueInChain({q, 'P131', 'raw'})
	if not adminUnitQId or adminUnitQId == countryQId then return p.findLinkToItem(countryQId, true, false, false) end
	return p.findLinkToItem(adminUnitQId, true, false, false) .. ', ' .. p.findLocationChain(adminUnitQId)
end

p.getLocationChain = function(frame)
	local args = getArgs(frame)
	return p.findLocationChain(args.q)
end

p.findAddress = function(qId)
	local q = qId or mw.wikibase.getEntityIdForCurrentPage()
	if not q then return nil,nil end
	
	p6375 = p.findOneValue('P6375', q)
	if p6375 then return p6375, 'P6375' end
	
	p669claims = p.findBestClaimsForProperty(q, 'P669')
	local addrs = {}
	if p669claims then for _,eachP669Claim in ipairs(p669claims) do
		if hasValueSnak(eachP669Claim) then
			local thisAddr = p.printSnak(eachP669Claim.mainsnak)
			if eachP669Claim.qualifiers and eachP669Claim.qualifiers['P670'] then
				local streetNumbers = {}
				for __,eachP669ClaimP670Qual in ipairs(eachP669Claim.qualifiers['P670']) do
					if isValueSnak(eachP669ClaimP670Qual) then
						table.insert(streetNumbers, eachP669ClaimP670Qual.datavalue.value)
					end
				end
				if #streetNumbers > 0 then
					thisAddr = thisAddr .. ' nr. ' .. table.concat(streetNumbers, ', ')
				end
			end
			table.insert(addrs, thisAddr)
		end
	end end
	if #addrs > 0 then
		table.insert(addrs, 1, p.findLocationChain(q))
		return table.concat(addrs, '; ')
	end
	
	p969claims = p.findBestClaimsForProperty(q, 'P969')
	addrs = {}
	if p969claims then for _,eachP969Claim in ipairs(p969claims) do
		if hasValueSnak(eachP969Claim) then
			table.insert(addrs, p.printSnak(eachP969Claim.mainsnak))
		end
	end end
	if #addrs > 0 then
		table.insert(addrs, 1, p.findLocationChain(q))
		return table.concat(addrs, '; ')
	end
	
	p159claims = p.findBestClaimsForProperty(q, 'P159')
	addrs = {}
	if p159claims then for _,eachP159claim in ipairs(p159claims) do
		if hasValueSnak(eachP159claim) then
			local addrParts = { p.printSnak(eachP159claim.mainsnak) }
			if eachP159claim.qualifiers then
				for __,qual in ipairs({'P969', 'P281', 'P17'}) do
					if eachP159claim.qualifiers[qual] then for ___,eachP159ClaimQual in ipairs(eachP159claim.qualifiers[qual]) do
						if isValueSnak(eachP159ClaimQual) then
							table.insert(addrParts, p.printSnak(eachP159ClaimQual))
						end
					end end
				end
			end
			table.insert(addrs, table.concat(addrParts, ', '))
		end
	end end
	if #addrs > 0 then
		return table.concat(addrs, tostring(mw.html.create('br')))
	end
	
	local p2795text = p.loadOneValueInChain({'P2795'})
	if p2795text then return p2795text end
	return nil
end

p.getAddress = function(frame)
	local args = getArgs(frame)
	local addr, srcProp = p.findAddress(args[1])
	return addr
end

p.hasValueSnak = hasValueSnak

return p