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

local nuts          = nodes.nuts

local insertafter   = nuts.insertafter
local hpack_string  = nuts.typesetters.tohpack
local hpack         = nuts.hpack
local getheight     = nuts.getheight
local getid         = nuts.getid
local setwhd        = nuts.setwhd
local setoffsets    = nuts.setoffsets
local setlink       = nuts.setlink

local newrule       = nuts.pool.virtualrule

local nodecodes     = nodes.nodecodes

local hlist_code    <const> = nodecodes.hlist
local vlist_code    <const> = nodecodes.vlist
local rule_code     <const> = nodecodes.rule

local setcolor      = nodes.tracers.colors.set

local buildcodes    = tex.buildcodes

local ejectpenalty  <const> = tex.magicconstants.eject_penalty

local report     = logs.reporter("build")

local xoffset    <const> =  4 * 65536
local yoffset    <const> =  1 * 65536
local usedfont   = false

local f_status_y = string.formatters["%i : badness=%i costs=%i height=%p depth=%p stretch=%p shrink=%p hasfil=%l"]
local f_status_n = string.formatters["%i"]

local variables  = interfaces.variables
local v_after    <const> = variables.after
local v_before   <const> = variables.before
local v_reset    <const> = variables.reset
local v_yes      <const> = variables.yes
local v_all      <const> = variables.all

local done       = { }
local last       = false
local count      = 0
local forced     = { } -- we can combine forced and ignored but then we can't disable
local ignored    = { } -- the callback easily
local detail     = false
local trace      = false
local enabled    = false
local ejecting   = false

local registercallback = callbacks.register

-- use

local function checkedforce(moveon,fireup,penalty)
    local how = forced[count]
    ejecting = fireup and penalty == ejectpenalty
    if how == v_after then
        report("forcing pagebreak %s %i",how,count)
        forced[count]  = nil
        ignored[count] = true
-- check on moveon
        return true, true
    elseif how == v_before then
        report("forcing pagebreak %s %i",how,count)
        forced[count]  = nil
        ignored[count] = true
        return false, true
    else
        return moveon, fireup
    end
end

local function check(current,moveon,fireup,badness,costs,penalty)
    if last then
        count = count + 1
    end
    return checkedforce(moveon,fireup,penalty)
end

local forceactions = {
    [buildcodes.step]  = step,
    [buildcodes.check] = check,
}

local function show_build_callback(what,...)
    local action = forceactions[what]
    if action then
        return action(...)
    end
end

local function disableforce()
    count   = 0
    forced  = { }
    ignored = { }
    if enabled and not trace then
        registercallback { name = "show_build", action = false }
    end
    enabled = false
end

local function enableforce()
    if not enabled and not trace then
        registercallback { name = "show_build", action = show_build_callback }
    end
    enabled = true
end

interfaces.implement {
    name      = "registerforcedbreak",
    public    = true,
    protected = true,
    arguments = "2 optionals",
    actions   = function(where,how)
        if where == v_reset then
            disableforce()
        else
            for n in string.gmatch(where,"%d+") do
                forced[tonumber(n)] = how
            end
            enableforce()
        end
    end
}

interfaces.implement {
    name      = "resetforcedbreak",
    public    = true,
    protected = true,
    actions   = disableforce,
}

-- show

local function initialize()
    usedfont = nodes.visualizers.getusedfont()
end

local function step(current)
    local id = getid(current)
    if id == hlist_code or id == vlist_code or id == rule_code then
        last = current
    end
end

local function check(current,moveon,fireup,badness,costs,penalty)
    if last then
        count = count + 1
        done[#done+1] = {
            count   = count,
            line    = last,
            badness = badness,
            penalty = penalty,
            costs   = costs,
            height  = 0,
            depth   = 0,
            stretch = 0,
            shrink  = 0,
            hasfil  = false,
        }
    end
    return checkedforce(moveon,fireup,penalty)
end

-- local function skip(current)
--     if last then
--         count = count + 1
--         done[#done+1] = {
--             count = count,
--             line  = last,
--         }
--     end
-- end

local function move(current,height,depth,stretch,shrink,hasfil)
    local d = done[#done]
    if d then
        d.height  = height
        d.depth   = depth
        d.stretch = stretch
        d.shrink  = shrink
        d.hasfil  = hasfil
    end
end

-- local function fireup(current)
-- end

-- local function wrapup()
-- end

local showactions = {
    [buildcodes.initialize] = initialize,
    [buildcodes.step]       = step,
    [buildcodes.check]      = check,
 -- [buildcodes.skip]       = skip,
    [buildcodes.move]       = move,
 -- [buildcodes.fireup]     = fireup,
 -- [buildcodes.wrapup]     = wrapup,
}

interfaces.implement {
    name    = "build_page_before_shipout",
    actions = function()
        if trace then
            done[#done] = nil
            if #done > 0 then
                for i=1,#done do
                    local data    = done[i]
                    local line    = data.line
                    local count   = data.count
                    if not ignored[count] then
                        local badness = data.badness
                        if detail and badness then
                            data = f_status_y(count,badness,data.costs,data.height,data.depth,data.stretch,data.shrink,data.hasfil)
                        else
                            data = f_status_n(count)
                        end
                        local text   = hpack_string(data,usedfont)
                        local height = getheight(text)
                        local rule   = newrule(height,height,0)
                        setcolor(rule,"trace:db")
                        setoffsets(rule,-(height+xoffset),0)
                        text = hpack(setlink(rule,text))
                        setwhd(text,0,0,0)
                        setoffsets(text,0,-(height+yoffset))
                        insertafter(line,line,text)
                    end
                end
                if ejecting then
                    count = 0
                    done = { }
                end
            end
        end
    end
}

interfaces.implement {
    name    = "build_page_after_shipout",
    actions = function()
        if trace then
            done = { }
        end
        last = false
    end
}

local function show_build_callback(what,...)
    local action = showactions[what]
    if action then
        return action(...)
    end
end

local function build_page_setup(v)
    if v == "all" or v == v_all then
        trace  = true
        detail = true
    elseif v == true or v == "yes" or v == v_yes then
        trace  = true
        detail = false
    else
        trace  = false
        detail = false
    end
    if trace then
        registercallback { name = "show_build", action = show_build_callback }
    elseif not enabled then
        registercallback { name = "show_build", action = false }
    end
end

interfaces.implement {
    name      = "build_page_setup",
    arguments = "string",
    actions   = build_page_setup,
}

trackers.register("builders.page.show",build_page_setup) -- for old times sake, will go
