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

-- Some day I need to make this more efficient.

local tonumber, rawget, rawset, type, next = tonumber, rawget, rawset, type, next
local match = string.match
local sort, tohash, insert, remove, sortedkeys = table.sort, table.tohash, table.insert, table.remove, table.sortedkeys
local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash

local texgetcount  = tex.getcount

local tonut        = nodes.tonut
local nextlist     = nodes.nuts.traversers.list
local texlists     = tex.lists

local context      = context
local ctx_doif     = commands.doif
local ctx_doifelse = commands.doifelse

local implement    = interfaces.implement

local data         = table.setmetatableindex("table")
local last         = 0
local pages        = structures.pages
local autolist     = { }
local report       = logs.reporter("pages","mark")
local active       = false

local trace        = false  trackers.register("pages.mark",function(v) trace = v end)

function pages.mark(name,list,settings)
    active = true
    --
    local realpage = texgetcount("realpageno")
    if type(settings) == "string" then
        settings = settings_to_hash(settings)
    end
    if not list or list == "" then
        if trace then
            report("marking current page %i as %a",realpage,name)
        end
        data[realpage][name] = settings or true
        return
    end
    if type(list) == "string" then
        list = settings_to_array(list)
    end
    if type(list) == "table" then
        for i=1,#list do
            local page = list[i]
            local sign = false
            if type(page) == "string" then
                local f, t = match(page,"(%d+)[:%-](%d+)")
                if f and t then
                    f, t = tonumber(f), tonumber(t)
                    if f and t and f <= t then
                        if trace then
                            report("marking page %i upto %i as %a",f,t,name)
                        end
                        for page=f,t do
                            data[page][name] = settings or true
                        end
                    end
                    page = false
                else
                    local s, p = match(page,"([%+%-])(%d+)")
                    if s then
                        sign, page = s, p
                    end
                end
            end
            if page then
                page = tonumber(page)
                if page then
                    if sign == "+" then
                        page = realpage + page
                    end
                    if sign == "-" then
                        report("negative page numbers are not supported")
                    else
                        if trace then
                            report("marking page %i as %a",page,name)
                        end
                        data[page][name] = settings or true
                    end
                end
            end
        end
    else
        if trace then
            report("marking current page %i as %a",realpage,name)
        end
        data[realpage][name] = settings or true
    end
end

local tobemarked = { }

function pages.markedlist(realpage)
    if active and realpage then
        local m = rawget(tobemarked,realpage) or rawget(data,realpage)
        return m and next(m) and sortedkeys(m)
    end
end

local function marked(name)
    if active then
        local realpage = texgetcount("realpageno")
        if last ~= 0 then
            for i=last,realpage-1 do
    --     print(last)
                local di = data[i]
                if di then
                    tobemarked[i] = di
                    rawset(data,i,nil)
                end
            end
            last = 0 -- needs checking
        end
        local pagedata = rawget(data,realpage)
        return pagedata and pagedata[name] and true or false
    else
        return false
    end
end

local function markedparameter(name,key)
    if active then
        local pagedata = rawget(data,texgetcount("realpageno"))
        if pagedata then
            pagedata = pagedata[name]
            if pagedata then
                pagedata = pagedata[key]
            end
        end
        return pagedata
    end
end

local function toranges(marked)
    local list = { }
    local size = #marked
    if size > 0 then
        local first = marked[1]
        local last  = first
        for i=2,size do
            local page = marked[i]
            if page > last + 1 then
                list[#list+1] = { first, last }
                first = page
            end
            last = page
        end
        list[#list+1] = { first, last }
        --
        active = true
    end
    return list
end

local function allmarked(list)
    if active and list then
        local collected = pages.collected
        if collected then
            if type(list) == "string" then
                list = settings_to_hash(list)
            elseif type(list) == "table" and #list > 0 then
                list = tohash(list)
            end
            if type(list) == "table" then
                local found = { }
                for name in next, list do
                    for page, list in next, data do
                        if list[name] and collected[page] then
                            found[#found+1] = page
                        end
                    end
                end
                if #found > 0 then
                    sort(found)
                    if trace then
                        local ranges = toranges(found)
                        for i=1,#ranges do
                            local range = ranges[i]
                            local first = range[1]
                            local last  = range[2]
                            if first == last then
                                report("marked page : %i",first)
                            else
                                report("marked range: %i upto %i",first,last)
                            end
                        end
                    end
                    return found
                end
            end
        end
    end
end

pages.marked    = marked
pages.toranges  = toranges
pages.allmarked = allmarked

-- An alternative is to use an attribute and identify the state by parsing the node
-- list but that's a bit overkill for a hardly used feature like this.

-- Page actions are bound to a real page. When we set one, we need to bind to the
-- current page unless we just flushed. So we also need to check the current page.

-- \page ... \start : sync realpage
-- \page     \start : sync realpage
-- \page     \stop  : reset synced because no content yet
-- \page ... \stop  : keep sync

local function autopageaction()
    if active then
        local nofauto = #autolist
        if nofauto > 0 then
            local realpage = texgetcount("realpageno")
            for i=1,nofauto do
                local entry    = autolist[i]
                local names    = entry[1]
                local settings = entry[2]
                for j=1,#names do
                    local name = names[j]
                    local list = data[realpage]
                    if not list[name] then
                        if trace then
                            report("automatically marking page %i as %a",realpage,name)
                        end
                        list[name] = settings or true
                    end
                end
            end
        end
    end
end

local function startmarked(name,settings)
    active = true
    --
    insert(autolist, { settings_to_array(name), settings_to_hash(settings) })
    autopageaction(true)
end

local function stopmarked()
    local nofauto = #autolist
    if nofauto > 0 then
        if not texlists.pagehead then
            local realpage = texgetcount("realpageno")
            for i=1,nofauto do
                local entry = autolist[i]
                local names = entry[1]
                for j=1,#names do
                    local name = names[j]
                    local list = data[realpage]
                    if list[name] then
                        if trace then
                            report("automatically unmarking page %i as %a",realpage,name)
                        end
                        list[name] = nil
                    end
                end
            end
        end
        remove(autolist)
    end
end

implement {
    name      = "checkmarkedpages",
    actions   = autopageaction,
}

implement {
    name      = "markpage",
    arguments = "3 strings",
    actions   = pages.mark
}

implement {
    name      = "doifelsemarkedpage",
    arguments = "string",
    actions   = { marked, ctx_doifelse }
}

implement {
    name      = "doifmarkedpage",
    arguments = "string",
    actions   = { marked, ctx_doif }
}

implement {
    name      = "markedpageparameter",
    arguments = "strings",
    actions   = function(name,key)
        local value = markedparameter(name,key)
        if value then
            context(value)
        end
    end
}

implement {
    name      = "markedpages",
    arguments = "string",
    actions   = function(name)
        local t = allmarked(name)
        if t then
            context("%,t",t)
        end
    end
}

implement {
    name      = "startmarkpages",
    arguments = "2 strings",
    actions   = startmarked,
}

implement {
    name    = "stopmarkpages",
    actions = stopmarked,
}

local tonut    = nodes.tonut
local nextlist = nodes.nuts.traversers.list
local texlists = tex.lists

implement {
    name    = "doifelsependingpagecontent",
    actions = function()
        local h = texlists.contrib_head
     -- local t = texlists.contrib_tail
        local p = false
        if h then
            for n in nextlist, tonut(h) do
                p = true
                break
            end
        end
        ctx_doifelse(p)
    end,
}
