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

-- this one runs on top of buffers and structure

local type, next = type, next
local find, formatters, validstring = string.find, string.formatters, string.valid
local settings_to_set, settings_to_array = utilities.parsers.settings_to_set, utilities.parsers.settings_to_array
local allocate = utilities.storage.allocate

local context     = context
local commands    = commands

local implement   = interfaces.implement

local structures  = structures

structures.blocks = structures.blocks or { }

local blocks      = structures.blocks
local sections    = structures.sections
local lists       = structures.lists
local helpers     = structures.helpers

local collected   = allocate()
local tobesaved   = allocate()
local states      = allocate()

blocks.collected  = collected
blocks.tobesaved  = tobesaved
blocks.states     = states

local function initializer()
    collected = blocks.collected
    tobesaved = blocks.tobesaved
end

job.register('structures.blocks.collected', tobesaved, initializer)

local listitem = utilities.parsers.listitem
local f_block  = formatters["block.%s"]

function blocks.uservariable(index,key,default)
    local c = collected[index]
    if c then
        local u = c.userdata
        if u then
            local v = u[key] or default
            if v then
                context(v)
            end
        end
    end
end

local function printblock(index,name,data,hide)
    if hide then
        context.dostarthiddenblock(index,name)
    else
        context.dostartnormalblock(index,name)
    end
    context.viafile(data,f_block(validstring(name,"noname")))
    if hide then
        context.dostophiddenblock()
    else
        context.dostopnormalblock()
    end
end

blocks.print = printblock

function blocks.define(name)
    states[name] = { all = "hide" }
end

function blocks.setstate(state,name,tag)
    local all  = tag == ""
    local tags = not all and settings_to_array(tag)
    for n in listitem(name) do
        local sn = states[n]
        if not sn then
            -- error
        elseif all then
            sn.all = state
        else
            for _, tag in next, tags do
                sn[tag] = state
            end
        end
    end
end


local function selectblocks(state,name,tag,criterium,action)
    if not criterium or criterium == "" then
        criterium = "text"
    end
    if find(tag,"=",1,true) then
        tag = ""
    end
    local names  = settings_to_set(name)
    local all    = tag == ""
    local tags   = not all and settings_to_set(tag)
    local hide   = state == "process"
    local result = lists.filter {
        names     = "all",
        criterium = criterium,
        number    = sections.numberatdepth(criterium), -- not needed
        collected = collected,
    }
    for i=1,#result do
        local ri = result[i]
        local metadata = ri.metadata
        if names[metadata.name] then
            if all then
                if action(ri.index,name,ri.data,hide) then
                    return
                end
            else
                local mtags = metadata.tags
                if mtags then
                    for tag, sta in next, tags do
                        if mtags[tag] then
                            if action(ri.index,name,ri.data,hide) then
                                return
                            else
                                break
                            end
                        end
                    end
                end
            end
        end
    end
end

function blocks.select(state,name,tag,criterium)
    selectblocks(state,name,tag,criterium,printblock)
end

function blocks.empty(state,name,tag,criterium)
    local found = false
    local function checkempty(_,_,data)
        found = type(data) == "string" and find(data,"%S")
        return found
    end
    selectblocks(state,name,tag,criterium,checkempty)
    return not found
end

function blocks.save(name,tag,userdata,buffer) -- wrong, not yet adapted
    local data  = buffers.getcontent(buffer)
    local tags  = settings_to_set(tag)
    local plus  = false
    local minus = false
    local last  = #tobesaved + 1
    local all   = states[name].all
    if tags['+'] then
        plus      = true
        tags['+'] = nil
    end
    if tags['-'] then
        minus     = true
        tags['-'] = nil
    end
    tobesaved[last] = helpers.simplify {
        metadata   = {
            name  = name,
            tags  = tags,
            plus  = plus,
            minus = minus,
        },
        index      = last,
        data       = data or "error",
        userdata   = userdata and type(userdata) == "string" and helpers.touserdata(userdata),
        references = {
            section = sections.currentid(),
        },
    }
    if not next(tags) then
        if all ~= "hide" then
            printblock(last,name,data)
        elseif plus then
            printblock(last,name,data,true)
        end
    else
        local sn = states[name]
        for tag, _ in next, tags do
            if sn[tag] == nil then
                if all ~= "hide" then
                    printblock(last,name,data)
                    break
                end
            elseif sn[tag] ~= "hide" then
                printblock(last,name,data)
                break
            end
        end
    end
    buffers.erase(buffer)
end


-- interface

implement { name = "definestructureblock",       actions = blocks.define,       arguments = "string" }
implement { name = "savestructureblock",         actions = blocks.save,         arguments = "4 strings" }
implement { name = "selectstructureblock",       actions = blocks.select,       arguments = "4 strings" }
implement { name = "setstructureblockstate",     actions = blocks.setstate,     arguments = "3 strings" }
implement { name = "structureblockuservariable", actions = blocks.uservariable, arguments = { "integer", "string" } }

implement {
    name      = "doifelsestructureblocksempty",
    arguments = "3 strings",
    actions   = function(name,tag,criterium)
        commands.doifelse(blocks.empty(false,name,tag,criterium))
    end,
}
