local locMap = {}

-- Internationalization

local api = {

   apiLocationMap    = 'locationMap',    apiAddLocation    = 'addLocation',    apiAddObject      = 'addObject',    apiGetMapValue    = 'getMapValue',    apiGetMapValueSet = 'getMapValueSet',

}

local errMsgs = {

   anError       = 'Fehler',    unknownMap    = 'Keine Karte für Region %s vorhanden',    noMapImage    = 'Kein Kartenbild spezifiziert',    wrongLat      = 'Breite %f liegt außerhalb der Kartenbegrenzungen',    wrongLong     = 'Länge %f liegt außerhalb der Kartenbegrenzungen',    wrongXBorders = 'Fehlende oder falsche horizontale Kartengrenzen',    wrongYBorders = 'Fehlende oder falsche vertikale Kartengrenzen',    noObject      = 'Kein Objekt angegeben',    noXPos        = 'Keine horizontale Lage angegeben',    noYPos        = 'Keine vertikale Lage angegeben',    noParam       = 'Kein Parameter für getMapValue angegeben',    notDefined    = 'Parameter nicht definiert',

}

local mapDocs = {

   tableClass    = 'prettytable',    name          = 'Name',    description   = 'Beschreibung',    projection    = 'Projektion',    top           = 'oben',    bottom        = 'unten',    left          = 'links',    right         = 'rechts',    default       = 'Standardkarte',    relief        = 'Physische Karte',    mark          = 'Marker',    marksize      = 'Markergröße',    linear        = 'Plattkarte',    nonlinear     = 'Nichtlineare Projektion',

}

-- Style aliases

local mapStyles = {

   mitte         = "margin: 0 auto !important",    center        = "margin: 0 auto !important",    left          = "clear: left; margin: 0 1em 1em 0; float: left",    links         = "clear: left; margin: 0 1em 1em 0; float: left",    right         = "clear: right; margin: 0 0 1em 1em; float: right",    rechts        = "clear: right; margin: 0 0 1em 1em; float: right",

}

local labelStyles = {

   bold          = "font-weight: bold",    fett          = "font-weight: bold",    italic        = "font-style: italic",    kursiv        = "font-style: italic",    underline     = "text-decoration: underline",    letterspacing = "letter-spacing: 0.1em",    wordspacing   = "word-spacing: 0.5em",    smallcaps     = "font-variant: small-caps",    uppercase     = "text-transform: uppercase",    region        = "font-weight: bold; text-transform: uppercase; color: #646464",    subregion     = "font-weight: bold; color: #646464",    waterbody     = "font-weight: bold; font-style: italic; letter-spacing: 0.1em; text-transform: uppercase; color: #2A6DB5",    mountain      = "font-weight: bold; font-style: italic; letter-spacing: 0.1em; color: #704040",

}

local labelPositions1 = {

   ["1"]         = "left: 0; bottom: msize_px;",    ["2"]         = "left: msize3_px; bottom: -2px;",    ["3"]         = "left: msize5_px; top: -2em; height: 4em;",    right         = "left: msize5_px; top: -2em; height: 4em;",    rechts        = "left: msize5_px; top: -2em; height: 4em;",    ["4"]         = "left: msize3_px; top: -2px;",    ["5"]         = "left: 0; top: msize_px;",    ["6"]         = "left: -3em; top: msize3_px;",    bottom        = "left: -3em; top: msize3_px;",    unten         = "left: -3em; top: msize3_px;",    ["7"]         = "right: 0; top: msize_px;",    ["8"]         = "right: msize3_px; top: -2px;",    ["9"]         = "right: msize5_px; top: -2em; height: 4em;",    left          = "right: msize5_px; top: -2em; height: 4em;",    links         = "right: msize5_px; top: -2em; height: 4em;",    ["10"]        = "right: msize3_px; bottom: -2px;",    ["11"]        = "right: 0; bottom: msize_px;",    ["12"]        = "left: -3em; bottom: msize3_px;",    top           = "left: -3em; bottom: msize3_px;",    oben          = "left: -3em; bottom: msize3_px;",    center        = "top: -2em; height: 4em; left: -3em;",    mitte         = "top: -2em; height: 4em; left: -3em;",

}

