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

-- Because we run into the 200 locals limit we now split the file into smaller
-- parts.

local tonumber = tonumber
local todimen = number.todimen
local sortedkeys, sortedhash, setmetatableindex, concat, insert = table.sortedkeys, table.sortedhash, table.setmetatableindex, table.concat, table.insert
local settings_to_hash = utilities.parsers.settings_to_hash
local lpegmatch = lpeg.match
local formatters = string.formatters

local references        = structures.references
local structurestags    = structures.tags
local taglist           = structurestags.taglist
local specifications    = structurestags.specifications
local properties        = structurestags.properties
local locatedtag        = structurestags.locatedtag

local backend           = structurestags.backend

local setattribute      = backend.setattribute
local extras            = backend.extras
local checks            = backend.checks
local fixes             = backend.fixes
local listdata          = backend.listdata
local finalizers        = backend.finalizers
local usedstyles        = backend.usedstyles -- for now
local usedimages        = backend.usedimages -- for now
local referencehash     = backend.referencehash
local destinationhash   = backend.destinationhash

local implement         = interfaces.implement

do

    local itemgroups = { }

    local function setitemgroup(packed,continue,level,symbol)
        itemgroups[locatedtag("itemgroup")] = {
            packed   = packed,
            continue = continue,
            symbol   = symbol,
            level    = level,
        }
    end

    local function getitemgroup(fulltag)
        return itemgroups[fulltag]
    end

    local function setitem(kind)
        itemgroups[locatedtag("item")] = {
            kind = kind,
        }
    end

    function extras.itemgroup(di,element,n,fulltag)
        local hash = itemgroups[fulltag]
        if hash then
            setattribute(di,"packed",hash.packed and "yes" or nil)
            setattribute(di,"continue",hash.continue and "yes" or nil)
            setattribute(di,"symbol",hash.symbol)
            setattribute(di,"level",hash.level)
        end
    end

    function extras.item(di,element,n,fulltag)
        local hash = itemgroups[fulltag]
        if hash then
            local kind = hash.kind
            if kind and kind ~= "" then
                setattribute(di,"kind",kind)
            end
        end
    end

    implement {
        name      = "settagitemgroup",
        actions   = setitemgroup,
        arguments = { "boolean", "boolean", "integer", "string" }
    }

    implement {
        name      = "settagitem",
        actions   = setitem,
        arguments = "string"
    }

    structurestags.setitemgroup = setitemgroup
    structurestags.getitemgroup = getitemgroup
    structurestags.setitem      = setitem

end

do

    local registered = structures.sections.registered

    local function resolve(di,element,n,fulltag)
        local data = listdata[fulltag]
        if data then
            extras.addreference(di,data.references)
            return true
        else
            local data = di.data
            if data then
                for i=1,#data do
                    local di = data[i]
                    if di then
                        local ft = di.fulltag
                        if ft and resolve(di,element,n,ft) then
                            return true
                        end
                    end
                end
            end
        end
    end

    function extras.section(di,element,n,fulltag)
        local r = registered[specifications[fulltag].detail]
        if r then
            setattribute(di,"level",r.level)
        end
        resolve(di,element,n,fulltag)
    end

    local floats = { }

    local function setfloat(options,method)
        floats[locatedtag("float")] = {
            options = options,
            method  = method,
        }
    end

    function extras.float(di,element,n,fulltag)
        local hash = floats[fulltag]
        if hash then
            local method  = hash.method
            if not method or method == "" then
                method = "here"
            end
            setattribute(di,"method",method)
            local options = hash.options
            if options and options ~= "" then
                options = settings_to_hash(options)
                options[method] = nil
                options = concat(sortedkeys(options),",")
                if #options > 0 then
                    setattribute(di,"options",options)
                end
            end
        end
        resolve(di,element,n,fulltag)
    end

    implement {
        name      = "settagfloat",
        actions   = setfloat,
        arguments = "2 strings",
    }

    structurestags.setfloat = setfloat

