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

-- This is rather experimental code.

local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
local gsub, find, topattern, format = string.gsub, string.find, string.topattern, string.format
local lpegmatch = lpeg.match
local texattribute = tex.attribute
local allocate = utilities.storage.allocate
local settings_to_hash = utilities.parsers.settings_to_hash

local trace_tags = false  trackers.register("structures.tags", function(v) trace_tags = v end)

local report_tags = logs.reporter("structure","tags")

local attributes, structures = attributes, structures

local a_tagged       = attributes.private('tagged')

local unsetvalue     = attributes.unsetvalue
local codeinjections = backends.codeinjections

local taglist    = allocate()
local properties = allocate()
local labels     = allocate()
local stack      = { }
local chain      = { }
local ids        = { }
local enabled    = false
local tagdata    = { } -- used in export

local tags       = structures.tags
tags.taglist     = taglist -- can best be hidden
tags.labels      = labels
tags.data        = tagdata

local properties = allocate {

    document           = { pdf = "Div",        nature = "display" },

    division           = { pdf = "Div",        nature = "display" },
    paragraph          = { pdf = "P",          nature = "mixed"   },
    construct          = { pdf = "Span",       nature = "inline"  },

    section            = { pdf = "Sect",       nature = "display" },
    sectiontitle       = { pdf = "H",          nature = "mixed"   },
    sectionnumber      = { pdf = "H",          nature = "mixed"   },
    sectioncontent     = { pdf = "Div",        nature = "display" },

    itemgroup          = { pdf = "L",          nature = "display" },
    item               = { pdf = "Li",         nature = "display" },
    itemtag            = { pdf = "Lbl",        nature = "mixed"   },
    itemcontent        = { pdf = "LBody",      nature = "mixed"   },

    description        = { pdf = "Div",        nature = "display" },
    descriptiontag     = { pdf = "Div",        nature = "mixed"   },
    descriptioncontent = { pdf = "Div",        nature = "mixed"   },
    descriptionsymbol  = { pdf = "Span",       nature = "inline"  }, -- note reference

    verbatimblock      = { pdf = "Code",       nature = "display" },
    verbatimlines      = { pdf = "Code",       nature = "display"   },
    verbatimline       = { pdf = "Code",       nature = "mixed"   },
    verbatim           = { pdf = "Code",       nature = "inline"  },

    synonym            = { pdf = "Span",       nature = "inline"  },
    sort               = { pdf = "Span",       nature = "inline"  },

    register           = { pdf = "Div",        nature = "display" },
    registersection    = { pdf = "Div",        nature = "display" },
    registertag        = { pdf = "Span",       nature = "mixed"   },
    registerentries    = { pdf = "Div",        nature = "display" },
    registerentry      = { pdf = "Span",       nature = "mixed"   },
    registersee        = { pdf = "Span",       nature = "mixed"   },
    registerpages      = { pdf = "Span",       nature = "mixed"   },
    registerpage       = { pdf = "Span",       nature = "inline"  },
    registerpagerange  = { pdf = "Span",       nature = "mixed"   },

    table              = { pdf = "Table",      nature = "display" },
    tablerow           = { pdf = "TR",         nature = "display" },
    tablecell          = { pdf = "TD",         nature = "mixed"   },
    tabulate           = { pdf = "Table",      nature = "display" },
    tabulaterow        = { pdf = "TR",         nature = "display" },
    tabulatecell       = { pdf = "TD",         nature = "mixed"   },

    list               = { pdf = "TOC",        nature = "display" },
    listitem           = { pdf = "TOCI",       nature = "display" },
    listtag            = { pdf = "Lbl",        nature = "mixed"   },
    listcontent        = { pdf = "P",          nature = "mixed"   },
    listdata           = { pdf = "P",          nature = "mixed"   },
    listpage           = { pdf = "Reference",  nature = "mixed"   },

    delimitedblock     = { pdf = "BlockQuote", nature = "display" },
    delimited          = { pdf = "Quote",      nature = "inline"  },
    subsentence        = { pdf = "Span",       nature = "inline"  },

    float              = { pdf = "Div",        nature = "display" }, -- Figure
    floatcaption       = { pdf = "Caption",    nature = "display" },
    floattag           = { pdf = "Span",       nature = "mixed"   },
    floattext          = { pdf = "Span",       nature = "mixed"   },
    floatcontent       = { pdf = "P",          nature = "mixed"   },

    image              = { pdf = "P",          nature = "mixed"   },
    mpgraphic          = { pdf = "P",          nature = "mixed"   },

    formulaset         = { pdf = "Div",        nature = "display" },
    formula            = { pdf = "Div",        nature = "display" }, -- Formula
    formulatag         = { pdf = "Span",       nature = "mixed"   },
    formulacontent     = { pdf = "P",          nature = "display" },
    subformula         = { pdf = "Div",        nature = "display" },

    link               = { pdf = "Link",       nature = "inline"  },

    margintextblock    = { pdf = "Span",       nature = "inline"  },
    margintext         = { pdf = "Span",       nature = "inline"  },

    math               = { pdf = "Div",        nature = "display" },
    mn                 = { pdf = "Span",       nature = "mixed"   },
    mi                 = { pdf = "Span",       nature = "mixed"   },
    mo                 = { pdf = "Span",       nature = "mixed"   },
    ms                 = { pdf = "Span",       nature = "mixed"   },
    mrow               = { pdf = "Span",       nature = "display" },
    msubsup            = { pdf = "Span",       nature = "display" },
    msub               = { pdf = "Span",       nature = "display" },
    msup               = { pdf = "Span",       nature = "display" },
    merror             = { pdf = "Span",       nature = "mixed"   },
    munderover         = { pdf = "Span",       nature = "display" },
    munder             = { pdf = "Span",       nature = "display" },
    mover              = { pdf = "Span",       nature = "display" },
    mtext              = { pdf = "Span",       nature = "mixed"   },
    mfrac              = { pdf = "Span",       nature = "display" },
    mroot              = { pdf = "Span",       nature = "display" },
    msqrt              = { pdf = "Span",       nature = "display" },

}

