require 'Амодуль:No globals'

local getArgs = require('Амодуль:Arguments').getArgs

local p = {}

--- Сопоставление идентификатора объекта в Викиданных с идентификатором тела в GeoHack.
p.myGlobes = {
	Q2 = "earth",
	Q308 = "mercury",
	Q313 = "venus",
	Q405 = "moon",
	Q111 = "mars",
	Q7547 = "phobos",
	Q7548 = "deimos",
	Q3169 = "ganymede",
	Q3134 = "callisto",
	Q3123 = "io",
	Q3143 = "europa",
	Q15034 = "mimas",
	Q3303 = "enceladus",
	Q15047 = "tethys",
	Q15040 = "dione",
	Q15050 = "rhea",
	Q2565 = "titan",
	Q15037 = "hyperion",
	Q17958 = "iapetus",
	Q17975 = "phoebe",
	Q3352 = "miranda",
	Q3343 = "ariel",
	Q3338 = "umbriel",
	Q3322 = "titania",
	Q3332 = "oberon",
	Q3359 = "triton",
	Q339 = "pluto",
}

local roundingForType = {
	landmark = 2,
	railwaystation = 2,
	pass = 2,
	edu = 2
}

-- dec2dms(0) --> 0°
-- dec2dms(10.5) --> 10°30′
-- dec2dms(0.016667) --> 0°1′
-- dec2dms(0.004166) --> 0°0′15″

function p._dec2dms(value, precision, display)
	value = math.abs(value)
	local sec, secfrac = math.modf(value * 3600 + 0.005)
	local minTotal = math.floor(value * 60 + 0.5)
	if display == "table" then
		precision = 0
	elseif not precision then
		if sec % 60 > 0 then
			precision = 0
		elseif minTotal % 60 > 0 then
			precision = -1
		else
			precision = -2
		end
	end

	if precision == -2 then
		local deg = math.floor(value + 0.5)
		return string.format("%d°", deg)
	elseif precision == -1 then
		local min = minTotal % 60
		local deg = math.floor(minTotal / 60)
		return string.format("%d°%d′", deg, min)
	else
		local mult = 10^precision
		local secTotal = math.floor(value * 3600 * mult + 0.5) / mult
		local min = math.floor(secTotal / 60)
		local sec = secTotal - min * 60
		local deg = math.floor(min / 60)
		min = min % 60
		-- ošetření zaokrouhlovací chyby
		sec = math.floor(sec * mult + 0.1) / mult

		if display ~= "table" then
			local lang = mw.language.getContentLanguage()
			return string.format("%d°%d′%s″", deg, min, lang:formatNum(sec))
		else
			return string.format("%d°%02d′%02d″", deg, min, sec)
		end
	end
end

function p.dec2dms(frame)
	return p._dec2dms(frame.args[1], frame.args[2], nil)
end

function p._renderCoordinates(args, frame)
	local pageTitle = mw.title.getCurrentTitle()
	local lat = tonumber(args[1])
	local lon = tonumber(args[2])
	if not lat or not lon then
		local err = "<span class='error'>Неправильный синтаксис координат: <code>" .. mw.text.nowiki(args[1] or "") .. "</code>, <code>" .. mw.text.nowiki(args[2] or "") .. "</code></span>"
		if pageTitle.namespace == 0 then err = err .. "[[Акатегориа:Обслуживание:Страницы с неправильными координатами]]" end
		return err
	end
	local display = args.display
	local isMain = display == "top" or display == "infobox"
	local globe = args.globe
	local explicitName = args.name
	local name = explicitName or (isMain and pageTitle.text)
	local typ = args.typ or args.type
	local region = args.region
	local scale = args.scale

	local rounding = typ and roundingForType[typ]
	typ = typ or "landmark"

	local geoParams = { tostring(lat), "N", tostring(lon), "E" }
	if globe then table.insert(geoParams, "globe:" .. globe) end
	if typ then table.insert(geoParams, "type:" .. typ) end
	if region then table.insert(geoParams, "region:" .. region) end
	if scale then table.insert(geoParams, "scale:" .. scale) end

	local urlParams = {
		language = "ab",
		pagename = pageTitle.prefixedText
	}
	if explicitName then urlParams.title = explicitName end

	-- WikiMiniAtlas требуется параметр «params» в самом конце (https://github.com/dschwen/wikiminiatlas/issues/35)
	local geohackUrl = "//geohack.toolforge.org/geohack.php?" .. mw.uri.buildQueryString(urlParams) .. '&params=' .. table.concat(geoParams, "_")
	
	local latStr = p._dec2dms(lat, rounding, display)
	local lonStr = p._dec2dms(lon, rounding, display)
	local sp = " "
	if display == "table" then
		local latLen = #string.gsub(string.gsub(args[1], '^-', ''), '%..*', '') - #string.gsub(latStr, '°.*', '')
		latStr = string.rep(' ', latLen) .. latStr  -- číslicová mezera
		local lonLen = #string.gsub(string.gsub(args[2], '^-', ''), '%..*', '') - #string.gsub(lonStr, '°.*', '')
		lonStr = string.rep(' ', lonLen) .. lonStr
		sp = " "  -- úzká mezera
	end
	local coordStrTab = {latStr .. sp .. (lat >= 0 and "ҩ." or "л.") .. sp .. "ҭ.",
		lonStr .. sp .. (lon >= 0 and "мг." or "мҭ.") .. sp .. "н."}
	for i, str in pairs(coordStrTab) do
		coordStrTab[i] = tostring(mw.html.create('span')
			:attr({style = "white-space:pre"})
			:wikitext(str)
			:done())
	end
	local coordStr = table.concat(coordStrTab, "," .. sp)

	local result = {}

	if pageTitle.namespace == 0 then
		if globe and globe ~= 'earth' and globe ~= 'moon' and lon < 0 then
			-- [[mw:Extension:GeoData#Glossary]]
			-- [[Амодуль ахцәажәара:Coordinates#Инопланетные объекты]]
			lon = lon + 360
		end
		local coordTagArgs = { isMain and "primary" or "", tostring(lat), tostring(lon) }
		if scale then coordTagArgs["scale"] = scale end
		if globe then coordTagArgs["globe"] = globe end
		if name then coordTagArgs["name"] = name end
		if region then coordTagArgs["region"] = region end
		if typ then coordTagArgs["type"] = typ end
		table.insert(result, frame:callParserFunction("#coordinates", coordTagArgs))
	end

	if isMain then
		table.insert(result, "<span>")
		table.insert(result, frame:extensionTag('indicator', 'Souřadnice: [' .. geohackUrl .. ' ' .. coordStr .. ']', { name = 'coordinates' }))
		table.insert(result, "</span>")
	end

	if display ~= "top" then
		local linkResult = {}
		table.insert(linkResult, "[")
		table.insert(linkResult, geohackUrl)
		table.insert(linkResult, " ")
		if display == "link" and explicitName then
			table.insert(linkResult, explicitName)
		else
			table.insert(linkResult, coordStr)
		end
		table.insert(linkResult, "]")
		local attrs = { class = 'coordinates' }
		if explicitName then
			attrs.id = explicitName -- внимание, необходимо гарантировать, что id будет уникальным на странице
		end
		if display == "table" then
			attrs.class = attrs.class .. ' plainlinks'
		end
		table.insert(result, tostring(mw.html.create('span')
			:attr(attrs)
			:wikitext(table.concat(linkResult))
			:done()))
	end

	return table.concat(result)
end

function p.renderCoordinates(frame)
	local args = getArgs(frame, { removeBlanks = true })
	return p._renderCoordinates(args, frame)
end

return p