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

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

local getdirection       = nuts.getdirection
local getlist            = nuts.getlist
local getoffsets         = nuts.getoffsets
local getorientation     = nuts.getorientation
local getanchors         = nuts.getanchors
local getgeometry        = nuts.getgeometry
local getwhd             = nuts.getwhd
local getkern            = nuts.getkern
local getwidth           = nuts.getwidth
local getheight          = nuts.getheight
local getdepth           = nuts.getdepth
local getnext            = nuts.getnext
local getid              = nuts.getid
local getshift           = nuts.getshift
local getprop            = nuts.getprop
local getreplace         = nuts.getreplace
local setreplace         = nuts.setreplace
----- getkerndimension   = nuts.getkerndimension
----- getglyphdimensions = nuts.getglyphdimensions

local setdirection       = nuts.setdirection
local setlink            = nuts.setlink

local nextnode           = nuts.traversers.node

local effectiveglue      = nuts.effectiveglue
local dirdimensions      = nuts.dirdimensions

local nodecodes          = nodes.nodecodes
local whatsitcodes       = nodes.whatsitcodes
local directioncodes     = tex.directioncodes

local glyph_code         <const> = nodecodes.glyph
local kern_code          <const> = nodecodes.kern
local glue_code          <const> = nodecodes.glue
local hlist_code         <const> = nodecodes.hlist
local vlist_code         <const> = nodecodes.vlist
local dir_code           <const> = nodecodes.dir
local disc_code          <const> = nodecodes.disc
local math_code          <const> = nodecodes.math
local rule_code          <const> = nodecodes.rule
local whatsit_code       <const> = nodecodes.whatsit

local userdefined_code   <const> = whatsitcodes.userdefined

local lefttoright_code   <const> = directioncodes.lefttoright
local righttoleft_code   <const> = directioncodes.righttoleft

local drivers            = drivers

local report             = logs.reporter("drivers")

local maxdimen           <const> = tex.magicconstants.maxdimen

local pos_h              = 0
local pos_v              = 0
local pos_r              = lefttoright_code

local applyorientation   = drivers.applyorientation
local applyanchor        = drivers.applyanchor

local initialize
local finalize
local userdefined

local function reset_state()
    pos_h = 0
    pos_v = 0
    pos_r = lefttoright_code
end

local dirstack = { }

local function reset_dir_stack()
    dirstack = { }
end

local function handlewhatsit(current,pos_h,pos_v)
    local action = userdefined[getprop(current,"id")]
    if action then
        action(current,pos_h,pos_v)
    end
end

local hlist_out, vlist_out -- todo: some can be combined

hlist_out = function(this_box,current)
    local ref_h = pos_h
    local ref_v = pos_v
    local ref_r = pos_r
          pos_r = getdirection(this_box)
    local cur_h = 0
    local cur_b

    for current, id, subtype in nextnode, current do
        if id == glyph_code then -- or id == kern_code
            local width, factor = getwidth(current,true)
            if width ~= 0 then
                if factor ~= 0 then
                    cur_h = cur_h + (1.0 + factor/1000000.0) * width
                else
                    cur_h = cur_h + width
                end
            end
        elseif id == glue_code then
