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

local tonumber, next, setmetatable, rawget = tonumber, next, setmetatable, rawget
local concat, sort, remove, copy = table.concat, table.sort, table.remove, table.copy
local match, find = string.match, string.find
local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local setmetatableindex = table.setmetatableindex
local P, Cc = lpeg.P, lpeg.Cc

graphics                = graphics or { }
local synchronizers     = { }
graphics.synchronizers  = synchronizers

local jobpositions      = job.positions

local p_number = lpegpatterns.cardinal/tonumber
local p_space  = lpegpatterns.whitespace^0
local p_option = p_number * ((P(",") * p_space * P("reset") * Cc(true)) + Cc(false)) -- for now

local list     = { }
local kinds    = {
    above    = 1,
    continue = 2,
    nothing  = 3,
    normal   = 4,
    below    = 5,
}

local lastdone = { }

function synchronizers.collect(category,realpage,region)
    local all = jobpositions.getsync(category)
    local m   = 0
    local n   = #all
    list = { }
    if region and region ~= "" then
        -- successive can be optimized when we sort by region
        local start = 1
        local done  = false
        local last, rtop, rbot
        for i=start,n do
            local pos = all[i]
            local p = pos.p
            local r = pos.r
            if r == region then
                if not done then
                    local region = jobpositions.collected[r]
                    list.region  = region
                    list.page    = region
                    rtop = (region.y or 0) + (region.h or 0)
                    rbot = (region.y or 0) - (region.d or 0)
                    last = { kind = "nothing", top = rtop, bottom = 0, task = 0 }
                    m = m + 1 ; list[m] = last
                    done = true
                end
                local top = pos.y + pos.h
                last.bottom = top
                local task, reset = lpegmatch(p_option,pos.e)
                last = { kind = "normal", top = top, bottom = 0, task = task }
                m = m + 1 ; list[m] = last
            end
        end
        if done then
            last.bottom = rbot
        end
    else
        local start = all.start or 1
        local done  = false
        local last, rtop, rbot, ptop, pbot
        for i=start,n do
            local pos = all[i]
            local p = pos.p
            if p == realpage then
                if not done then
                    local region = jobpositions.collected[pos.r]
                    local page   = jobpositions.collected["page:"..realpage] or region
                    list.region  = region
                    list.page    = page
                    rtop = (region.y or 0) + (region.h or 0)
                    rbot = (region.y or 0) - (region.d or 0)
                    ptop = (page  .y or 0) + (page  .h or 0)
                    pbot = (page  .y or 0) - (page  .d or 0)
                    last = { kind = "above", top = ptop, bottom = rtop, task = 0 }
                    m = m + 1 ; list[m] = last
                    if i > 1 then
                        local task, reset = lpegmatch(p_option,all[i-1].e)
                        last = { kind = "continue", top = rtop, bottom = 0, task = task }
                        m = m + 1 ; list[m] = last
                    else
                        last = { kind = "nothing", top = rtop, bottom = 0, task = 0 }
                        m = m + 1 ; list[m] = last
                    end
                    done = true
                end
                local top = pos.y + pos.h
                last.bottom = top
                local task, reset = lpegmatch(p_option,pos.e)
                if reset then
                    local l = list[2]
                    l.kind = "nothing"
                    l.task = 0
                end
                last = { kind = "normal", top = top, bottom = 0, task = task }
                m = m + 1 ; list[m] = last
            elseif p > realpage then
                all.start = i -- tricky, only for page
                break
            end
        end
        if done then
            last.bottom = rbot
            last = { kind = "below", top = rbot, bottom = pbot, task = 0 }
            m = m + 1 ; list[m] = last
            lastdone[category] = {
                { kind = "above", top = ptop, bottom = rtop, task = 0 },
                { kind = "continue", top = rtop, bottom = rbot, task = list[#list-1].task }, -- lasttask
                { kind = "below", top = rbot, bottom = pbot, task = 0 },
                region = list.region,
                page   = list.page,
            }
        else
            local l = lastdone[category]
            if l then
                list = copy(l) -- inefficient, maybe metatable for region/page
                m    = 3
            end
        end
    end
    return m
end

function synchronizers.extend()
     local n = #list
     if n > 0 then
        for i=1,n do
            local l = list[i]
            local k = l.kind
            if k == "nothing" then
                local ll = list[i+1]
                if ll and ll.kind == "normal" then
                    ll.top = l.top
                    remove(list,i)
                    n = #list
                    break
                end
            end
        end
    end
    return n
end

function synchronizers.prune()
     local n = #list
     if n > 0 then
        if list[1].kind == "above" then
            remove(list,1)
        end
        if list[1].kind == "nothing" then
            remove(list,1)
        end
        if list[#list].kind == "below" then
            remove(list,#list)
        end
        n = #list
    end
    return n
end

function synchronizers.collapse()
    local n = #list
    if n > 0 then
        local m = 0
        local p = nil
        for i=1,n do
            local l = list[i]
            local t = l.task
            if p == t then
                list[m].bottom = l.bottom
            else
                m = m + 1
                list[m] = l
            end
            p = t
        end
        for i=n,m+1,-1 do
            list[i] = nil
        end
        n = m
    end
    return n
end

-- These operate on the currently set list:

function synchronizers.getsize  ()  return #list               end
function synchronizers.gettop   (n) return list[n].top         end
function synchronizers.getbottom(n) return list[n].bottom      end
function synchronizers.getkind  (n) return kinds[list[n].kind] end
function synchronizers.gettask  (n) return list[n].task        end

function synchronizers.getx() return list.page.x or 0 end
function synchronizers.gety() return list.page.y or 0 end
function synchronizers.getw() return list.page.w or 0 end
function synchronizers.geth() return list.page.h or 0 end
function synchronizers.getd() return list.page.d or 0 end

-- function mp.xxOverlayRegion()
--     local r = tokens.getters.macro("m_overlay_region")
--     mp.quoted('"'.. r .. '"')
-- end

