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

local context      = context

local tostring = tostring

local nuts              = nodes.nuts
local tonut             = nodes.tonut
local tonode            = nodes.tonode

local left_box_code     <const> = nodes.listcodes.left
local right_box_code    <const> = nodes.listcodes.right
local local_box_code    <const> = nodes.listcodes["local"]
local hlist_code        <const> = nodes.nodecodes.hlist

-- some can go:

local getlist           = nuts.getlist
local getprev           = nuts.getprev
local getnext           = nuts.getnext
local getattribute      = nuts.getattribute
local gettail           = nuts.tail
local getwidth          = nuts.getwidth
local getboth           = nuts.getboth
local getindex          = nuts.getindex
local setlist           = nuts.setlist
local flushlist         = nuts.flushlist
local takebox           = nuts.takebox
local setbox            = nuts.setbox
local copynode          = nuts.copynode
local rangedimensions   = nuts.rangedimensions
local traverse_list     = nuts.traversers.list

----- setlocalbox       = tex.setlocalbox -- todo: also in node.direct namespace
----- getlocalbox       = tex.getlocalbox -- todo: also in node.direct namespace

local expandmacro       = token.expandmacro

local dimension_value   <const> = tokens.values.dimension
local integer_value     <const> = tokens.values.integer

local implement         = interfaces.implement

typesetters             = typesetters or { }
local typesetters       = typesetters

typesetters.localboxes  = typesetters.localboxes or { }
local localboxes        = typesetters.localboxes

local a_localboxesmark  <const> = attributes.private("localboxesmark")