--             cur_h = cur_h + effectiveglue(current,this_box)
cur_h = cur_h + effectiveglue(current,this_box,true)
        elseif id == hlist_code or id == vlist_code then
            local width, height, depth = getwhd(current)
            local list = getlist(current)
            if list then
                local boxdir = getdirection(current) or lefttoright_code
                local shift  = getshift(current)
                local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true)
                local anchor, source, target, targetdata, s_anchor, t_anchor
                local anc_h, anc_v
                local usedorientation = false
                if hasanchor then
                    anchor, source, target, s_anchor, t_anchor  = getanchors(current)
                end
                if hasorientation then
                    local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
                    local orientation, basepoint_h, basepoint_v = applyorientation(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
                    if orientation == 1 then
                        basepoint_h = basepoint_h + doffset
                        if boxdir == pos_r then
                            basepoint_v = basepoint_v - height
                        end
                        usedorientation = orientation
                    elseif orientation == 2 then
                        if boxdir == pos_r then
                            basepoint_h = basepoint_h + width
                        end
                        usedorientation = orientation
                    elseif orientation == 3 then
                        basepoint_h = basepoint_h + hoffset
                        if boxdir ~= pos_r then
                            basepoint_v = basepoint_v - height
                        end
                        usedorientation = orientation
                    end
                    if target then
                        targetdata = anchors[target]
                        if targetdata then
                            anc_h =   basepoint_h
                            anc_v = - basepoint_v
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - (cur_h + basepoint_h)
                    else
                        pos_h = ref_h + (cur_h + basepoint_h)
                    end
                 -- pos_v = ref_v - (cur_v + basepoint_v)
                    pos_v = ref_v - basepoint_v
                elseif hasoffset then
                    local orientation, xoffset, yoffset = getorientation(current)
                    local basepoint_h = boxdir ~= pos_r and width or 0
                    local basepoint_v = shift
                    if target then
                        targetdata = anchors[target]
                        if targetdata then
                            anc_h = xoffset + basepoint_h
                            anc_v = yoffset - basepoint_v
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - (cur_h + basepoint_h + xoffset)
                    else
                        pos_h = ref_h + (cur_h + basepoint_h + xoffset)
                    end
                    pos_v = ref_v - (basepoint_v - yoffset)
                elseif hasanchor then
                    local basepoint_h = boxdir ~= pos_r and width or 0
                    local basepoint_v = shift
                    if target then
                        targetdata = anchors[target]
                        if targetdata then
                            anc_h =   basepoint_h
                            anc_v = - basepoint_v
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - (cur_h + basepoint_h)
                    else
                        pos_h = ref_h + (cur_h + basepoint_h)
                    end
                    pos_v = ref_v - basepoint_v
                else
                    local basepoint_h = boxdir ~= pos_r and width or 0
                    local basepoint_v = shift
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - (cur_h + basepoint_h)
                    else
                        pos_h = ref_h + (cur_h + basepoint_h)
                    end
                    pos_v = ref_v - basepoint_v
                end
                goto process
              ::posdone::
                if pos_r == righttoleft_code then
                    pos_h = targetdata[1] - anc_h
                else
                    pos_h = targetdata[1] + anc_h
                end
                pos_v = targetdata[2] + anc_v
                if anchor and anchor > 0 then
                    pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5])
                    pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth)
                end
              ::process::
                if source then
                    local anchor_h = pos_h
                    local anchor_v = pos_v
                    if usedorientation then
                        if usedorientation == 1 then
                            anchor_v = anchor_v - (width - height)
                        elseif usedorientation == 2 then
                            anchor_v = anchor_v - (depth - height)
                        elseif usedorientation == 3 then -- weird
                            anchor_v = anchor_v + (height - width)
                        end
                    end
                    anchors[source] = { anchor_h, anchor_v, width, height, depth }
                end
             -- if usedorientation then
             --     pushorientation(usedorientation,pos_h,pos_v,pos_r)
             -- end
                if id == vlist_code then
                    vlist_out(current,list)
                else
                    hlist_out(current,list)
                end
             -- if usedorientation then
             --     poporientation(usedorientation,pos_h,pos_v,pos_r)
             -- end
            end
            cur_h = cur_h + width

        elseif id == kern_code then
            local kern, factor = getkern(current,true)
            if kern ~= 0 then
                if factor ~= 0 then
                    cur_h = cur_h + (1.0 + factor/1000000.0) * kern
                else
                    cur_h = cur_h + kern
                end
            end
        elseif id == rule_code then
            cur_h = cur_h + getwidth(current)
        elseif id == math_code then
