if not modules then modules = { } end modules ['mtx-server-ctx-help'] = {
    version   = 1.001,
    comment   = "Basic Definition Browser",
    author    = "Hans Hagen",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- todo in lua interface: noargument, oneargument, twoarguments, threearguments
-- todo: pickup translations from mult file

dofile(resolvers.findfile("trac-lmx.lua","tex"))

-- problem ... serialize parent stack

local format, match, gsub, find = string.format, string.match, string.gsub, string.find
local concat = table.concat

local report = logs.reporter("ctx-help")

-- -- -- make this a module: cont-xx.lua

document = document or { }
document.setups = document.setups or { }

document.setups.div = {
    pe = "<div dir='rtl' lang='arabic'>%s</div>"
}

document.setups.span = {
    pe = "<span dir='rtl' lang='arabic'>%s</span>"
}

document.setups.translations =  document.setups.translations or {

    nl = {
        ["title"]       = "setup",
        ["formula"]     = "formule",
        ["number"]      = "getal",
        ["list"]        = "lijst",
        ["dimension"]   = "maat",
        ["mark"]        = "markering",
        ["reference"]   = "verwijzing",
        ["command"]     = "commando",
        ["file"]        = "file",
        ["name"]        = "naam",
        ["identifier"]  = "naam",
        ["text"]        = "tekst",
        ["section"]     = "sectie",
        ["singular"]    = "naam enkelvoud",
        ["plural"]      = "naam meervoud",
        ["matrix"]      = "n*m",
        ["see"]         = "zie",
        ["inherits"]    = "erft van",
        ["optional"]    = "optioneel",
        ["displaymath"] = "formule",
        ["index"]       = "ingang",
        ["math"]        = "formule",
        ["nothing"]     = "leeg",
        ["file"]        = "file",
        ["position"]    = "positie",
        ["reference"]   = "verwijzing",
        ["csname"]      = "naam",
        ["destination"] = "bestemming",
        ["triplet"]     = "triplet",
        ["word"]        = "woord",
        ["content"]     = "tekst",
    },

    en = {
        ["title"]       = "setup",
        ["formula"]     = "formula",
        ["number"]      = "number",
        ["list"]        = "list",
        ["dimension"]   = "dimension",
        ["mark"]        = "mark",
        ["reference"]   = "reference",
        ["command"]     = "command",
        ["file"]        = "file",
        ["name"]        = "name",
        ["identifier"]  = "identifier",
        ["text"]        = "text",
        ["section"]     = "section",
        ["singular"]    = "singular name",
        ["plural"]      = "plural name",
        ["matrix"]      = "n*m",
        ["see"]         = "see",
        ["inherits"]    = "inherits from",
        ["optional"]    = "optional",
        ["displaymath"] = "formula",
        ["index"]       = "entry",
        ["math"]        = "formula",
        ["nothing"]     = "empty",
        ["file"]        = "file",
        ["position"]    = "position",
        ["reference"]   = "reference",
        ["csname"]      = "name",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "word",
        ["content"]     = "text",

        ["noargument"]     = "\\cs",
        ["oneargument"]    = "\\cs#1{..}",
        ["twoarguments"]   = "\\cs#1#2{..}{..}",
        ["threearguments"] = "\\cs#1#2#3{..}{..}{..}",

    },

    de = {
        ["title"]       = "Setup",
        ["formula"]     = "Formel",
        ["number"]      = "Nummer",
        ["list"]        = "Liste",
        ["dimension"]   = "Dimension",
        ["mark"]        = "Beschriftung",
        ["reference"]   = "Referenz",
        ["command"]     = "Befehl",
        ["file"]        = "Datei",
        ["name"]        = "Name",
        ["identifier"]  = "Name",
        ["text"]        = "Text",
        ["section"]     = "Abschnitt",
        ["singular"]    = "singular",
        ["plural"]      = "plural",
        ["matrix"]      = "n*m",
        ["see"]         = "siehe",
        ["inherits"]    = "inherits from",
        ["optional"]    = "optioneel",
        ["displaymath"] = "formula",
        ["index"]       = "entry",
        ["math"]        = "formula",
        ["nothing"]     = "empty",
        ["file"]        = "file",
        ["position"]    = "position",
        ["reference"]   = "reference",
        ["csname"]      = "name",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "word",
        ["content"]     = "text",
    },

    cz = {
        ["title"]       = "setup",
        ["formula"]     = "rovnice",
        ["number"]      = "cislo",
        ["list"]        = "seznam",
        ["dimension"]   = "dimenze",
        ["mark"]        = "znacka",
        ["reference"]   = "reference",
        ["command"]     = "prikaz",
        ["file"]        = "soubor",
        ["name"]        = "jmeno",
        ["identifier"]  = "jmeno",
        ["text"]        = "text",
        ["section"]     = "sekce",
        ["singular"]    = "jmeno v singularu",
        ["plural"]      = "jmeno v pluralu",
        ["matrix"]      = "n*m",
        ["see"]         = "viz",
        ["inherits"]    = "inherits from",
        ["optional"]    = "optioneel",
        ["displaymath"] = "formula",
        ["index"]       = "entry",
        ["math"]        = "formula",
        ["nothing"]     = "empty",
        ["file"]        = "file",
        ["position"]    = "position",
        ["reference"]   = "reference",
        ["csname"]      = "name",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "word",
        ["content"]     = "text",
    },

    it = {
        ["title"]       = "setup",
        ["formula"]     = "formula",
        ["number"]      = "number",
        ["list"]        = "list",
        ["dimension"]   = "dimension",
        ["mark"]        = "mark",
        ["reference"]   = "reference",
        ["command"]     = "command",
        ["file"]        = "file",
        ["name"]        = "name",
        ["identifier"]  = "name",
        ["text"]        = "text",
        ["section"]     = "section",
        ["singular"]    = "singular name",
        ["plural"]      = "plural name",
        ["matrix"]      = "n*m",
        ["see"]         = "see",
        ["inherits"]    = "inherits from",
        ["optional"]    = "optioneel",
        ["displaymath"] = "formula",
        ["index"]       = "entry",
        ["math"]        = "formula",
        ["nothing"]     = "empty",
        ["file"]        = "file",
        ["position"]    = "position",
        ["reference"]   = "reference",
        ["csname"]      = "name",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "word",
        ["content"]     = "text",
    },

    ro = {
        ["title"]       = "setari",
        ["formula"]     = "formula",
        ["number"]      = "numar",
        ["list"]        = "lista",
        ["dimension"]   = "dimensiune",
        ["mark"]        = "marcaj",
        ["reference"]   = "referinta",
        ["command"]     = "comanda",
        ["file"]        = "fisier",
        ["name"]        = "nume",
        ["identifier"]  = "nume",
        ["text"]        = "text",
        ["section"]     = "sectiune",
        ["singular"]    = "nume singular",
        ["plural"]      = "nume pluram",
        ["matrix"]      = "n*m",
        ["see"]         = "vezi",
        ["inherits"]    = "inherits from",
        ["optional"]    = "optioneel",
        ["displaymath"] = "formula",
        ["index"]       = "entry",
        ["math"]        = "formula",
        ["nothing"]     = "empty",
        ["file"]        = "file",
        ["position"]    = "position",
        ["reference"]   = "reference",
        ["csname"]      = "name",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "word",
        ["content"]     = "text",
    },

    fr = {
        ["title"]       = "réglage",
        ["formula"]     = "formule",
        ["number"]      = "numéro",
        ["list"]        = "liste",
        ["dimension"]   = "dimension",
        ["mark"]        = "marquage",
        ["reference"]   = "reference",
        ["command"]     = "commande",
        ["file"]        = "fichier",
        ["name"]        = "nom",
        ["identifier"]  = "identificateur",
        ["text"]        = "texte",
        ["section"]     = "section",
        ["singular"]    = "nom singulier",
        ["plural"]      = "nom pluriel",
        ["matrix"]      = "n*m",
        ["see"]         = "vois",
        ["inherits"]    = "herite de",
        ["optional"]    = "optionel",
        ["displaymath"] = "formule",
        ["index"]       = "entrée",
        ["math"]        = "formule",
        ["nothing"]     = "vide",
        ["file"]        = "fichier",
        ["position"]    = "position",
        ["reference"]   = "réference",
        ["csname"]      = "nom",
        ["destination"] = "destination",
        ["triplet"]     = "triplet",
        ["word"]        = "mot",
        ["content"]     = "texte",
    }

}

document.setups.formats = {
    open_command    = {
        tex = [[\%s]],
        lua = [[context.%s (]],
    },
    close_command   = {
        tex = [[]],
        lua = [[ )]],
    },
    connector       = {
        tex = [[]],
        lua = [[, ]],
    },
    href_in_list    = {
        tex = [[<a href='mtx-server-ctx-help.lua?command=%s&mode=%s'>%s</a>]],
        lua = [[<a href='mtx-server-ctx-help.lua?command=%s&mode=%s'>%s</a>]],
    },
    href_as_command = {
        tex = [[<a href='mtx-server-ctx-help.lua?command=%s&mode=%s'>\%s</a>]],
        lua = [[<a href='mtx-server-ctx-help.lua?command=%s&mode=%s'>context.%s</a>]],
    },
    modes           = {
        tex = [[<a href='mtx-server-ctx-help.lua?mode=lua'>lua mode</a>]],
        lua = [[<a href='mtx-server-ctx-help.lua?mode=tex'>tex mode</a>]],
    },
    optional_single = {
        tex = "[optional string %s]",
        lua = "{optional string %s}",
    },
    optional_list   = {
        tex = "[optional list %s]",
        lua = "{optional table %s}" ,
    } ,
    mandate_single  = {
        tex = "[mandate string %s]",
        lua = "{mandate string %s}",
    },
    mandate_list    = {
        tex = "[mandate list %s]",
        lua = "{mandate list %s}",
    },
    interface       = [[<a href='mtx-server-ctx-help.lua?interface=%s&mode=%s'>%s</a>]],
    source          = [[<a href='mtx-server-ctx-help.lua?source=%s&mode=%s'>%s</a>]],
    parameter       = [[<tr><td width='15%%'>%s</td><td width='15%%'>%s</td><td width='70%%'>%s</td></tr>]],
    parameters      = [[<table width='100%%'>%s</table>]],
    listing         = [[<pre><t>%s</t></listing>]],
    special         = [[<i>%s</i>]],
    default         = [[<u>%s</u>]],
}

local function translate(tag,int,noformat)
    local t = document.setups.translations
    local te = t["en"]
    local ti = t[int] or te
    if noformat then
        return ti[tag] or te[tag] or tag
    else
        return format(document.setups.formats.special,ti[tag] or te[tag] or tag)
    end
end

local function translated(e,int)
    local attributes = e.at
    local s = attributes.type or "?"
    local tag = match(s,"^cd:(.*)$")
    if attributes.default == "yes" then
        return format(document.setups.formats.default,tag or "?")
    elseif tag then
        return translate(tag,int)
    else
        return s
    end
end

document.setups.loaded = document.setups.loaded or { }

document.setups.current = { }
document.setups.showsources = true
document.setups.mode = "tex"

function document.setups.load(filename)
    filename = resolvers.findfile(filename) or ""
    if filename ~= "" then
        local current = document.setups.loaded[filename]
        if not current then
            local loaded = xml.load(filename)
            if loaded then
                -- xml.inject(document.setups.root,"/",loaded)
                current = {
                    file = filename,
                    root = loaded,
                    names = { },
                    used = { },
                }
                document.setups.loaded[filename] = current
            end
        end
        document.setups.current = current or { }
    end
end

function document.setups.name(ek)
    local at = ek.at
    local name = at.name
    if at.type == 'environment' then
        name = "start" .. name
    end
    if at.variant then
        name = name .. ":" .. at.variant
    end
    if at.generated == "yes" then
        name = name .. "*"
    end
    return name:lower()
end

function document.setups.csname(ek,int)
    local cs = ""
    local at = ek.at or { }
    if at.type == 'environment' then
        cs = translate("start",int,true) .. cs
    end
    for e in xml.collected(ek,'cd:sequence/(cd:string|variable)') do
        if e.tg == "string" then
            cs = cs .. e.at.value
        else
            cs = cs .. e.at.value -- to be translated
        end
    end
    return cs
end

function document.setups.names()
    local current = document.setups.current
    local names = current.names
    if not names or #names == 0 then
        names = { }
        local name = document.setups.name
        local csname = document.setups.csname
        for e in xml.collected(current.root,'cd:command') do
            names[#names+1] = { e.at.name, csname(e,int) }
        end
        table.sort(names, function(a,b) return a[2]:lower() < b[2]:lower() end)
        current.names = names
    end
    return names
end

function document.setups.show(name)
    local current = document.setups.current
    if current.root then
        local name = gsub(name,"[<>]","")
        local setup = xml.first(current.root,"cd:command[@name='" .. name .. "']")
        current.used[#current.used+1] = setup
        xml.sprint(setup)
    end
end

function document.setups.showused()
    local current = document.setups.current
    if current.root and next(current.used) then
        local sorted = table.sortedkeys(current.used)
        for i=1,#sorted do
            xml.sprint(current.used[sorted[i]])
        end
    end
end
function document.setups.showall()
    local current = document.setups.current
    if current.root then
        local list = { }
        for e in xml.collected(current.root,"cd:command") do
            list[document.setups.name(e)] = e
        end
        local sorted = table.sortedkeys(list)
        for i=1,#sorted do
            xml.sprint(list[sorted[i]])
        end
    end
end
function document.setups.resolve(name)
    local current = document.setups.current
    if current.root then
        local e = xml.filter(current.root,format("cd:define[@name='%s']/text()",name))
        if e then
            xml.sprint(e)
        end
    end
end

function document.setups.collect(name,int,lastmode)
    local current = document.setups.current
    local formats = document.setups.formats
    local command = xml.filter(current.root,format("cd:command[@name='%s']/first()",name))
    if command then
        local attributes = command.at or { }
        local data = {
            command = command,
            category = attributes.category or "",
        }
        if document.setups.showsources then
            data.source = (attributes.file and format(formats.source,attributes.file,lastmode,attributes.file)) or ""
        else
            data.source = attributes.file or ""
        end
        local n, sequence, tags = 0, { }, { }
        sequence[#sequence+1] = format(formats.open_command[lastmode],document.setups.csname(command,int))
        local arguments, tag = { }, ""
        for r, d, k in xml.elements(command,"(cd:keywords|cd:assignments)") do
            n = n + 1
            local attributes = d[k].at
            if #sequence > 1 then
                local c = formats.connector[lastmode]
                if c ~= "" then
                    sequence[#sequence+1] = c
                end
            end
            if attributes.optional == 'yes' then
                if attributes.list == 'yes' then
                    tag = format(formats.optional_list[lastmode],n)
                else
                    tag = format(formats.optional_single[lastmode],n)
                end
            else
                if attributes.list == 'yes' then
                    tag = format(formats.mandate_list[lastmode],n)
                else
                    tag = format(formats.mandate_single[lastmode],n)
                end
            end
            sequence[#sequence+1] = tag
            tags[#tags+1] = tag
        end
        sequence[#sequence+1] = formats.close_command[lastmode]
        data.sequence = concat(sequence, " ")
        local parameters, n = { }, 0
        for r, d, k in xml.elements(command,"(cd:keywords|cd:assignments)") do
            n = n + 1
            if d[k].tg == "keywords" then
                local left = tags[n]
                local right = { }
                for r, d, k in xml.elements(d[k],"(cd:constant|cd:resolve)") do
                    local tag = d[k].tg
                    if tag == "resolve" then
                        local name = d[k].at.name or ""
                        if name ~= "" then
                            local resolved = xml.filter(current.root,format("cd:define[@name='%s']",name))
                            for r, d, k in xml.elements(resolved,"cd:constant") do
                                right[#right+1] = translated(d[k],int)
                            end
                        end
                    else
                        right[#right+1] = translated(d[k],int)
                    end
                end
                parameters[#parameters+1] = format(formats.parameter,left,"",concat(right, ", "))
            else
                local what = tags[n]
                for r, d, k in xml.elements(d[k],"(cd:parameter|cd:inherit)") do
                    local tag = d[k].tg
                    local left, right = d[k].at.name or "?", { }
                    if tag == "inherit" then
                        local name = d[k].at.name or "?"
                        local url  = format(document.setups.formats.href_as_command[lastmode],name,lastmode,name)
                        if #parameters > 0 and not find(parameters[#parameters],"<br/>") then
                            parameters[#parameters+1] = format(formats.parameter,"<br/>","","")
                        end
                        parameters[#parameters+1] = format(formats.parameter,what,format(formats.special,translate("inherits",int)),url)
                    else
                        for r, d, k in xml.elements(d[k],"(cd:constant|cd:resolve)") do
                            local tag = d[k].tg
                            if tag == "resolve" then
                                local name = d[k].at.name or ""
                                if name ~= "" then
                                    local resolved = xml.filter(current.root,format("cd:define[@name='%s']",name))
                                    for r, d, k in xml.elements(resolved,"cd:constant") do
                                        right[#right+1] = translated(d[k],int)
                                    end
                                end
                            else
                                right[#right+1] = translated(d[k],int)
                            end
                        end
                        parameters[#parameters+1] = format(formats.parameter,what,left,concat(right, ", "))
                    end
                    what = ""
                end
            end
            parameters[#parameters+1] = format(formats.parameter,"<br/>","","")
        end
        data.parameters = parameters or { }
        data.mode = formats.modes[lastmode or "tex"]
        return data
    else
        return nil
    end
end

-- -- --

tex = tex or { }

-- -- --

local interfaces = {
    czech    = 'cz',
    dutch    = 'nl',
    english  = 'en',
    french   = 'fr',
    german   = 'de',
    italian  = 'it',
    persian  = 'pe',
    romanian = 'ro',
}

local lastinterface, lastcommand, lastsource, lastmode = "en", "", "", "tex"

local variables = {
    ['color-background-main-left']  = '#3F3F3F',
    ['color-background-main-right'] = '#5F5F5F',
    ['color-background-one']        = lmx.get('color-background-green'),
    ['color-background-two']        = lmx.get('color-background-blue'),
    ['title']                       = 'ConTeXt Help Information',
}

--~ function lmx.loadedfile(filename)
--~     return io.loaddata(resolvers.findfile(filename)) -- return resolvers.texdatablob(filename)
--~ end

local function doit(configuration,filename,hashed)

    local formats = document.setups.formats

    local start   = os.clock()
    local detail  = hashed.queries or { }

    if detail then

        lastinterface = detail.interface or lastinterface
        lastcommand   = detail.command   or lastcommand
        lastsource    = detail.source    or lastsource
        lastmode      = detail.mode      or lastmode or "tex"

        lastcommand = gsub(lastcommand,"%s*^\\*(.+)%s*","%1")

        if lastinterface then
            report("checking interface: %s",lastinterface)
            document.setups.load(format("cont-%s.xml",lastinterface))
        end

        local div  = document.setups.div [lastinterface]
        local span = document.setups.span[lastinterface]

        local names, refs, ints = document.setups.names(lastinterface), { }, { }
        for k=1,#names do
            local v = names[k]
            refs[k] = format(formats.href_in_list[lastmode],v[1],lastmode,v[2])
        end
        if lastmode ~= "lua" then
            local sorted = table.sortedkeys(interfaces)
            for k=1,#sorted do
                local v = sorted[k]
                ints[k] = format(formats.interface,interfaces[v],lastmode,v)
            end
        end

        local n = concat(refs,"<br/>")
        local i = concat(ints,"<br/><br/>")

        if div then
            variables.names      = format(div,n)
            variables.interfaces = format(div,i)
        else
            variables.names      = n
            variables.interfaces = i
        end

        -- first we need to add information about mkii/mkiv

        variables.maintitle = "no definition"
        variables.maintext  = ""
        variables.extra     = ""

        if document.setups.showsources and lastsource and lastsource ~= "" then
            -- todo: mkii, mkiv, tex (can be different)
            local name = lastsource
            local full = resolvers.findfile(name)
            if full == "" and file.suffix(lastsource) == "tex" then
                name = file.replacesuffix(lastsource,"mkiv")
                full = resolvers.findfile(name)
                if full  == "" then
                    name = file.replacesuffix(lastsource,"mkvi")
                    full = resolvers.findfile(name)
                end
            end
            if full == "" then
                variables.maintitle = lastsource
                variables.maintext  = format(formats.listing,"no source found")
            else
                variables.maintitle = name
                variables.maintext  = format(formats.listing,io.loaddata(full))
            end
            lastsource = ""
        elseif lastcommand and lastcommand ~= "" then
            local data = document.setups.collect(lastcommand,lastinterface,lastmode)
            if data then
                local what, extra = { "environment", "category", "source", "mode" }, { }
                for k=1,#what do
                    local v = what[k]
                    if data[v] and data[v] ~= "" then
                        lmx.set(v, data[v])
                        extra[#extra+1] = v .. ": " .. data[v]
                    end
                end
                variables.maintitle = data.sequence
                variables.maintext  = format(formats.parameters,concat(data.parameters))
                variables.extra     = concat(extra,"&nbsp;&nbsp;&nbsp;")
            else
                variables.maintext = "select command"
            end
        end

    else

        variables.maintitle = "no definition"
        variables.maintext  = "some error"
        variables.extra     = ""

    end

    local content = lmx.convert('context-help.lmx',false,variables)

    report("time spent on page: %0.03f seconds",os.clock()-start)

    return { content = content }
end

return doit, true