end

do

    local registered = { }

    local function setformulacontent(n,image)
        registered[locatedtag("formulacontent")] = {
            n     = n,
            image = image,
        }
    end

    function extras.formulacontent(di,element,n,fulltag)
        local r = registered[fulltag]
        if r then
            setattribute(di,"n",r.n)
            if tonumber(r.image) ~= 0 then
                -- image is the export index
                setattribute(di,"image",tex.jobname .. "-image-exported-" .. r.image)
            end
        end
    end

    implement {
        name      = "settagformulacontent",
        actions   = setformulacontent,
        arguments = "2 strings",
    }

    structurestags.setformulacontent = setformulacontent

end

do

    local symbols = { }

    local function settagdelimitedsymbol(symbol)
        symbols[locatedtag("delimitedsymbol")] = {
            symbol = symbol,
        }
    end

    function extras.delimitedsymbol(di,element,n,fulltag)
        local hash = symbols[fulltag]
        if hash then
            setattribute(di,"symbol",hash.symbol or nil)
        end
    end

    implement {
        name      = "settagdelimitedsymbol",
        actions   = settagdelimitedsymbol,
        arguments = "string"
    }

    structurestags.settagdelimitedsymbol = settagdelimitedsymbol

end


do

    local symbols = { }

    local function settagsubsentencesymbol(symbol)
        symbols[locatedtag("subsentencesymbol")] = {
            symbol = symbol,
        }
    end

    function extras.subsentencesymbol(di,element,n,fulltag)
        local hash = symbols[fulltag]
        if hash then
            setattribute(di,"symbol",hash.symbol or nil)
        end
    end

    implement {
        name      = "settagsubsentencesymbol",
        actions   = settagsubsentencesymbol,
        arguments = "string"
    }

    structurestags.settagsubsentencesymbol = settagsubsentencesymbol

end

do

    -- todo: register prerolled alt

    local synonyms = { }
    local sortings = { }

    local function setsynonym(tag)
        synonyms[locatedtag("synonym")] = tag
    end

    function extras.synonym(di,element,n,fulltag)
        local tag = synonyms[fulltag]
        if tag then
            setattribute(di,"tag",tag)
        end
    end

    local function setsorting(tag)
        sortings[locatedtag("sorting")] = tag
    end

    function extras.sorting(di,element,n,fulltag)
        local tag = sortings[fulltag]
        if tag then
            setattribute(di,"tag",tag)
        end
    end

    implement {
        name      = "settagsynonym",
        actions   = setsynonym,
        arguments = "string"
    }

    implement {
        name      = "settagsorting",
        actions   = setsorting,
        arguments = "string"
    }

    structurestags.setsynonym = setsynonym
    structurestags.setsorting = setsorting

    structurestags.getsynonym = function(fulltag) return synonyms[fulltag] end
    structurestags.getsorting = function(fulltag) return sortings[fulltag] end

end