--             cur_h = cur_h + effectiveglue(current,this_box)
cur_h = cur_h + effectiveglue(current,this_box,true)
        elseif id == dir_code then
            local dir, cancel = getdirection(current)
            if cancel then
                local ds = dirstack[current]
                if ds then
                    ref_h = ds.ref_h
                    ref_v = ds.ref_v
                    cur_h = ds.cur_h
                end
                pos_r = dir
            else
                local width, enddir = dirdimensions(this_box,current)
                local new_h = cur_h + width
                if dir ~= pos_r then
                    cur_h = new_h
                end
                if enddir ~= current then
                    dirstack[enddir] = {
                        cur_h = new_h,
                        ref_h = ref_h,
                        ref_v = ref_v,
                    }
                    setdirection(enddir,pos_r)
                end
                if pos_r == righttoleft_code then
                    pos_h = ref_h - cur_h
                else
                    pos_h = ref_h + cur_h
                end
                pos_v = ref_v
                ref_h = pos_h
                ref_v = pos_v
                cur_h = 0
                pos_r = dir
                goto synced
            end
        elseif id == whatsit_code then
            if subtype == userdefined_code then
                local action = userdefined[getprop(current,"id")]
                if action then
                    if not cur_b then
                        local wd, ht, dp = getwhd(this_box)
                        cur_b = { wd, ht, dp, ref_h, ref_v, ref_r }
                    end
                    action(current, pos_h,pos_v, cur_b)
                end
            end
        elseif id == disc_code then
            local replace, tail = getreplace(current)
            if replace then
                setlink(tail,getnext(current))
                setlink(current,replace)
                setreplace(current)
            end
        else
            goto synced
        end
        if pos_r == righttoleft_code then
            pos_h = ref_h - cur_h
        else
            pos_h = ref_h + cur_h
        end
        pos_v = ref_v
        ::synced::
    end
    pos_h = ref_h
    pos_v = ref_v
    pos_r = ref_r
end

vlist_out = function(this_box,current)
    local ref_h    = pos_h
    local ref_v    = pos_v
    local ref_r    = pos_r
          pos_r    = getdirection(this_box)
    local cur_v    = - getheight(this_box)
    local top_edge = cur_v
          pos_h    = ref_h
          pos_v    = ref_v - cur_v
    local cur_b

    for current, id, subtype in nextnode, current do
        if id == glue_code then
