{{#invoke:Wikidata infobox | infobox }}


require "Modul:No globals"

local Wikidata = require 'Modul:Wikidata'
local lib = require 'Modul:Wikidata/lib'

local frame = mw.getCurrentFrame()
local args = frame and frame.args or {}
local title = args[1] and mw.text.trim(args[1]) or 'osoba' --TODO: better default box

local submodule = require ('Modul:Wikidata infobox/' .. title)
local t = submodule.layout
local requirements = submodule.requirements or {}
local colors = submodule.colors or {}

local parent = frame:getParent()
local params = parent and parent.args or {}
local entity = mw.wikibase.getEntityObject(params.id or nil)

local curr_title = mw.title.getCurrentTitle()
local pagename = curr_title.text
local namespace = curr_title.namespace
local Cats, Interwiki = {}, {}

local property_type = 1
local item_desc     = 2
local param_name    = 3
local prop_option   = 4

local errors = {
	["infobox-type-invalid"] = "Neplatný typ hodnoty na řádce %s: %s.",
}
-- TODO: mw.html
local layout_args = {
	abovestyle  = 'text-align:center; font-size:125%; ' .. (args.abovestyle or ''),
	aboveclass  = args.aboveclass or '',
	bottomstyle = 'font-size:smaller; text-align:center; ' .. (args.bottomstyle or ''),
	class       = 'infobox ' .. (args.class or ''),
	datastyle   = args.datastyle or '',
	headerstyle = 'text-align:center; ' .. (args.headerstyle or ''),
	imagestyle  = 'text-align:center; ' .. (args.imagestyle or ''),
	labelstyle  = 'text-align:left; white-space:nowrap; ' .. (args.labelstyle or ''),
	style       = 'text-align:left; font-size:88%; line-height:1.5em; width:' .. (args.width or '250px') .. '; ' .. (args.style or ''),
}
-- TODO: mw.html
local layout = { -- infobox table layout
	[1] =	'<table cellspacing="2" class="' .. layout_args.class .. '" style="' .. layout_args.style .. '">\n',
	[2] =	' <tr><th colspan="2" class="' ..layout_args.aboveclass .. '" style="background-color:$1; ' .. layout_args.abovestyle .. '">$2</th></tr>\n', -- label
	[4] =	' <tr><td colspan="2" style="' .. layout_args.imagestyle .. '">$1<br>$2</td></tr>\n', -- image
	[7] =	' <tr><td colspan="2" align="center"><table border="0" width="100%"><tr>\n',-- begin of small image
	[8] =	' <td align="center">$1</td>\n',											-- small image
	[11] =	' </tr></table></td></tr>\n',												-- end of small image
	[12] =	' <tr><th style="' .. layout_args.labelstyle .. '">$1</th><td style="' .. layout_args.datastyle .. '">$2\n</td></tr>', -- item
	[15] =	' <tr><th style="' .. layout_args.labelstyle .. '">$1</th><td style="' .. layout_args.datastyle .. '">$2\n</td></tr>', -- string
	[16] =	' <tr><th style="' .. layout_args.labelstyle .. '">$1</th><td style="' .. layout_args.datastyle .. '">$2\n</td></tr>', -- date
	[17] =	' <tr><th style="' .. layout_args.labelstyle .. '">$1</th><td style="' .. layout_args.datastyle .. '">$2\n</td></tr>', -- number
	[18] =	' <tr><th style="' .. layout_args.labelstyle .. '">$1</th><td style="' .. layout_args.datastyle .. '">$2\n</td></tr>', -- coord
	[19] =	' <tr><td>$1</td><td>$2\n</td></tr>', -- taxa
	[20] =	' <tr><td style="' .. layout_args.datastyle .. ' colspan="2">$1\n</td></tr>',
	[21] =	' <tr><td style="' .. layout_args.datastyle .. ' colspan="2">[[Soubor:Commons-logo.svg|20px|Logo Wikimedia Commons]] <b>[[c:$1|multimédia]]</b> na [[Wikimedia Commons]]</td></tr>\n',	-- link to commons
	[22] =	' <tr><td style="' .. layout_args.datastyle .. ' colspan="2">[[Soubor:Wikisource-logo.svg|20px|Logo Wikizdrojů]] <b>[[s:$1|dokumenty]]</b> na [[Wikizdroje|Wikizdrojích]]</td></tr>\n',	-- link to wikisource
	[23] =	' <tr><td style="' .. layout_args.datastyle .. ' colspan="2">[[Soubor:Wikiquote-logo.svg|20px|Logo Wikicitátů]] <b>[[q:$1|citáty]]</b> na [[Wikicitáty|Wikicitátech]]</td></tr>\n',		-- link to wikiquote
	[90] =	' <tr><th colspan="2" style="background-color:$1; ' .. layout_args.headerstyle .. '">$2</th></tr>\n',					-- group
	[99] =	' <tr><td colspan="2" style="' .. layout_args.bottomstyle .. '">Data mohou pocházet z [[d:' .. (entity and entity.id or '') .. '|datové položky]].</td></tr>\n'
}

local function safegsub(s, pattern, repl, n)
	local saferepl = mw.ustring.gsub(repl, '%%', '%%%%')
	return mw.ustring.gsub(s, pattern, saferepl, n)
end

local function addCat(catname)
	table.insert(Cats, '[[Kategorie:' .. catname .. '|' .. pagename .. ']]')
end

local function inMainWithEntityOrWithId()
	if entity then
		if params.id or namespace == 0 then
			return true
		end
	end
	return false
end

local function getOptions(line) -- get options for Wikidata.formatStatementsFromLua from infobox table line
	local options = { entity = entity }
	for a, b in pairs(t[line][prop_option]) do
		options[a] = b
	end
	return options
end

local function getDesc(line)
	local desc = t[line][item_desc]
	local link = t[line].link
	local text = params[t[line][param_name]] or ''
	if t[line].pl then
		if text ~= '' then
			local IsList = require 'Modul:IsList'
			if IsList.isList({ args = { text } }) then
				desc = t[line].pl
			end
		elseif namespace == 0 and t[line][prop_option] and entity and entity.claims then
			local options = getOptions(line)
			local Statements = Wikidata.filterStatementsFromLua(entity, options)
			if #Statements > 1 then desc = t[line].pl end
		end
	end
	if link then
		if desc ~= '' then
			desc = '[[' .. link .. '|' .. desc .. ']]'
		else
			desc = '[[' .. link .. ']]'
		end
	end
	return desc
end

local function getImageDescription(line, heading)
	if t[line].param_desc then
		if params[t[line].param_desc] and params[t[line].param_desc] ~= '' then
			return params[t[line].param_desc]
		end
	end
	return heading
end

local function makeImage(line, description)
	local options
	local image_options = {
		description = description,
		size = (t[line].param_size and params[t[line].param_size]) or (t[line].default_size and params[t[line].default_size]) or '200px'
	}
	local image_formatter = require 'Modul:ImageFormatter'
	local image_local = params[t[line][param_name]]
	local image_value
	if t[line][prop_option] then
		options = getOptions(line)
		image_value = Wikidata.getRawValueFromLua(options)
	end
	local placeholder_image
	if submodule.placeholder_images then
		placeholder_image = queryPairs(submodule.placeholder_images, entity)
		placeholder_image = image_formatter.makeImage(placeholder_image, image_options)
	end
	if image_local then
		local what = options.catbase or ('Vlastnost ' .. options.property:upper())
		if image_local == '' or image_local == '-' or image_local == 'ne' then
			if image_value and image_value ~= '' and tostring(t[line].over) ~= 'true' then
				addCat( 'Údržba:' .. what .. ' dostupný na Wikidatech a chybí v článku' )
			end
			if image_local == '' and t[line].over and image_value and image_value ~= '' then
				return image_formatter.makeImage(image_value, image_options)
			end
			return nil
		else
			local i18n = mw.loadData("Modul:Wikidata/i18n")
			if image_value and image_value ~= '' then
				if image_value == image_local then
					addCat(mw.ustring.format(i18n.categories.same, what))
				else
					addCat(mw.ustring.format(i18n.categories.diff, what))
				end
			else
				addCat(mw.ustring.format(i18n.categories["not"], what))
			end
			return image_formatter.makeImage(image_local, image_options)
		end
	elseif image_value and image_value ~= '' then
		return lib.addWdClass(image_formatter.makeImage(image_value, image_options))
	end
	return placeholder_image
end

local function makeValue(line, type)
	if t[line][prop_option] then
		local options = getOptions(line)
		if options.site then
			return Wikidata.getSitelinkFromLua(options)
		end
		if params[t[line][param_name]] then
			options.value = params[t[line][param_name]]
		end
		options.addclass = true
		if type ~= 'smallimage' then --temp
			options.compare = true
			options.addlink = true
		end
		return Wikidata.formatStatementsFromLua(options)
	end
	return params[t[line][param_name]] or nil
end

local function queryPairs(data, entity)
	local default = data.default or nil
	for propId, values in pairs(data) do
		if entity and entity.claims and entity.claims[propId] then
			for Qid, value in pairs(values) do
				local Statements = Wikidata.filterStatementsFromLua(entity, { property = propId })
				for _, statement in pairs(Statements) do
					if lib.getEntityIdFromValue(statement.mainsnak.datavalue.value) == value then
						return values[Qid]
					end
				end
			end
		end
	end
	return default
end

local function checkParams()
	local AllParams = { id = '' } -- default params
	for _, line in pairs(t) do
		if line[param_name] and line[param_name] ~= '' then
			AllParams[line[param_name]] = '' -- empty value is enough
		end
		for param in pairs(line) do
			if mw.ustring.find(param, '^param_') then
				AllParams[line[param]] = ''
			end
		end
	end
	for param in pairs(params) do
		if not AllParams[param] then
			addCat('Údržba:Články s neznámým parametrem v infoboxu ' .. title)
			break
		end
	end
end

local function checkRequirements()
	for prop, values in pairs(requirements) do
		if entity.claims[prop:upper()] then
			for i, id in pairs(values) do
				local Statements = Wikidata.filterStatementsFromLua(entity, { property = prop })
				for _, statement in pairs(Statements) do
					if id == lib.getEntityIdFromValue(statement.mainsnak.datavalue.value) then
						break
					elseif i == #values then
						addCat('Údržba:Chybná vlastnost ' .. prop .. ' na Wikidatech')
					end
				end
			end
		else
			addCat('Údržba:Chybí vlastnost ' .. prop .. ' na Wikidatech')
		end
	end
end

local function formatTaxonId(id, entity)
	local localised = mw.wikibase.label(id)
	local taxon_name = Wikidata.formatStatementsFromLua{ entity = entity, limit = 1, property = 'P225', rank = 'best' }
	local itemFormatter = require 'Modul:Wikidata/item'
	if taxon_name then
		if not localised or localised ~= taxon_name then
			return itemFormatter.formatEntityId(id, {}) .. " (''" .. taxon_name .. "'')"
		end
	end
	return itemFormatter.formatEntityId(id, {})
end

local function generateTaxaRows(entity, color, rows)
	if entity and entity.claims then
		local FilteredAbove = Wikidata.filterStatementsFromLua(entity, { property = 'P171', rank = 'best' })
		for _, statement in pairs(FilteredAbove) do
			local above_id = lib.getEntityIdFromValue(statement.mainsnak.datavalue.value)
			local above_entity = mw.wikibase.getEntityObject(above_id)
			if above_entity.claims then
				local value = formatTaxonId(above_id, above_entity)
				local above_rank, description
				local color = color
				local Ranks = Wikidata.filterStatementsFromLua(above_entity, { property = 'P105', rank = 'best' })
				if #Ranks > 0 then
					local itemFormatter = require 'Modul:Wikidata/item'
					for _, statement in pairs(Ranks) do
						above_rank = lib.getEntityIdFromValue(statement.mainsnak.datavalue.value)
						description = itemFormatter.formatEntityId(above_rank, {}) or ''
						if not color or color == colors.default then
							color = queryPairs(colors, above_entity)
						end
					end
				else
					description = '(nezařazeno)'
				end
				local row = safegsub(safegsub(layout[19],'$1',lib.addWdClass(description)),'$2',lib.addWdClass(value))
				local above_rows, above_color = generateTaxaRows(above_entity, color, row .. rows)
				if above_rows then
					return above_rows, above_color
				else
					return nil, nil
				end
			end
		end
	end
	return rows, color
end

local function generateInterwiki()
	local Properties = { 'P361', 'P460' }
	if entity and entity.claims then
		for _, prop in pairs(Properties) do
			if entity.claims[prop] then
				local Sitelinks = entity.sitelinks or {}
				local Statements = Wikidata.filterStatementsFromLua(entity, { property = prop, rank = 'best' })
				if #Statements == 1 then
					local id = lib.getEntityIdFromValue(Statements[1].mainsnak.datavalue.value)
					local entity = mw.wikibase.getEntityObject(id)
					if entity and entity.sitelinks then
						for site, sitelink in pairs(entity.sitelinks) do
							if mw.ustring.find(site, 'wiki$') then
								local code = mw.ustring.gsub(mw.ustring.gsub(site, '^(.+)wiki$', '%1'), '_', '-')
								if code ~= 'wikidata' and code ~= 'commons' and code ~= 'mediawiki' and code ~= 'species' and code ~= 'meta' then
									if not Sitelinks[site] then
										if #Interwiki == 0 then
											addCat('Monitoring:Infobox generující interwiki')
										end
										table.insert(Interwiki, '[[' .. code .. ':' .. sitelink.title .. ']]')
									end
								end
							end
						end
						break
					end
				end
			end
		end
	end
end

local function addMainCategory()
	if entity.claims.P910 then
		local Statements = entity:getBestStatements('P910')
		for _, statement in pairs(Statements) do
			if lib.IsSnakValue(statement.mainsnak) then
				local sitelink = mw.wikibase.sitelink(lib.getEntityIdFromValue(statement.mainsnak.datavalue.value))
				if sitelink then
					table.insert(Cats, '[[' .. sitelink .. '| ]]')
					break
				end
			end
		end
	end
end

local p = {}

function p.generateCode()
	local table1 = '<pre>{{Infobox - ' .. title .. '\n'
	local table2 = '<pre>\n'
	for line = 1, #t do
		if t[line][param_name] and t[line][param_name] ~= '' then
			table1 = table1 .. '| ' .. t[line][param_name] .. ' = \n'
			local options = {}
			if t[line][prop_option] then
				 options = getOptions(line)
			end
			table2 = table2 .. mw.ustring.gsub(options.of or options.property or '','p(%d)','P%1') .. '\n'
		end
		for param in pairs(t[line]) do
			if mw.ustring.find(param, '^param_') then
				table1 = table1 .. '| ' .. t[line][param] .. ' = \n'
				table2 = table2 .. '\n'
			end
		end
	end
	table1 = table1 .. '}}</pre>'
	table2 = table2 .. '\n</pre>'
	return '<table><tr><td>\n' .. table1 .. '</td>\n<td>\n' .. table2 .. '</td></tr></table>'
end

function p.infobox()
	if params.id and not entity then
		addCat('Údržba:Entita nebyla nalezena')
		return error(lib.formatError('entity-not-found'))
	end
	if not entity and namespace == 0 then
		addCat('Údržba:Článek bez propojení na Wikidatech')
	end
	local backgroundcolor, taxa_rows
	if title == "taxobox" then
		taxa_rows, backgroundcolor = generateTaxaRows(entity, nil, '')
		taxa_rows = taxa_rows or ''
		backgroundcolor = backgroundcolor or colors.default
	else
		backgroundcolor = queryPairs(colors, entity) or '#f0f0f0'
	end
	local heading
	local label = inMainWithEntityOrWithId() and mw.wikibase.label(entity.id) or nil
	if t[1][property_type] == 'begin' and params[t[1][param_name]] and params[t[1][param_name]] ~= '' then
		heading = params[t[1][param_name]]
		if label and heading ~= label then
			addCat('Údržba:Wikidata mají jiný štítek')
		end
	elseif label then
		heading = lib.addWdClass(label)
	else
		heading = pagename
		if inMainWithEntityOrWithId() then
			addCat('Údržba:Doplnit štítek na Wikidatech')
		end
	end
	if entity and entity.claims and namespace == 0 then
		addMainCategory()
		checkRequirements()
	end
	local result, detail, previousgroup = '', '', ''
	local open_image_group = false
	for line = 1, #t do	-- check all properties
		if open_image_group and t[line][property_type] ~= 'smallimage' then
			detail = detail .. layout[11]
			open_image_group = false
		end
		if t[line][property_type] == 'begin' then
			detail = detail .. layout[1] .. safegsub(safegsub(layout[2],'$1',backgroundcolor),'$2',heading)
		-- image
		elseif t[line][property_type] == 'image' then
			local description = getImageDescription(line, heading)
			local value = makeImage(line, description)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[4],'$1',value),'$2',description)
			end
		-- smallimage
		elseif t[line][property_type] == 'smallimage' then
			-- temp
			local value = makeValue(line, t[line][property_type])
			if value and value ~= '' then
				if not open_image_group then
					detail = detail .. layout[7]
					open_image_group = true
				end
				detail = detail .. safegsub(layout[8],'$1',value)
			end
		-- item
		elseif t[line][property_type] == 'item' then
			local value = makeValue(line, t[line][property_type])
			local description = getDesc(line)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[12],'$1',description),'$2',value)
			end
		-- string
		elseif t[line][property_type] == 'string' then
			local value = makeValue(line, t[line][property_type])
			local description = getDesc(line)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[15],'$1',description),'$2',value)
			end
		-- date
		elseif t[line][property_type] == 'date' then
			local value = makeValue(line, t[line][property_type])
			local description = getDesc(line)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[16],'$1',description),'$2',value)
			end
		-- number
		elseif t[line][property_type] == 'number' then
			local value = makeValue(line, t[line][property_type])
			local description = getDesc(line)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[17],'$1',description),'$2',value)
			end
		-- coordinates
		elseif t[line][property_type] == 'coord' then
			local value = makeValue(line, t[line][property_type])
			local description = getDesc(line)
			if value and value ~= '' then
				detail = detail .. safegsub(safegsub(layout[18],'$1',description),'$2',value)
			end
		-- taxa
		elseif t[line][property_type] == 'taxa' then
			detail = detail .. taxa_rows
		elseif t[line][property_type] == 'long' then
			local value = makeValue(line, t[line][property_type])
			if value and value ~= '' then
				if t[line].pattern then
					value = safegsub(t[line].pattern,'$1',value)
				end
				detail = detail .. safegsub(layout[20],'$1',value)
			end
		-- sitelinks TODO
	--	elseif t[line][property_type] == 'commons' then
	--		detail = detail .. safegsub(layout[21],'$1',value)
	--	elseif t[line][property_type] == 'source' then
	--		detail = detail .. safegsub(layout[22],'$1',value)
	--	elseif t[line][property_type] == 'quote' then
	--		detail = detail .. safegsub(layout[23],'$1',value)
		-- group
		elseif t[line][property_type] == 'group' then
			if open_image_group then
				detail = detail .. layout[11]
				open_image_group = false
			end
			if detail ~= "" then
				result = result .. previousgroup .. detail
				detail = ""
			end
			local description = function(line)
				local description = t[line][item_desc]
				if description == '' then
					if params[t[line][param_name]] then
						description = params[t[line][param_name]]
					end
				end
				return description
			end
			if description(line) ~= '' then
				previousgroup = safegsub(safegsub(layout[90],'$1',backgroundcolor),'$2',description(line))
			else
				previousgroup = ''
			end
		-- others / error
		else
			addCat('Údržba:Chyba v modulu Wikidata infobox')		
			return error(mw.ustring.format(errors["infobox-type-invalid"], line, t[line][property_type]))
		end
	end
	if inMainWithEntityOrWithId() then
		result = result .. layout[99]
	end
	checkParams()
	if namespace == 0 then
		generateInterwiki()
	end
	result = result .. '</table>' .. table.concat(Cats) .. table.concat(Interwiki)
	return result
end

return p