do

    local descriptions = { }
    local symbols      = { }
    local linked       = { }
    local listindices  = { }

    -- we could move the notation itself to the first reference (can be an option)

    -- this is not ok ...

    local function setdescriptionindex(n,l)
        local fulltag = locatedtag("description")
        if fulltag then
            -- why not in ... we are not to much off here
         -- local spec = specifications[fulltag]
            listindices[fulltag] = {
                noteindex = n,
                listindex = l,
            }
        else
        end
    end

    local function setnotation(tag,n) -- needs checking (is tag needed)
        -- we can also use the internals hash or list
        local nd = structures.notes.get(tag,n)
        if nd then
            local references = nd.references
            descriptions[references and references.internal] = locatedtag("description")
        end
    end

    local function setnotationsymbol(tag,n) -- needs checking (is tag needed)
        local nd = structures.notes.get(tag,n) -- todo: use listdata instead
        if nd then
            local references = nd.references
            symbols[references and references.internal] = locatedtag("descriptionsymbol")
        end
    end

    function finalizers.descriptions(tree)
        local n = 0
        for id, tag in sortedhash(descriptions) do
            local sym = symbols[id]
            if sym then
                n = n + 1
                linked[tag] = n
                linked[sym] = n
            end
        end
    end

    function extras.description(di,element,n,fulltag)
        local id = linked[fulltag]
        if id then
            setattribute(di,"insert",id)
        end
    end

    function extras.descriptionsymbol(di,element,n,fulltag)
        local id = linked[fulltag]
        if id then
            setattribute(di,"insert",id)
        end
    end

    implement {
        name      = "settagdescriptionindex",
        actions   = setdescriptionindex,
        arguments = "2 integers",
    }

    implement {
        name      = "settagnotation",
        actions   = setnotation,
        arguments = { "string", "integer" }
    }

    implement {
        name      = "settagnotationsymbol",
        actions   = setnotationsymbol,
        arguments = { "string", "integer" }
    }

    structurestags.setdescriptionindex = setdescriptionindex
    structurestags.setnotation         = setnotation
    structurestags.setnotationsymbol   = setnotationsymbol

    function structurestags.getdescriptionindex(fulltag)
        return listindices[fulltag]
    end

end


do

    local strippedtag    = structurestags.strip -- we assume global styles

    local highlight      = { }
    local construct      = { }

    usedstyles.highlight = highlight
    usedstyles.construct = construct

    local function sethighlight(name,style,color,mode)
        if not highlight[name] then
            highlight[name] = {
                style = style,
                color = color,
                mode  = mode == 1 and "display" or nil,
            }
        end
    end

    local function setconstruct(name,style,color,mode)
        if not construct[name] then
            construct[name] = {
                style = style,
                color = color,
                mode  = mode == 1 and "display" or nil,
            }
        end
    end

    implement {
        name      = "settagconstruct",
        actions   = setconstruct,
        arguments = { "string", "string", "integer", "integer" }
    }

    implement {
        name      = "settaghighlight",
        actions   = sethighlight,
        arguments = { "string", "string", "integer", "integer" }
    }

    structurestags.sethighlight = sethighlight
    structurestags.setconstruct = setconstruct

end