do
    local lb_found            = nil
    local lb_index            = 0
    local lb_linenumber       = 0
    local lb_linewidth        = 0
    local lb_localwidth       = 0
    local lb_progress         = 0
    local lb_leftoffset       = 0
    local lb_rightoffset      = 0
    local lb_leftskip         = 0
    local lb_rightskip        = 0
    local lb_lefthang         = 0
    local lb_righthang        = 0
    local lb_indent           = 0
    local lb_parfillleftskip  = 0
    local lb_parfillrightskip = 0
    local lb_parinitleftskip  = 0
    local lb_parinitrightskip = 0
    local lb_overshoot        = 0

    implement { name = "localboxindex",            public = true, usage = "value", actions = function() return integer_value,   lb_index            end }
    implement { name = "localboxlinenumber",       public = true, usage = "value", actions = function() return integer_value,   lb_linenumber       end }
    implement { name = "localboxlinewidth",        public = true, usage = "value", actions = function() return dimension_value, lb_linewidth        end }
    implement { name = "localboxlocalwidth",       public = true, usage = "value", actions = function() return dimension_value, lb_localwidth       end }
    implement { name = "localboxprogress",         public = true, usage = "value", actions = function() return dimension_value, lb_progress         end }
    implement { name = "localboxleftoffset",       public = true, usage = "value", actions = function() return dimension_value, lb_leftoffset       end }
    implement { name = "localboxrightoffset",      public = true, usage = "value", actions = function() return dimension_value, lb_rightoffset      end }
    implement { name = "localboxleftskip",         public = true, usage = "value", actions = function() return dimension_value, lb_leftskip         end }
    implement { name = "localboxrightskip",        public = true, usage = "value", actions = function() return dimension_value, lb_rightskip        end }
    implement { name = "localboxlefthang",         public = true, usage = "value", actions = function() return dimension_value, lb_lefthang         end }
    implement { name = "localboxrighthang",        public = true, usage = "value", actions = function() return dimension_value, lb_righthang        end }
    implement { name = "localboxindent",           public = true, usage = "value", actions = function() return dimension_value, lb_indent           end }
    implement { name = "localboxparfillleftskip",  public = true, usage = "value", actions = function() return dimension_value, lb_parfillleftskip  end }
    implement { name = "localboxparfillrightskip", public = true, usage = "value", actions = function() return dimension_value, lb_parfillrightskip end }
    implement { name = "localboxparinitleftskip",  public = true, usage = "value", actions = function() return dimension_value, lb_parinitleftskip  end }
    implement { name = "localboxparinitrightskip", public = true, usage = "value", actions = function() return dimension_value, lb_parinitrightskip end }
    implement { name = "localboxovershoot",        public = true, usage = "value", actions = function() return dimension_value, lb_overshoot        end }

    local cache = table.setmetatableindex(function(t,k)
        local v = { n = 0, m = 0 }
        t[k] = v
        return v
    end)

    -- todo: use a simple usernode that refers to a cache so that we don't need to
    -- make copies

    implement {
        name      = "localboxmarkonce",
        public    = true,
        arguments =  "integer",
        actions   = function(attr)
            local c = cache[attr]
            local n = c.n
-- first test this:
--             if n == c.m then
-- print("all false",attr,n)
--                 -- all false
--                 n = 1
--                 c.m = 0
--             else
                n = n + 1
--             end
            c[n] = true
            c.n = n
            context(n)
        end
    }

    local function action(current)
        local list = getlist(current)
        if list then
            local attr = getattribute(list,a_localboxesmark) or 0
            local cach = attr and cache[lb_index]
            local once = cach and cach[attr]
            if once == false then
                setlist(current)
                flushlist(list)
            else
                setlist(current)
                local head = copynode(current)
                setlist(head,list)
                setbox("localboxcontentbox",head)
                expandmacro("localboxcommand") -- no longer pass arguments
                local box = takebox("localboxcontentbox")
                setlist(current,box)
                if once and cach[attr] == true then
                    cach[attr] = false
                    cach.m = cach.m + 1
                end
            end
        end
    end

    -- these two are now more or less the same so ... todo: add
    -- warning about non zero width

    local function processleftbox(box)
        local list = getlist(box)
        for current, id, subtype in traverse_list, list do
            if subtype == local_box_code and getwidth(current) == 0 then
                local index = getindex(current)
                if index then
                    lb_found    = current
                    lb_index    = index
                    lb_progress = rangedimensions(box,list,current)
                    action(current)
                end
            end
        end
    end

    local function processrightbox(box)
        local list = getlist(box)
        for current, id, subtype in traverse_list, list do
            if subtype == local_box_code and getwidth(current) == 0 then
                local index = getindex(current)
                if index then
                    lb_found    = current
                    lb_index    = index
                    lb_progress = rangedimensions(box,list,current)
                    action(current)
                end
            end
        end
    end

    local function processmiddlebox(box,line)
        local list = getlist(box)
        for current, id, subtype in traverse_list, list do
            if subtype == local_box_code and getwidth(current) == 0 then
                local index = getindex(current)
                if index then
                    lb_found    = current
                    lb_index    = index
                    lb_progress = rangedimensions(line,getlist(line),box)
                    action(current)
                end
            end
        end
    end

    local function processlocalboxes(line,leftbox,rightbox,middlebox,linenumber,leftskip,rightskip,lefthang,righthang,indent,parinitleftskip,parinitrightskip,parfillleftskip,parfillrightskip,overshoot)
        --
        lb_found            = nil
        lb_index            = 0
        lb_linenumber       = linenumber
        lb_progress         = 0
        lb_leftskip         = leftskip
        lb_rightskip        = rightskip
        lb_lefthang         = lefthang
        lb_righthang        = righthang
        lb_indent           = indent
        lb_parfillleftskip  = parfillleftskip
        lb_parfillrightskip = parfillrightskip
        lb_parinitleftskip  = parinitleftskip
        lb_parinitrightskip = parinitrightskip
        lb_overshoot        = overshoot
        lb_linewidth        = getwidth(line)
        -- only true for some cases
        lb_leftoffset       = leftskip  + lefthang  + parfillleftskip  + parinitleftskip  + indent
        lb_rightoffset      = rightskip + righthang + parfillrightskip + parinitrightskip - overshoot
        --
        if leftbox then
            lb_localwidth = getwidth(leftbox)
            processleftbox(leftbox)
        end
        if middlebox then
            lb_localwidth = getwidth(middlebox)
            processmiddlebox(middlebox,line)
        end
        if rightbox then
            lb_localwidth = getwidth(rightbox)
            processrightbox(rightbox)
        end
    end

    typesetters.localboxes.handler = processlocalboxes

end

local local_box_callback = nodes.tasks.actions("localboxes")

callbacks.register {
    name    = "local_box",
    action  = local_box_callback,
    comment = "process local boxes",
    frozen  = true,
}
