if not modules then modules = { } end modules ['cldf-int'] = {
    version   = 1.001,
    comment   = "companion to cldf-int.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- another experiment
-- needs upgrading
-- needs checking
-- todo: multilingual

local byte = string.byte
local insert, remove, concat = table.insert, table.remove, table.concat
local unpack, type = unpack or table.unpack, type

local context        = context
local contextsprint  = context.sprint

local ctxcatcodes    <const> = catcodes.numbers.ctxcatcodes

local trace_define   = false  trackers.register("context.define", function(v) trace_define = v end)

interfaces           = interfaces or { }
local implement      = interfaces.implement
local estart         = interfaces.elements.start
local estop          = interfaces.elements.stop

local scanners       = tokens.scanners
local shortcuts      = tokens.shortcuts

local peekchar       = scanners.peekchar
local scankey        = scanners.key
local scanvalue      = scanners.value
local scanskip       = scanners.skip

local open           <const> = byte('[')
local close          <const> = byte(']')
local equal          <const> = byte('=')
local comma          <const> = byte(',')

local function scanhash(scanners)
    if peekchar() == open then
        local data = { }
        scanskip()
        while true do
            local c = peekchar()
            if c == comma then
                scanskip()
            elseif c == close then
                scanskip()
                break
            else
                local key = scankey(equal)
                if key then
                    if peekchar() == equal then
                        scanskip()
                        if scanners then
                            local scanner = scanners[key]
                            if scanner then
                                data[key] = scanner()
                            else
                                data[key] = scanvalue(comma,close) or ""
                            end
                        else
                            data[key] = scanvalue(comma,close) or ""
                        end
                    else
                        break
                    end
                else
                    break
                end
            end
        end
        return data
    end
end

local function scanarray()
    if peekchar() == open then
        local data = { }
        local d = 0
        scanskip()
        while true do
            local c = peekchar()
            if c == comma then
                scanskip()
            elseif c == close then
                scanskip()
                break
            else
                local v = scanvalue(comma,close) or ""
                d = d + 1
                data[d] = v
            end
        end
        return data
    end
end

shortcuts.scanhash  = scanhash
shortcuts.scanarray = scanarray

scanners.hash  = scanhash
scanners.array = scanarray

local function remap(arguments)
    -- backward compatibility
    if type(arguments) == "table" then
        for i=1,#arguments do
            local a = arguments[i]
            if type(a) == "table" then
                local t = a[2]
                arguments[i] = t == "list" and "array" or t
            end
        end
        return arguments
    end
end

function interfaces.definecommand(name,specification) -- name is optional
    if type(name) == "table" then
        specification = name
        name = specification.name
    end
    if name and specification then
        local environment = specification.environment
        local arguments   = remap(specification.arguments)
        if environment then
            local starter = specification.starter
            local stopper = specification.stopper
            if starter and stopper then
                implement {
                    name      = estart .. name,
                    arguments = arguments,
                    public    = true,
                    protected = true,
                    actions   = starter,
                }
                implement {
                    name      = estop .. name,
                    public    = true,
                    protected = true,
                    actions   = stopper,
                }
            else
                -- message
            end
        end
        if not environment or environment == "both" then
            local macro = specification.macro
            if macro then
                implement {
                    name      = name,
                    arguments = arguments,
                    public    = true,
                    protected = true,
                    actions   = macro,
                }
            else
                -- message
            end
        end
    else
        -- message
    end
end

function interfaces.tolist(t)
    if t then
        local r = { }
        for i=1,#t do
            r[i] = t[i]
        end
        local n = #r
        for k,v in table.sortedhash(t) do
            if type(k) ~= "number" then
                n = n + 1
                r[n] = k .. "=" .. v
            end
        end
        return concat(r,", ")
    else
        return ""
    end
end

-- \startluacode
-- function test(opt_1, opt_2, arg_1)
--     context.startnarrower()
--     context("options 1: %s",interfaces.tolist(opt_1))
--     context.par()
--     context("options 2: %s",interfaces.tolist(opt_2))
--     context.par()
--     context("argument 1: %s",arg_1)
--     context.stopnarrower()
-- end
--
-- interfaces.definecommand {
--     name = "test",
--     arguments = {
--         { "option", "list" },
--         { "option", "hash" },
--         { "content", "string" },
--     },
--     macro = test,
-- }
-- \stopluacode
--
-- test: \test[1][a=3]{whatever}
--
-- \startluacode
-- local function startmore(opt_1)
--     context.startnarrower()
--     context("start more, options: %s",interfaces.tolist(opt_1))
--     context.startnarrower()
-- end
--
-- local function stopmore()
--     context.stopnarrower()
--     context("stop more")
--     context.stopnarrower()
-- end
--
-- interfaces.definecommand ( "more", {
--     environment = true,
--     arguments = {
--         { "option", "list" },
--     },
--     starter = startmore,
--     stopper = stopmore,
-- } )
-- \stopluacode
--
-- more: \startmore[1] one \startmore[2] two \stopmore one \stopmore
--
-- More modern (no need for option or content):
--
-- \startluacode
-- interfaces.definecommand {
--     name = "test",
--     arguments = {
--         "array", -- or list
--         "hash",
--         "string",
--         "number",
--     },
--     macro = test,
-- }
-- \stopluacode
--