do

    local image          = { }
    local mpgraphic      = { }
    local nofimages      = 0
    local nofmpgraphics  = 0
    usedimages.image     = image
    usedimages.mpgraphic = mpgraphic

    structurestags.usewithcare.image     = image
    structurestags.usewithcare.mpgraphic = mpgraphic

    local function setfigure(
        name,used,page,
        width,height,depth,label,
        category,exported,
        descriptiontext,alternativetext
    )
        local fulltag = locatedtag("image") -- we actually can be deeper
        local spec    = specifications[fulltag]
        if spec then
            local id
            if tonumber(exported) then
                id = tex.jobname .. "-image-exported-" .. exported
            else
                nofimages = nofimages + 1
                id = tex.jobname .. "-image-" .. nofimages
            end
            local page = tonumber(page)
            --
            -- This is rather ugly but very much a side effect of MS wanting to be able to tag
            -- them as figure so these /Alt's are mandate then .. sigh:
            --
            -- \startimage[alternativetext=outer] \startMPcode
            -- \startimage                        \startMPcode[alternativetext=inner wins]
            -- \startMPcode[alternativetext=inner wins]
            -- \startimage                        \startMPcode
            -- \startMPcode
            --
            -- which gets processed (set) in the reverse order but so be it.
            --
            local found = image[fulltag]
            if found then
                if descriptiontext == "" then descriptiontext = found.descriptiontext or descriptiontext end
                if alternativetext == "" then alternativetext = found.alternativetext or alternativetext end
            end
            --
            image[fulltag] = {
                exported        = exported,
                id              = id,
                image           = id,
                name            = name,
                used            = used,
                page            = page and page > 1 and page or nil,
                width           = todimen(width,"pt","%0.3F%s"),
                height          = todimen(height+depth,"pt","%0.3F%s"),
                drop            = depth ~= 0 and todimen(depth,"pt","%0.3F%s") or nil,
                label           = label,
                category        = category,
                descriptiontext = descriptiontext,
                alternativetext = alternativetext,
            }
        else
            -- we ignore images in layers in the background / pagebody
        end
    end

    local function setmpgraphic(
        descriptiontext,alternativetext
    )
        local fulltag = locatedtag("mpgraphic")
        local spec    = specifications[fulltag]
        if spec then
            mpgraphic[fulltag] = {
                descriptiontext = descriptiontext,
                alternativetext = alternativetext,
            }
        end
    end

    function extras.image(di,element,n,fulltag)
        local data = image[fulltag]
        if data then
            local id = data.id
            -- can be less
            setattribute(di,"name",data.name)
            setattribute(di,"page",data.page)
            setattribute(di,"exported",data.exported) -- can be nil
            setattribute(di,"id",id)
            setattribute(di,"image",id)
            setattribute(di,"width",data.width)
            setattribute(di,"height",data.height)
            setattribute(di,"drop",data.drop)
            setattribute(di,"label",data.label)
            setattribute(di,"descriptiontext",data.descriptiontext)
            setattribute(di,"alternativetext",data.alternativetext)
            setattribute(di,"category",data.category)
        end
    end

    function extras.mpgraphic(di,element,n,fulltag)
        local data = image[fulltag]
        if data then
            setattribute(di,"descriptiontext",data.descriptiontext)
            setattribute(di,"alternativetext",data.alternativetext)
        end
    end

    implement {
        name      = "settagfigure",
        actions   = setfigure,
        arguments = {
            "string",    -- name
            "string",    -- used
            "string",    -- page
            "dimension", -- width
            "dimension", -- height
            "dimension", -- depth
            "string",    -- label
            "string",    -- category
            "string",    -- exported
            "string",    -- descriptiontext
            "string",    -- alternativetext
        }
    }

   implement {
        name      = "settagmpgraphic",
        actions   = setmpgraphic,
        arguments = {
            "string",    -- descriptiontext
            "string",    -- alternativetext
        }
    }

    structurestags.setfigure    = setfigure
    structurestags.setmpgraphic = setmpgraphic

end

do

    local combinations     = { }
    local combinationpairs = { }

    local function setcombination(nx,ny)
        combinations[locatedtag("combination")] = {
            nx = nx,
            ny = ny,
        }
    end
    local function setcombinationpair(x,y)
        combinationpairs[locatedtag("combinationpair")] = {
            x = x,
            y = y,
        }
    end

    function extras.combination(di,element,n,fulltag)
        local data = combinations[fulltag]
        if data then
            setattribute(di,"nx",data.nx)
            setattribute(di,"ny",data.ny)
        end
    end
    function extras.combinationpair(di,element,n,fulltag)
        local data = combinationpairs[fulltag]
        if data then
            setattribute(di,"x",data.x)
            setattribute(di,"y",data.y)
        end
    end

    implement {
        name      = "settagcombination",
        actions   = setcombination,
        arguments = { "integer", "integer" }
    }
    implement {
        name      = "settagcombinationpair",
        actions   = setcombinationpair,
        arguments = { "integer", "integer" }
    }

    structurestags.setcombination     = setcombination
    structurestags.setcombinationpair = setcombinationpair

end

