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

-- Future version will support adding arbitrary bookmarks with
-- associated complex actions (rather trivial to implement).

local format, concat, gsub = string.format, table.concat, string.gsub
local texsprint, utfvalues = tex.sprint, string.utfvalues

local ctxcatcodes = tex.ctxcatcodes

local lists    = structure.lists
local levelmap = structure.sections.levelmap

structure.bookmarks = structure.bookmarks or { }

local bookmarks = structure.bookmarks

bookmarks.method = "internal" -- or "page"

local names, opened = "", ""

function bookmarks.register(n,o)
    if names  == "" then names  = n else names  = names  .. "," .. n end
    if opened == "" then opened = o else opened = opened .. "," .. o end
end

function bookmarks.overload(name,text)
    local l, ls = lists.tobesaved, nil
    if #l == 0 then
        -- no entries
    elseif name == "" then
        ls = l[#l]
    else
        for i=#l,0,-1 do
            local li = l[i]
            local metadata = li.metadata
            if metadata and not metadata.nolist and metadata.name == name then
                ls = li
                break
            end
        end
    end
    if ls then
        ls.titledata.bookmark = text
    end
end

local function stripped(str) -- kind of generic
    str = gsub(str,"\\([A-Z]+)","%1")            -- \LOGO
    str = gsub(str,"\\ "," ")                    -- \
    str = gsub(str,"\\([A-Za-z]+) *{(.-)}","%1") -- \bla{...}
    str = gsub(str," +"," ")                     -- spaces
    return str
end

-- todo: collect specs and collect later i.e. multiple places

function bookmarks.place()
    if names ~= "" then
        local list = lists.filter(names,"all",nil,lists.collected)
        local lastlevel = 1
        if #list > 0 then
            local opened, levels = aux.settings_to_set(opened), { }
            for i=1,#list do
                local li = list[i]
                local metadata = li.metadata
                local name = metadata.name
                if not metadata.nolist then -- and levelmap[name] then
                    local titledata = li.titledata
                    if titledata then
                        local structural = levelmap[name]
                        lastlevel = structural or lastlevel
                        local title = titledata.bookmark
                        if not title or title == "" then
                            if not structural then
                                -- placeholder, todo: bookmarklabel
                                title = name .. ": " .. (titledata.title or "?")
                            else
                                title = titledata.title or "?"
                            end
                        end
                        levels[#levels+1] = {
                            lastlevel,
                            stripped(title),
                            li.references, -- has internal and realpage
                            allopen or opened[name]
                        }
                    end
                end
            end
            backends.codeinjections.addbookmarks(levels,bookmarks.method)
        end
        function bookmarks.place() end -- prevent second run
    end
end

lpdf.registerdocumentfinalizer(function() structure.bookmarks.place() end,1)

-- bkm
--~ function nodes.toutf(list)
--~     local t= { }
--~     for n in node.traverse(list) do
--~         local id = n.id
--~         if id == node.id("glyph") then
--~             local c = n.char
--~             local f = fonts.ids[n.font]
--~             if f then
--~                 local u = f.characters[c].tounicode
--~                 if u then
--~                     for s in string.gmatch(u,"..") do
--~                         t[#t+1] = utf.char(tonumber(s,16))
--~                     end
--~                 else
--~                     t[#t+1] = utf.char(c)
--~                 end
--~             else
--~                 t[#t+1] = utf.char(c)
--~             end
--~         elseif id == node.id("glue") then
--~             t[#t+1] = " "
--~         end
--~     end
--~     return table.concat(t,"")
--~ end
--~ print(nodes.toutf(tex.box[999].list))