local labelPositions2 = {

   ["1"]         = "text-align: left;",    ["2"]         = "text-align: left;",    ["3"]         = "text-align: left; height: 4em;",    right         = "text-align: left; height: 4em;",    rechts        = "text-align: left; height: 4em;",    ["4"]         = "text-align: left;",    ["5"]         = "text-align: left;",    ["6"]         = "text-align: center;",    bottom        = "text-align: center;",    unten         = "text-align: center;",    ["7"]         = "text-align: right;",    ["8"]         = "text-align: right;",    ["9"]         = "text-align: right; height: 4em;",    left          = "text-align: right; height: 4em;",    links         = "text-align: right; height: 4em;",    ["10"]        = "text-align: right;",    ["11"]        = "text-align: right;",    ["12"]        = "text-align: center;",    top           = "text-align: center;",    oben          = "text-align: center;",    center        = "text-align: center; height: 4em;",    mitte         = "text-align: center; height: 4em;",

}

-- Local functions, please do not call them directly

local function split(s)

   local split = mw.text.split(s, ';')    local result = {}    for key,value in pairs(split) do        tr = mw.text.trim(value)        if tr ~=  then table.insert(result, tr) end    end    return result;

end

local function analyzeLabelStyle(style)

   local split = split(style)    for key,value in pairs(split) do        if labelStyles[value] ~= nil then split[key] = labelStyles[value] end    end    return table.concat(split, '; ') .. ';'

end

local function analyzeMapStyle(style)

   local split = split(style)    for key,value in pairs(split) do        if mapStyles[value] ~= nil then split[key] = mapStyles[value] end    end    return table.concat(split, '; ') .. ';'

end

local function setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)

   local lmarksize = math.floor(marksize + 0.5)    local msize = math.floor((marksize - 1) / 2 + 0.5)    local msize3 = math.floor((marksize + 2) / 2 + 0.5)    local msize5 = math.floor((marksize + 4) / 2 + 0.5)    local halfMarkSize = -msize .. 'px'
   -- Setting a marker

local sCode = '

'
   if mark ~= 'none' then
sCode = sCode .. '
'
       .. '' .. name .. ''
.. '
'
   end
   -- Adding a label    if (label ~= ) and (label ~= 'none') then
sCode = sCode .. '

<td style="border: none; padding: 0; vertical-align: middle; ' .. labelPositions2[labelPosition] else -- Automatic Estimation if y<=0.5 then sCode = sCode .. 'top: ' .. msize3 .. 'px; ' else sCode = sCode .. 'bottom: ' .. msize3 .. 'px; ' end if x<0.25 then sCode = sCode .. 'text-align: left; left: ' .. math.floor(3 - 60*x)/10 .. 'em;' .. '"><td style="border: none; padding: 0; vertical-align: middle; text-align: left;' else if x<0.75 then sCode = sCode .. 'text-align: center; left: -3em;' .. '">

' .. label .. '
'
   end
sCode = sCode .. '

'

   return sCode

end

local function baseMap(mapImage, description, mapStyle, width, caption, captionStyle, captionInnerBorder, captionOuterBorder, x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition, places)

local sCode = '

' if caption ~= then sCode = sCode .. '' end sCode = sCode .. '

' else sCode = sCode .. '0;">' end sCode = sCode .. '
' else sCode = sCode .. ' width: auto !important;">' end sCode = sCode .. '
' .. description .. '
' .. description .. '
'
   if (x<0) or (x>1) or (y<0) or (y>1) then
sCode = sCode .. '
' .. 'Fehlerhafte Koordinate ' .. name .. '
'
   else        sCode = sCode .. setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)    end
sCode = sCode .. places .. '
' .. caption .. '

'

   return sCode

end

-- Handling regional map data

local function getMapData(id)

   local region = require('Modul:Location map data ' .. id)    if (region ~= nil) and (region.data ~= nil) then        region.data['id'] = id        return region.data    else        return nil    end

end

local function getMapObject(id)

   local region = require('Modul:Location map data ' .. id)    if (region ~= nil) and (region.data ~= nil) then        region.data['id'] = id        return region    else        return nil    end

end

local function linearX(data, long)

   local left = data['left']    local right = data['right']    if (data == nil) or (left == nil) or (right == nil) or  (left == right) then        -- Error        return -1    else if left < right then            return (long - left) / (right - left)        else            if long < 0 then                return (360 + long - left) / (360 + right - left)            else                return (long - left) / (360 + right - left)            end        end    end

end

local function linearY(data, lat)

   local top = data['top']    local bottom = data['bottom']    if (data == nil) or (top == nil) or (bottom == nil) or  (top == bottom) then        -- Error        return -1    else        return (lat - top) / (bottom - top)    end

end

