-- version 20181219 from master @cawiki

local p = {}

-- get safely a serialized snak
local function getSnak(statement, snaks)
	local ret = statement
	for i, v in ipairs(snaks) do
		if ret == nil then break end
		ret = ret[v]
	end
	return ret
end

-- get unit symbol
local function unitSymbol(id, lang)
	local claims = mw.wikibase.getBestStatements(id, 'P5061')
	if #claims > 0 then
		-- get all values by language
		local lang_text = {}
		for _, snak in ipairs(claims) do
			local value = getSnak(snak, {"mainsnak", "datavalue", "value"})
			if type(value) == 'table' then
				lang_text[value.language] = value.text
			end
		end
		
		-- get value in best language
		local languages = {lang, 'en', 'mul'}
		for _, l in ipairs(languages) do
			if lang_text[l] then
				return lang_text[l]
			end
		end
	end
	-- if not any symbol return label
	return mw.wikibase.getLabel(id)
end

function p.main(frame)
	local args = frame.args or frame -- via invoke or require
	local pargs = frame.args and frame:getParent().args or {}
	local id = args.item or pargs.item; if id == "" then id = nil end
	local unitcode = args.unit or pargs.unit; if unitcode == "" then unitcode = nil end
	local blacklist = {}
	for v in mw.text.gsplit(args.blacklist or "", "/", true) do
		blacklist[v] = true
	end
	local lang = args.lang or pargs.lang
	if lang == "" or lang == nil or mw.language.isKnownLanguageTag(lang) == false then
		if not mw.title.getCurrentTitle().isContentPage then
			lang = mw.getCurrentFrame():preprocess( '{{int:lang}}' )
		end
		if lang == "" or lang == nil or mw.language.isKnownLanguageTag(lang) == false then
			lang = mw.language.getContentLanguage().code
		end
	end
	local lang_obj = mw.language.new(lang)
	
	local entity = mw.wikibase.getEntity(id)
	if not entity then return end
	
	--fetch all data
	local dim_statements = {}
	local labels = {}
	dim_statements.diameter = entity:getBestStatements('P2386')
	-- height and alike
	labels.diameter = #dim_statements.diameter == 0 and nil or mw.wikibase.getLabel('P2386')
	dim_statements.height = entity:getBestStatements('P2048')
	labels.height = #dim_statements.height == 0 and nil or mw.wikibase.getLabel('P2048')
	dim_statements.draft = entity:getBestStatements('P2262')
	labels.draft = #dim_statements.draft == 0 and nil or mw.wikibase.getLabel('P2262')
	dim_statements.clearance = entity:getBestStatements('P2793')
	labels.clearance = #dim_statements.clearance == 0 and nil or mw.wikibase.getLabel('P2793')
	-- width and alike
	dim_statements.width = entity:getBestStatements('P2049')
	if #dim_statements.width == 0 then
		dim_statements.width = entity:getBestStatements('P2261') -- beam
		labels.width = #dim_statements.width == 0 and nil or mw.wikibase.getLabel('P2261')
	else
		labels.width = mw.wikibase.getLabel('P2049')
	end
	-- length and alike
	dim_statements.length = entity:getBestStatements('P2043')
	if #dim_statements.length == 0 then
		dim_statements.length = entity:getBestStatements('P5524') -- depth
		labels.length = #dim_statements.length == 0 and nil or mw.wikibase.getLabel('P5524')
	else
		labels.length = mw.wikibase.getLabel('P2043')
	end
	dim_statements.span = entity:getBestStatements('P2787')
	labels.span = #dim_statements.span == 0 and nil or mw.wikibase.getLabel('P2787')
	dim_statements.thickness = entity:getBestStatements('P2610')
	labels.thickness = #dim_statements.thickness == 0 and nil or mw.wikibase.getLabel('P2610')
	
	-- fill a table with all data needed
	local data = {}
	local qualifiers = {"P518", "P1013"} -- applies to part, criterion used
	local convert = require("Module:Convert")._convert
	for dim, statm in pairs(dim_statements) do
		for _, s in ipairs(statm) do
			data[#data + 1] = {}
			data[#data].dimension = dim
			local group
			for _, q in ipairs(qualifiers) do
				group = getSnak(s, {"qualifiers", q, 1, "datavalue", "value", "id"})
				if group then break end
			end
			if group then
				if lang == mw.language.getContentLanguage().code then
					data[#data].group = mw.wikibase.getLabel(group) or group
				else
					data[#data].group = mw.wikibase.getLabelByLang(group, lang) or group
				end
				if blacklist[group] then
					blacklist[data[#data].group] = true
				end
			end
			local amount = getSnak(s, {"mainsnak", "datavalue", "value", "amount"})
			data[#data].amount = lang_obj:formatNum(tonumber(amount))
			local unit = getSnak(s, {"mainsnak", "datavalue", "value", "unit"})
			if unit then
				unit = mw.ustring.sub(unit, mw.ustring.find(unit, "Q"), -1)
				local u_symbol = unitSymbol(unit, lang)
				if unitcode and u_symbol ~= unitcode then
					amount = convert({["args"] = {["numdot"]=".", ["numsep"]=""}}, {["args"] = {[1]=amount, [2]=u_symbol, [3]=unitcode, ["disp"]="number"}})
					local amount_num = tonumber(amount) -- avoid error message returned by convert
					if amount_num then
						data[#data].amount = lang_obj:formatNum(amount_num)
						u_symbol = unitcode
					end
				end
				data[#data].unit = u_symbol
			end
		end
	end
	
	-- group by qualifier
	local dimensions = {}
	local global_unit
	for i, v in ipairs(data) do
		local index = v.group or 1
		if blacklist[index] == nil then
			if dimensions[index] == nil then
				dimensions[index] = {}
			end
			table.insert(dimensions[index], {["dimension"]=v.dimension, ["amount"]=v.amount, ["unit"]=v.unit})
			-- global unit
			if i == 1 then
				global_unit = v.unit
			elseif global_unit and global_unit ~= v.unit then
				global_unit = nil
			end
		end
	end
	
	-- format output
	local icons = {
		["diameter"] = '<span title="' .. labels.diameter .. '">⌀</span>',
		["height"] = "[[File:Chess uat45.svg|15px|link=|" .. labels.height .. "]]",
		["draft"] = "[[File:Breezeicons-actions-22-draw-halfcircle4.svg|15px|link=|" .. labels.draft .. "]]",
		["clearance"] = "[[File:Breezeicons-actions-22-format-align-vertical-top.svg|15px|link=|" .. labels.clearance .. "]]",
		["width"] = "[[File:Chess lrt45.svg|15px|link=|" .. labels.width .. "]]",
		["length"] = "[[File:Chess urt45.svg|15px|link=|" .. labels.length .. "]]",
		["span"] = "[[File:Breezeicons-actions-22-draw-halfcircle3.svg|15px|link=|" .. labels.span .. "]]",
		["thickness"] = "[[File:Breezeicons-actions-22-format-align-vertical-center.svg|15px|link=|" .. labels.thickness .. "]]"
	}
	local dim_sort = {["diameter"]=1, ["height"]=2, ["draft"]=3, ["clearance"]=4, ["width"]=5, ["length"]=6, ["span"]=7, ["thickness"]=8}
	local out = {}
	for q, t in pairs(dimensions) do
		if type(q) == "string" then
			table.insert(out, q .. ":")
		end
		table.sort(t, function(a, b) return dim_sort[a.dimension] < dim_sort[b.dimension] end)
		for i, v in ipairs(t) do
			local suffix = " (" .. icons[v.dimension] .. ")"
			if i == #t or not global_unit then
				suffix = suffix .. " " .. v.unit
			end
			if i < #t then
				suffix = suffix .. " ×"
			end
			table.insert(out, '<span style="white-space:nowrap;">' .. v.amount .. suffix .. '</span>')
		end
		table.insert(out, "<br />")
	end
	
	return table.concat(out, " ")
end

return p