do

    local function hascontent(data)
        for i=1,#data do
            local di = data[i]
            if not di or di.tg == "ignore" then
                --
            else
                local content = di.content
                if content == " " then
                    --
                elseif content then
                    return true
                else
                    local d = di.data
                    if d and #d > 0 and hascontent(d) then
                        return true
                    end
                end
            end
        end
    end

    local tabledata = { }

    local function settablecell(rows,columns,align)
        if align > 0 or rows > 1 or columns > 1 then -- or kind > 0
            tabledata[locatedtag("tablecell")] = {
                rows    = rows,
                columns = columns,
                align   = align,
            }
        end
    end

    local function gettablecell(fulltag)
        return tabledata[fulltag]
    end

    function extras.tablecell(di,element,n,fulltag)
        local hash = tabledata[fulltag]
        if hash then
            local columns = hash.columns
            if columns and columns > 1 then
                setattribute(di,"columns",columns)
            end
            local rows = hash.rows
            if rows and rows > 1 then
                setattribute(di,"rows",rows)
            end
            local align = hash.align
            if not align or align == 0 then
                -- normal
            elseif align == 1 then -- use numbertoalign here
                setattribute(di,"align","flushright")
            elseif align == 2 then
                setattribute(di,"align","middle")
            elseif align == 3 then
                setattribute(di,"align","flushleft")
            end
        end
    end

    local tabulatedata = { }

    local function settabulatecell(align,kind)
        if align > 0 or kind > 0 then
            tabulatedata[locatedtag("tabulatecell")] = {
                align = align,
                kind  = kind, -- 1 = bold head
            }
        end
    end

    local function gettabulatecell(fulltag)
        return tabulatedata[fulltag]
    end

    function extras.tabulate(di,element,n,fulltag)
        local data = di.data
        for i=1,#data do
            local di = data[i]
            if di.tg == "tabulaterow" and not hascontent(di.data) then
                di.element = "" -- or simply remove
            end
        end
    end

    function extras.tabulatecell(di,element,n,fulltag)
        local hash = tabulatedata[fulltag]
        if hash then
            local align = hash.align
            if not align or align == 0 then
                -- normal
            elseif align == 1 then
                setattribute(di,"align","flushleft")
            elseif align == 2 then
                setattribute(di,"align","flushright")
            elseif align == 3 then
                setattribute(di,"align","middle")
            end
            local kind = hash.kind
            if kind == 1 then
                setattribute(di,"kind","strong")
            elseif kind == 2 then
                setattribute(di,"kind","equals")
            end
        end
    end

    implement {
        name      = "settagtablecell",
        actions   = settablecell,
        arguments = { "integer", "integer", "integer" }
    }

    implement {
        name      = "settagtabulatecell",
        actions   = settabulatecell,
        arguments = { "integer", "integer" },
    }

    structurestags.settablecell    = settablecell
    structurestags.gettablecell    = gettablecell
    structurestags.settabulatecell = settabulatecell
    structurestags.gettabulatecell = gettabulatecell

end

do

    -- todo: internal is already hashed

    local p_stripper = lpeg.patterns.stripper

    local function setregister(tag,n) -- check if tag is needed
        local data = structures.registers.get(tag,n)
        if data then
            referencehash[locatedtag("registerlocation")] = data
        end
    end

    function extras.registerlocation(di,element,n,fulltag)
        local data = referencehash[fulltag]
        if type(data) == "table" then
            extras.addinternal(di,data.references)
            return true
        else
            -- needs checking, probably bookmarks
        end
    end

    function extras.registerpages(di,element,n,fulltag) -- ignorebreaks
        local data = di.data
        for i=1,#data do
            local d = data[i]
            if d.content == " " then
                d.content = ""
            end
        end
    end

    function extras.registerseparator(di,element,n,fulltag) -- ignorespaces
        local data = di.data
        for i=1,#data do
            local d = data[i]
            local c = d.content
            if type(c) == "string" then
                d.content = lpegmatch(p_stripper,c)
            end
        end
    end

    implement {
        name      = "settagregister",
        actions   = setregister,
        arguments = { "string", "integer" }
    }

    structurestags.setregister = setregister

end

do

    -- todo: internal is already hashed

    local function setlist(n)
        local data = structures.lists.getresult(n)
        if data then
            referencehash[locatedtag("listitem")] = data
        end
    end

    function extras.listitem(di,element,n,fulltag)
        local data = referencehash[fulltag]
        if data then
            extras.addinternal(di,data.references)
            return true
        end
    end

    implement {
        name      = "settaglist",
        actions   = setlist,
        arguments = "integer",
    }