local function getX(anObject, long, lat)

   if anObject.x ~= nil then        return anObject.x(lat, long)    else    	return linearX(anObject.data, long)    end

end

local function getY(anObject, long, lat)

   if anObject.y ~= nil then        return anObject.y(lat, long)    else    	return linearY(anObject.data, lat)    end

end

local function getMapImage(data, which)

   local image = data['default']    if (which ~= ) and (data[which] ~= nil) and (data[which] ~=  ) then        image = data[which]    end    return image

end

-- Parameters and error handling

local function argCheck(param, altValue)

   if param == nil then        return altValue    else        local val = mw.text.trim(param)        if val ==  then val = altValue end        return val    end

end

local function errorStr(s)

   return 'Fehler im Modul Location map: ' .. s .. ''

end

-- Map functions

local function apiLocationMap(args)

   local map = argCheck(args['map'], 'missing')    local success, mObject = pcall(getMapObject, map)    if not success then        return errorStr(string.format(errMsgs['unknownMap'], map))    else        -- Error handling        local errorMsgs = {}        success = true                -- Parameters check        local mData = mObject.data        local description =         if mData['description'] ~= nil then description = mData['description'] end
       local lat = tonumber(argCheck(tostring(args['lat']), 0))        local long = tonumber(argCheck(tostring(args['long']), 0))        local x = getX(mObject, long, lat)        if (x<0) or (x>1) then            success = false            if x == -1 then                table.insert(errorMsgs, errorStr(errMsgs['wrongXBorders']))            else                table.insert(errorMsgs, errorStr(string.format(errMsgs['wrongLong'], long)))            end        end        local y = getY(mObject, long, lat)        if (y<0) or (y>1) then            success = false            if y == -1 then                table.insert(errorMsgs, errorStr(errMsgs['wrongYBorders']))            else                table.insert(errorMsgs, errorStr(string.format(errMsgs['wrongLat'], lat)))            end        end
       local maptype = argCheck(args['maptype'], 'default')        local mapImage = argCheck(args['alternativeMap'], getMapImage(mData, maptype))        if (mapImage == nil) or (mapImage == ) then            success = false            table.insert(errorMsgs, errorStr(errMsgs['noMapImage']))        end        if not success then            return table.concat(errorMsgs, '
') else local name = argCheck(args['name'], ) local label = argCheck(args['label'], ) -- Checking width syntax local width = argCheck(tostring(args['width']), ) if (string.match(width, '^%d+$') == nil) and (string.match(width, '^%d*x%d+$') == nil) then width = '200x200' end if mData['mark'] ~= nil then mark = mData['mark'] else mark = 'Reddot.svg' end mark = argCheck(args['mark'], mark) if mData['marksize'] ~= nil then marksize = mData['marksize'] else marksize = 5 end local marksize = argCheck(args['marksize'], marksize) local mapStyle = argCheck(args['mapStyle'], 'mitte') local labelStyle = argCheck(args['labelStyle'], ) local labelBackground = argCheck(args['labelBackground'], ) if labelBackground ~= then labelBackground = 'background: ' .. labelBackground if labelStyle ~= then labelStyle = labelStyle .. '; ' .. labelBackground else labelStyle = labelBackground end end local labelWrap = argCheck(args['labelWrap'], 'auto') local labelPosition = argCheck(args['labelPosition'], 'auto') local caption = argCheck(args['caption'], ) local captionStyle = argCheck(args['captionStyle'], ) local captionInnerBorder = argCheck(args['captionInnerBorder'], '1px solid #cccccc') local captionOuterBorder = argCheck(args['captionOuterBorder'], '1px solid #cccccc') local places = argCheck(args['places'], )
           return baseMap(mapImage, description, mapStyle, width, caption, captionStyle,            	captionInnerBorder, captionOuterBorder, x, y, name, label, mark, marksize,            	labelStyle, labelWrap, labelPosition, places)        end    end

end

local function apiAddLocation(args)

   local map = argCheck(args['map'], 'missing')    local success, mObject = pcall(getMapObject, map)    if not success then        return errorStr(string.format(errMsgs['unknownMap'], map))    else        -- Error handling        local errorMsgs = {}        success = true
       -- Parameters check        local mData = mObject.data        local lat = tonumber(argCheck(tostring(args['lat']), 0))        local long = tonumber(argCheck(tostring(args['long']), 0))        local x = getX(mObject, long, lat)        if (x<0) or (x>1) then            success = false            if x == -1 then                table.insert(errorMsgs, errorStr(errMsgs['wrongXBorders']))            else                table.insert(errorMsgs, errorStr(string.format(errMsgs['wrongLong'], long)))            end        end        local y = getY(mObject, long, lat)        if (y<0) or (y>1) then            success = false            if y == -1 then                table.insert(errorMsgs, errorStr(errMsgs['wrongYBorders']))            else                table.insert(errorMsgs, errorStr(string.format(errMsgs['wrongLat'], lat)))            end        end        if not success then            return table.concat(errorMsgs, '
') else local name = argCheck(args['name'], ) local label = argCheck(args['label'], )
           if mData['mark'] ~= nil then mark = mData['mark'] else mark = 'Reddot.svg' end            mark = argCheck(args['mark'], mark)            if mData['marksize'] ~= nil then marksize = mData['marksize'] else marksize = 5 end            local marksize = argCheck(args['marksize'], marksize)            local mapStyle = argCheck(args['mapStyle'], 'mitte')            local labelStyle = argCheck(args['labelStyle'], )            local labelBackground = argCheck(args['labelBackground'], )            if labelBackground ~= then                labelBackground = 'background: ' .. labelBackground                if labelStyle ~= then                    labelStyle = labelStyle .. '; ' .. labelBackground                else                    labelStyle = labelBackground                end            end            local labelWrap = argCheck(args['labelWrap'], 'auto')            local labelPosition = argCheck(args['labelPosition'], 'auto')
           return setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)        end    end

end

local function apiAddObject(args)

   local anObject = argCheck(args['object'], )    if anOject ==  then        return errorStr(errMsgs['noObject'])    else        local success = true        local errorMsgs = {}                local right = argCheck(args['right'], )        local left = argCheck(args['left'], )        if (right == ) and (left == ) then            success = false            table.insert(errorMsgs, errorStr(errMsgs['noXPos']))        end        local top = argCheck(args['top'], )        local bottom = argCheck(args['bottom'], )        if (top == ) and (bottom == ) then            success = false            table.insert(errorMsgs, errorStr(errMsgs['noYPos']))        end        if not success then            return table.concat(errorMsgs, '
') else local objectStyle = argCheck(args['objectStyle'], ) local objectBackground = argCheck(args['objectBackground'], ) if objectBackground ~= then objectBackground = 'background: ' .. objectBackground if objectStyle ~= then objectStyle = objectStyle .. '; ' .. objectBackground else objectStyle = objectBackground end end

sCode = '

' .. anObject .. '

'

           return sCode        end    end

end

-- Documentation of map data

local function apiGetMapValue(args)

   local map = argCheck(args['map'], 'missing')    local success, mData = pcall(getMapData, map)    if not success then        return errorStr(string.format(errMsgs['unknownMap'], map))    else        local param = argCheck(args['param'], )        if param ==  then            return errorStr(errMsgs['noParam'])        else            if mData[param] == nil then                return errMsgs['anError']            else                return mData[param]            end        end    end

end

local function apiGetMapValueSet(args)

   local map = argCheck(args['map'], 'missing')    local success, mData = pcall(getMapData, map)    if not success then        return errorStr(string.format(errMsgs['unknownMap'], map))    else        local paramList = {'name', 'description', 'projection', 'top', 'bottom',           'left', 'right', 'default', 'relief', 'mark', 'marksize'}

local sCode = '

' for i = 1, #paramList, 1 do sCode = sCode .. '' else local v = mData[paramList[i]] if mapDocs[v] ~= nil then v = mapDocs[v] end sCode = sCode .. v .. '' end end return sCode .. '

' .. mapDocs[paramList[i]] .. ''
           if mData[paramList[i]] == nil then
sCode = sCode .. errMsgs['notDefined'] .. '

'

   end

end

-- API function calls

locMap[api.apiLocationMap] = function (frame)

   return apiLocationMap(frame.args)

end

locMap[api.apiAddLocation] = function (frame)

   return apiAddLocation(frame.args)

end

locMap[api.apiAddObject] = function (frame)

   return apiAddObject(frame.args)

end

locMap[api.apiGetMapValue] = function (frame)

   return apiGetMapValue(frame.args)

end

locMap[api.apiGetMapValueSet] = function (frame)

   return apiGetMapValueSet(frame.args)

end

-- Example for usage in a Lua script

function locMap.exampleLuaCall()

   local frame = {}    frame.args = {        map   = 'de',        lat   = 52.51789,        long  = 13.38873,        name  = 'Berlin',        label = 'Berlin',    }    return locMap.locationMap(frame)

end

return locMap