--             cur_v = cur_v + effectiveglue(current,this_box)
cur_v = cur_v + effectiveglue(current,this_box,true)
        elseif id == hlist_code or id == vlist_code then
            local width, height, depth = getwhd(current)
            local list = getlist(current)
            if list then
                local boxdir = getdirection(current) or lefttoright_code
                local shift = getshift(current)
                local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true)
                local anchor, source, target, targetdata, s_anchor, t_anchor
                local usedorientation = false
                if hasanchor then
                    anchor, source, target, s_anchor, t_anchor  = getanchors(current)
                end
                if hasorientation then
                    local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
                    local orientation, basepoint_h, basepoint_v = applyorientation(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
                    if orientation == 1 then
                        basepoint_h = basepoint_h + width - height -- hm
                        basepoint_v = basepoint_v - height
                        usedorientation = orientation
                    elseif orientation == 2 then
                        basepoint_h = basepoint_h + width
                        basepoint_v = basepoint_v + depth - height
                        usedorientation = orientation
                    elseif orientation == 3 then -- weird
                        basepoint_h = basepoint_h + height
                        usedorientation = orientation
                    end
                    if target then
                        targetdata = anchors[target]
                        if targetdata then
                            if pos_r == righttoleft_code then
                                pos_h = targetdata[1] - basepoint_h
                            else
                                pos_h = targetdata[1] + basepoint_h
                            end
                            pos_v = targetdata[2] - basepoint_v
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - basepoint_h
                    else
                        pos_h = ref_h + basepoint_h
                    end
                    pos_v = ref_v - (cur_v + basepoint_v)
                elseif hasoffset then
                    local orientation, xoffset, yoffset = getorientation(current)
                 -- local basepoint_h = shift
                 -- local basepoint_v = height
                    if boxdir ~= pos_r then
                        shift = shift + width
                    end
                    if target then
                        targetdata = anchors[target]
                        if targetdata then
                            if pos_r == righttoleft_code then
                                pos_h = targetdata[1] - (shift + xoffset)
                            else
                                pos_h = targetdata[1] + (shift + xoffset)
                            end
                            pos_v = targetdata[2] - (height - yoffset)
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - (shift + xoffset)
                    else
                        pos_h = ref_h + (shift + xoffset)
                    end
                    pos_v = ref_v - (cur_v + height - yoffset)
                elseif hasanchor then
                 -- local basepoint_h = shift
                 -- local basepoint_v = height
                    if boxdir ~= pos_r then
                        shift = shift + width
                    end
                    if target then
                        local a = anchors[target]
                        if a then
                            if pos_r == righttoleft_code then
                                pos_h = targetdata[1] - shift
                            else
                                pos_h = targetdata[1] + shift
                            end
                            pos_v = targetdata[2] - height
                            goto posdone
                        end
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - shift
                    else
                        pos_h = ref_h + shift
                    end
                    pos_v = ref_v - (cur_v + height)
                else
                 -- local basepoint_h = shift
                 -- local basepoint_v = height
                    if boxdir ~= pos_r then
                        shift = shift + width
                    end
                    if pos_r == righttoleft_code then
                        pos_h = ref_h - shift
                    else
                        pos_h = ref_h + shift
                    end
                    pos_v = ref_v - (cur_v + height)
                end
                goto process
              ::posdone::
                if anchor and anchor > 0 then
                    pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5])
                    pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth)
                end
              ::process::
                if source then
                    -- move this into apply_anchor
                    local anchor_h = pos_h
                    local anchor_v = pos_v
                    if usedorientation then
                        if usedorientation == 1 then
                            anchor_v = anchor_v - (width - height)
                        elseif usedorientation == 2 then
                            anchor_v = anchor_v - (depth - height)
                        elseif usedorientation == 3 then -- weird
                            anchor_v = anchor_v + (height - width)
                        end
                    end
                    anchors[source] = { anchor_h, anchor_v, width, height, depth }
                end
             -- if usedorientation then
             --     pushorientation(usedorientation,pos_h,pos_v,pos_r)
             -- end
             -- if source then
             --     flushstored(current,source,true)
             -- end
                if id == vlist_code then
                    vlist_out(current,list)
                else
                    hlist_out(current,list)
                end
             -- if source then
             --     flushstored(current,source,false)
             -- end
             -- if usedorientation then
             --     poporientation(usedorientation,pos_h,pos_v,pos_r)
             -- end
            end
            cur_v = cur_v + height + depth

        elseif id == kern_code then
            cur_v = cur_v + getkern(current)
        elseif id == rule_code then
            local width, height, depth = getwhd(current)
            cur_v = cur_v + height + depth
        elseif id == whatsit_code then
            if subtype == userdefined_code then
                local action = userdefined[getprop(current,"id")]
                if action then
                    if not cur_b then
                        local wd, ht, dp = getwhd(this_box)
                        cur_b = { wd, ht, dp, ref_h, ref_v, ref_r }
                    end
                    action(current,pos_h,pos_v)
                end
            end
        else
            goto synced
        end
        pos_h = ref_h
        ::synced::
    end
    pos_h = ref_h
    pos_v = ref_v
    pos_r = ref_r
end

function drivers.converters.analyze(driver,box)

    if not driver then
        report("error in converter, no driver")
        return
    elseif box then
        box = tonut(box)
    else
        report("error in converter, no box")
        return
    end

    local width, height, depth = getwhd(box)
    local total = height + depth

    if height > maxdimen or depth > maxdimen or width > maxdimen or total > maxdimen then
        report("error in converter, overflow")
        return
    end

    local actions   = driver.actions
    local flushers  = driver.flushers

    initialize      = actions.initialize
    finalize        = actions.finalize

    userdefined     = flushers.userdefined

    reset_dir_stack()
    reset_state()

    pos_r = getdirection(box)
    pos_v = depth
    pos_h = pos_r == righttoleft_code and width or 0

    local details = {
        boundingbox = { 0, 0, width, total },
    }

    if initialize then
        initialize(driver,details)
    end

    if getid(box) == vlist_code then
        vlist_out(box,getlist(box))
    else
        hlist_out(box,getlist(box))
    end

    if finalize then
        finalize(driver,details)
    end

end