end

do

    local list = { }

    function structurestags.gettagreferencedescription(id)
        return list[id]
    end

    implement {
        name      = "settagsetreferencedescription",
        actions   = function(id,str) list[id] = str end,
        arguments = { "integer", "string" },
    }

end

do

    local usedpublications = { }
    local tagsindatasets   = setmetatableindex("table")
    local serialize        = false

    local function setpublication(dataset,tag,rendering)
        usedpublications[locatedtag("publication")] = {
            dataset   = dataset,
            tag       = tag,
            rendering = rendering
        }
        tagsindatasets[dataset][tag] = true
        if not serialize then
            structures.tags.registerextradata("btx",function()
                local t = { "<btxdata>"}
                for dataset, used in sortedhash(tagsindatasets) do
                    t[#t+1] = publications.converttoxml(dataset,true,false,true,false,true,true)
                end
                t[#t+1] = "</btxdata>"
                return concat(t,"\n")
            end)
        end
    end

    function extras.publication(di,element,n,fulltag)
        local hash = usedpublications[fulltag]
        if hash then
            setattribute(di,"dataset",hash.dataset)
            setattribute(di,"tag",hash.tag)
        end
    end

    implement {
        name      = "settagpublication",
        actions   = setpublication,
        arguments = "2 strings"
    }

    structurestags.setpublication = setpublication

end

do

    local usedparagraphs = { }

    local function setparagraph(align)
        if align ~= "" then
            usedparagraphs[locatedtag("paragraph")] = {
                align = align,
            }
        end
    end

    function extras.paragraph(di,element,n,fulltag)
        local hash = usedparagraphs[fulltag]
        if hash then
            setattribute(di,"align",hash.align)
        end
    end

    implement {
        name      = "settagparagraph",
        actions   = setparagraph,
        arguments = "string"
    }

    structurestags.setparagraph = setparagraph

end

do

    local marginanchors = { }
    local margincontent = { }

    function checks.margintext(di)
        local i = marginanchors[di.fulltag]
        margincontent[i] = di
    end

    function checks.marginanchor(di)
        local i = marginanchors[di.fulltag]
        local d = margincontent[i]
        --
        di.attribute = d.attribute
        di.data      = d.data
        di.detail    = d.detail
        di.element   = d.element
        di.fulltag   = d.fulltag
        di.nature    = d.nature
        di.samepar   = true
        di.tg        = d.tg
        --
        d.skip       = "ignore"
    end

    implement {
        name      = "settagmargintext",
        arguments = "integer",
        actions   = function(n)
            marginanchors[locatedtag("margintext")] = n
        end
    }

    implement {
        name      = "settagmarginanchor",
        arguments = "integer",
        actions   = function(n)
            marginanchors[locatedtag("marginanchor")] = n
        end
    }

end

do

    function fixes.linenumber(di,data,i)
        local ni = data[i+1]
        if ni then
            if ni.data then
                while true do
                    local d = ni.data[1]
                    if d then
                        local e = d.element
                        if e then
                            if e == "line" or e == "verbatimline" then
                                insert(d.data,1,di)
                                data[i] = false
                                return
                            else
                                ni = d
                            end
                        else
                            return
                        end
                    else
                        return
                    end
                end
            end
        end
    end

end


do

    local usedcodepoints = { }

    local function checkcodepoint(di,element,n,fulltag)
        local unicode = usedcodepoints[fulltag]
        if unicode then
            setattribute(di,"codepoint",unicode)
        end
    end

    local function setcodepoint(tag,unicode)
        usedcodepoints[locatedtag(tag)] = unicode
        if not extras[tag] then
            extras[tag] = checkcodepoint
        end
    end

    implement {
        name      = "settagcodepoint",
        actions   = setcodepoint,
        arguments = { "argument", "integer" },
    }

    structurestags.setcodepoint = setcodepoint

end
