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

local tonumber, next = tonumber, next
local gmatch = string.gmatch
local stepper = utilities.parsers.stepper

-- local trace_stacking = false  trackers.register("typesetters.stacking", function(v) trace_stacking = v end)
--
-- local report_stacking = logs.reporter("typesetting","stacking")

local nodes, node = nodes, node
local nuts               = nodes.nuts
local tonut              = nuts.tonut

local nodecodes          = nodes.nodecodes

local glyph_code         <const> = nodecodes.glyph
local disc_code          <const> = nodecodes.disc -- unlikely
local hlist_code         <const> = nodecodes.hlist
local vlist_code         <const> = nodecodes.vlist
local whatsit_code       <const> = nodecodes.whatsit
local rule_code          <const> = nodecodes.rule
local glue_code          <const> = nodecodes.glue

local emptyrule_code     <const> = nodes.rulecodes.empty

local a_stacking         <const> = attributes.private("stacking")

local getattr            = nuts.getattr
local getwhd             = nuts.getwhd
local getlist            = nuts.getlist
local setlist            = nuts.setlist
local setleader          = nuts.setleader
local getsubtype         = nuts.getsubtype
local setsubtype         = nuts.setsubtype
local isnextglyph        = nuts.isnextglyph

local replace_node       = nuts.replace
local remove_node        = nuts.remove

local nextcontent        = nuts.traversers.content

local new_emptyrule      = nuts.pool.emptyrule


local unblocked =  {
     savepos        = true,
     save           = true,
     restore        = true,
     setmatrix      = true,
     startmatrix    = true,
     stopmatrix     = true,
     startscaling   = true,
     stopscaling    = true,
     startrotation  = true,
     stoprotation   = true,
     startmirroring = true,
     stopmirroring  = true,
     startclipping  = true,
     stopclipping   = true,
}

local stacking       = typesetters.stacking or { }
typesetters.stacking = stacking

local nofstacking = 0xFFFF -- we don't mix with numbers
local registered  = { }
local currentset  = { }
local enabled     = false

local function getselection(str)
    local t = { }
    stepper(str,function(s)
        local r = registered[s] or tonumber(s)
        if r then
            t[r] = true
        end
    end)
    return t
end

local function getindex(str)
    return registered[str] or tonumber(str) or 0
end

typesetters.stacking.getselection = getselection
typesetters.stacking.getindex     = getindex

local report = logs.reporter("stacking")
local trace  = false

interfaces.implement {
    name      = "newstacking",
    arguments = "2 strings",
    usage     = "value",
    actions   = function(s,n)
        n = n and tonumber(n)
        if not n then
            nofstacking = nofstacking + 1
            n = nofstacking
        end
        registered[s] = n
        if trace then
            report("define %a as index %i",s,n)
        end
        return tokens.values.integer, n
    end,
}

interfaces.implement {
    name      = "enablestacking",
    actions   = function()
        if not enabled then
            nodes.tasks.enableaction("shipouts", "typesetters.stacking.handler")
            enabled = true
        end
    end,
}

interfaces.implement {
    name      = "setstacking",
    arguments = "string",
    actions   = function(str)
        currentset = getselection(str)
        if not enabled and next(currentset) then
            if trace then
                report("selecting %a",str)
            end
            nodes.tasks.enableaction("shipouts", "typesetters.stacking.handler")
            enabled = true
        end
    end,
}

local function process(head)
    local current = head
    while current do
        local nxt, chr, id = isnextglyph(current)
        if chr then
            local a = getattr(current,a_stacking)
            if a and (a == 0 or not currentset[a]) then
                local w, h, d = getwhd(current)
                local r = new_emptyrule(w,h,d)
                head, current = replace_node(head,current,r)
            end
        elseif id == glue_code then
            local a = getattr(current,a_stacking)
            if a and (a == 0 or not currentset[a]) then
                setleader(current)
            end
        elseif id == hlist_code or id == vlist_code then
            local a = getattr(current,a_stacking)
            if a and (a == 0 or not currentset[a]) then
                setlist(current)
            else
                local list = getlist(current)
                if list then
                    local l = process(list)
                    if l ~= list then
                        setlist(current,l)
                    end
                end
            end
        elseif id == rule_code then
            local a = getattr(current,a_stacking)
            if a and not currentset[a] then
                setsubtype(current,emptyrule_code)
            end
        elseif id == whatsit_code then
            local a = getattr(current,a_stacking)
            if a and (a == 0 or not currentset[a]) and not unblocked[getsubtype(current)] then
                head, current = remove_node(head,current,true)
            end
        elseif id == disc_code then
            -- doesn't happen
            local a = getattr(current,a_stacking)
            if a and (a == 0 or not currentset[a]) then
                local w, h, d = getwhd(current)
                local r = new_emptyrule(w,h,d)
                head, current = replace_node(head,current,r)
            end
        end
        current = nxt
    end
    return head
end

stacking.handler = process