tags.properties = properties

function tags.settagproperty(tag,key,value)
    local p = properties[tag]
    if p then
        p[key] = value
    else
        properties[tag] = { [key] = value }
    end
end

local lasttags = { }
local userdata = { }

tags.userdata = userdata

function tags.start(tag,specification)
    local label, detail, user
    if specification then
        label, detail, user = specification.label, specification.detail, specification.userdata
    end
    if not enabled then
        codeinjections.enabletags()
        enabled = true
    end
    labels[tag] = label ~= "" and label or tag
    local fulltag
    if detail and detail ~= "" then
        fulltag = tag .. ":" .. detail
    else
        fulltag = tag
    end
    local t = #taglist + 1
    local n = (ids[fulltag] or 0) + 1
    ids[fulltag] = n
    lasttags[tag] = n
--~ print("SETTING",tag,n)
    local completetag = fulltag .. "-" .. n
    chain[#chain+1] = completetag -- insert(chain,tag .. ":" .. n)
    stack[#stack+1] = t -- insert(stack,t)
    taglist[t] = { unpack(chain) } -- we can add key values for alt and actualtext if needed
    if user and user ~= "" then
        -- maybe we should merge this into taglist or whatever ... anyway there is room to optimize
        -- taglist.userdata = settings_to_hash(user)
        userdata[completetag] = settings_to_hash(user)
    end
    texattribute[a_tagged] = t
    return t
end

function tags.stop()
    local t = stack[#stack] stack[#stack] = nil -- local t = remove(stack)
    if not t then
        if trace_tags then
            report_tags("ignoring end tag, previous chain: %s",#chain > 0 and concat(chain[#chain]) or "none")
        end
        t = unsetvalue
    else
        chain[#chain] = nil -- remove(chain)
    end
    texattribute[a_tagged] = t
    return t
end

function tags.getid(tag,detail)
    if detail and detail ~= "" then
        return ids[tag .. ":" .. detail] or "?"
    else
        return ids[tag] or "?"
    end
end

function tags.last(tag)
    return lasttags[tag] -- or false
end

function tags.lastinchain()
    return chain[#chain]
end

function tags.registerdata(data)
    local fulltag = chain[#chain]
    if fulltag then
        tagdata[fulltag] = data
    end
end

function structures.atlocation(str)
    local location = gsub(concat(taglist[texattribute[a_tagged]],"-"),"%-%d+","")
    return find(location,topattern(str)) ~= nil
end

function tags.handler(head)  -- we need a dummy
    return head, false
end

statistics.register("structure elements", function()
    if enabled then
        return format("%s element chains identified",#taglist)
    else
        return nil
    end
end)

directives.register("backend.addtags", function(v)
    if not enabled then
        codeinjections.enabletags()
        enabled = true
    end
end)
