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

local type, next, tonumber, unpack = type, next, tonumber, unpack
local ceil = math.ceil
local match = string.match

local fonts = fonts

local trace_goodies      = false  trackers.register("fonts.goodies", function(v) trace_goodies = v end)
local report_goodies     = logs.reporter("fonts","goodies")

local registerotffeature = fonts.handlers.otf.features.register

local fontgoodies        = fonts.goodies or { }

local fontcharacters     = fonts.hashes.characters

local trace_defining     = false  trackers.register("math.defining",   function(v) trace_defining   = v end)

local report_math        = logs.reporter("mathematics","initializing")

local nuts               = nodes.nuts

local setlink            = nuts.setlink

local nodepool           = nuts.pool

local new_kern           = nodepool.kern
local new_glyph          = nodepool.glyph
local new_hlist          = nodepool.hlist
local new_vlist          = nodepool.vlist

local insertnodeafter    = nuts.insertafter

local helpers            = fonts.helpers
local upcommand          = helpers.commands.up
local rightcommand       = helpers.commands.right
local charcommand        = helpers.commands.char
local prependcommands    = helpers.prependcommands

-- experiment, we have to load the definitions immediately as they precede
-- the definition so they need to be initialized in the typescript

-- local function withscriptcode(tfmdata,unicode,data,action)
--     if type(unicode) == "string" then
--         local p, u = match(unicode,"^(.-):(.-)$")
--         if u then
--             u = tonumber(u)
--             if u then
--                 local slots = fonts.helpers.mathscriptslots(tfmdata,u)
--                 if slots then
--                     if p == "*" then
--                         action(u,data)
--                         for i=1,#slots do
--                             action(slots[i],data)
--                         end
--                     else
--                         p = tonumber(p)
--                         if p then
--                             action(slots[p],data)
--                         end
--                     end
--                 end
--             end
--         end
--     else
--         action(unicode,data)
--     end
-- end

-- local function finalize(tfmdata,feature,value)
--  -- if tfmdata.mathparameters then -- funny, cambria text has this
--     local goodies = tfmdata.goodies
--     if goodies then
--         local virtualized = mathematics.virtualized
--         for i=1,#goodies do
--             local goodie = goodies[i]
--             local mathematics = goodie.mathematics
--             local dimensions  = mathematics and mathematics.dimensions
--             if dimensions then
--                 if trace_defining then
--                     report_math("overloading dimensions in %a @ %p",tfmdata.properties.fullname,tfmdata.parameters.size)
--                 end
--                 local characters   = tfmdata.characters
--                 local descriptions = tfmdata.descriptions
--                 local parameters   = tfmdata.parameters
--                 local factor       = parameters.factor
--                 local hfactor      = parameters.hfactor
--                 local vfactor      = parameters.vfactor
--                 local function overloadone(unicode,data)
--                     local character = characters[unicode]
--                     if not character then
--                         local c = virtualized[unicode]
--                         if c then
--                             character = characters[c]
--                         end
--                     end
--                     if character then
--                         local width  = data.width
--                         local height = data.height
--                         local depth  = data.depth
--                         if trace_defining and (width or height or depth) then
--                             report_math("overloading dimensions of %C, width %p, height %p, depth %p",
--                                 unicode,width or 0,height or 0,depth or 0)
--                         end
--                         if width  then character.width  = width  * hfactor end
--                         if height then character.height = height * vfactor end
--                         if depth  then character.depth  = depth  * vfactor end
--                         --
--                         local xoffset = data.xoffset
--                         local yoffset = data.yoffset
--                         if xoffset == "llx" then
--                             local d = descriptions[unicode]
--                             if d then
--                                 xoffset         = - d.boundingbox[1] * hfactor
--                                 character.width = character.width + xoffset
--                                 xoffset         = rightcommand[xoffset]
--                             else
--                                 xoffset = nil
--                             end
--                         elseif xoffset and xoffset ~= 0 then
--                             xoffset = rightcommand[xoffset * hfactor]
--                         else
--                             xoffset = nil
--                         end
--                         if yoffset and yoffset ~= 0 then
--                             yoffset = upcommand[yoffset * vfactor]
--                         else
--                             yoffset = nil
--                         end
--                         if xoffset or yoffset then
--                             local commands = character.commands
--                             if commands then
--                                 prependcommands(commands,yoffset,xoffset)
--                             else
--                                 local slot = charcommand[unicode]
--                                 if xoffset and yoffset then
--                                     character.commands = { xoffset, yoffset, slot }
--                                 elseif xoffset then
--                                     character.commands = { xoffset, slot }
--                                 else
--                                     character.commands = { yoffset, slot }
--                                 end
--                             end
--                         end
--                     elseif trace_defining then
--                         report_math("no overloading dimensions of %C, not in font",unicode)
--                     end
--                 end
--                 local function overload(dimensions)
--                     for unicode, data in next, dimensions do
--                         withscriptcode(tfmdata,unicode,data,overloadone)
--                     end
--                 end
--                 if value == nil then
--                     value = { "default" }
--                 end
--                 if value == "all" or value == true then
--                     for name, value in next, dimensions do
--                         overload(value)
--                     end
--                 else
--                     if type(value) == "string" then
--                         value = utilities.parsers.settings_to_array(value)
--                     end
--                     if type(value) == "table" then
--                         for i=1,#value do
--                             local d = dimensions[value[i]]
--                             if d then
--                                 overload(d)
--                             end
--                         end
--                     end
--                 end
--             end
--         end
--     end
-- end
--
-- registerotffeature {
--     name         = "mathdimensions",
--     description  = "manipulate math dimensions",
--  -- default      = true,
--     manipulators = {
--         base = finalize,
--         node = finalize,
--     }
-- }

local function initialize(goodies)
    local mathgoodies = goodies.mathematics
    if mathgoodies then
        local virtuals = mathgoodies.virtuals
        local mapfiles = mathgoodies.mapfiles
        local maplines = mathgoodies.maplines
        if virtuals then
            for name, specification in next, virtuals do
                -- beware, they are all constructed ... we should be more selective
                mathematics.makefont(name,specification,goodies)
            end
        end
        if mapfiles then
            for i=1,#mapfiles do
                fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function
            end
        end
        if maplines then
            for i=1,#maplines do
                fonts.mappings.loadline(maplines[i]) -- todo: backend function
            end
        end
    end
end

fontgoodies.register("mathematics", initialize)

-- local enabled = false   directives.register("fontgoodies.mathkerning",function(v) enabled = v end)

-- local function initialize(tfmdata)
--  -- if enabled and tfmdata.mathparameters then -- funny, cambria text has this
--     if tfmdata.mathparameters then -- funny, cambria text has this
--         local goodies = tfmdata.goodies
--         if goodies then
--             local characters = tfmdata.characters
--             if characters[0x1D44E] then -- 119886
--                 -- we have at least an italic a
--                 for i=1,#goodies do
--                     local mathgoodies = goodies[i].mathematics
--                     if mathgoodies then
--                         local kerns = mathgoodies.kerns
--                         if kerns then
--                             local function kernone(unicode,data)
--                                 local chardata = characters[unicode]
--                                 if chardata and (not chardata.mathkerns or data.force) then
--                                     chardata.mathkerns = data
--                                 end
--                             end
--                             for unicode, data in next, kerns do
--                                 withscriptcode(tfmdata,unicode,data,kernone)
--                             end
--                             return
--                         end
--                     end
--                 end
--             else
--                 return -- no proper math font anyway
--             end
--         end
--     end
-- end
--
-- registerotffeature {
--     name         = "mathkerns",
--     description  = "math kerns",
--  -- default      = true,
--     initializers = {
--         base = initialize,
--         node = initialize,
--     }
-- }

-- -- math italics (not really needed)
-- --
-- -- it would be nice to have a \noitalics\font option
--
-- local function initialize(tfmdata)
--     local goodies = tfmdata.goodies
--     if goodies then
--         local shared = tfmdata.shared
--         for i=1,#goodies do
--             local mathgoodies = goodies[i].mathematics
--             if mathgoodies then
--                 local mathitalics = mathgoodies.italics
--                 if mathitalics then
--                     local properties = tfmdata.properties
--                     if properties.setitalics then
--                         mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics
--                         if mathitalics then
--                             if trace_goodies then
--                                 report_goodies("loading mathitalics for font %a",properties.name)
--                             end
--                             local corrections   = mathitalics.corrections
--                             local defaultfactor = mathitalics.defaultfactor
--                          -- properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
--                             if corrections then
--                                 fontgoodies.registerpostprocessor(tfmdata, function(tfmdata) -- this is another tfmdata (a copy)
--                                     -- better make a helper so that we have less code being defined
--                                     local properties = tfmdata.properties
--                                     local parameters = tfmdata.parameters
--                                     local characters = tfmdata.characters
--                                     properties.mathitalic_defaultfactor = defaultfactor
--                                     properties.mathitalic_defaultvalue  = defaultfactor * parameters.quad
--                                     if trace_goodies then
--                                         report_goodies("assigning mathitalics for font %a",properties.name)
--                                     end
--                                     local quad    = parameters.quad
--                                     local hfactor = parameters.hfactor
--                                     for k, v in next, corrections do
--                                         local c = characters[k]
--                                         if c then
--                                             if v > -1 and v < 1 then
--                                                 c.italic = v * quad
--                                             else
--                                                 c.italic = v * hfactor
--                                             end
--                                         else
--                                             report_goodies("invalid mathitalics entry %U for font %a",k,properties.name)
--                                         end
--                                     end
--                                 end)
--                             end
--                             return -- maybe not as these can accumulate
--                         end
--                     end
--                 end
--             end
--         end
--     end
-- end
--
-- registerotffeature {
--     name         = "mathitalics",
--     description  = "additional math italic corrections",
--  -- default      = true,
--     initializers = {
--         base = initialize,
--         node = initialize,
--     }
-- }

local function mathradicalaction(n,h,v,font,mchar,echar)
    local characters = fontcharacters[font]
    local mchardata  = characters[mchar]
    local echardata  = characters[echar]
    local ewidth     = echardata.width
    local mwidth     = mchardata.width
    local delta      = h - ewidth
    local glyph      = new_glyph(font,echar)
    local head       = glyph
    if delta > 0 then
        local count = ceil(delta/mwidth)
        local kern  = (delta - count * mwidth) / count
        for i=1,count do
            local k = new_kern(kern)
            local g = new_glyph(font,mchar)
            setlink(k,head)
            setlink(g,k)
            head = g
        end
    end
    local height = mchardata.height
    local list   = new_hlist(head)
    local kern   = new_kern(height-v)
    list = setlink(kern,list)
    local list = new_vlist(kern)
    insertnodeafter(n,n,list)
end

local function mathhruleaction(n,h,v,font,bchar,mchar,echar)
    local characters = fontcharacters[font]
    local bchardata  = characters[bchar]
    local mchardata  = characters[mchar]
    local echardata  = characters[echar]
    local bwidth     = bchardata.width
    local mwidth     = mchardata.width
    local ewidth     = echardata.width
    local delta      = h - ewidth - bwidth
    local glyph      = new_glyph(font,echar)
    local head       = glyph
    if delta > 0 then
        local count = ceil(delta/mwidth)
        local kern  = (delta - count * mwidth) / (count+1)
        for i=1,count do
            local k = new_kern(kern)
            local g = new_glyph(font,mchar)
            setlink(k,head)
            setlink(g,k)
            head = g
        end
        local k = new_kern(kern)
        setlink(k,head)
        head = k
    end
    local g = new_glyph(font,bchar)
    setlink(g,head)
    head = g
    local height = mchardata.height
    local list   = new_hlist(head)
    local kern   = new_kern(height-v)
    list = setlink(kern,list)
    local list = new_vlist(kern)
    insertnodeafter(n,n,list)
end

local function initialize(tfmdata)
    local goodies = tfmdata.goodies
    if goodies then
        local resources = tfmdata.resources
        local ruledata  = { }
        for i=1,#goodies do
            local mathematics = goodies[i].mathematics
            if mathematics then
                local rules = mathematics.rules
                if rules then
                    for tag, name in next, rules do
                        ruledata[tag] = name
                    end
                end
            end
        end
        if next(ruledata) then
            local characters = tfmdata.characters
            local unicodes   = resources.unicodes
            if characters and unicodes then
                local mathruleactions = resources.mathruleactions
                if not mathruleactions then
                    mathruleactions = { }
                    resources.mathruleactions = mathruleactions
                end
                --
                local mchar = unicodes[ruledata["radical.extender"] or false]
                local echar = unicodes[ruledata["radical.end"]      or false]
                if mchar and echar then
                    mathruleactions.radicalaction = function(n,h,v,font)
                        mathradicalaction(n,h,v,font,mchar,echar)
                    end
                end
                --
                local bchar = unicodes[ruledata["hrule.begin"]    or false]
                local mchar = unicodes[ruledata["hrule.extender"] or false]
                local echar = unicodes[ruledata["hrule.end"]      or false]
                if bchar and mchar and echar then
                    mathruleactions.hruleaction = function(n,h,v,font)
                        mathhruleaction(n,h,v,font,bchar,mchar,echar)
                    end
                end
                -- not that nice but we need to register it at the tex end
             -- context.enablemathrules("\\fontclass")
            end
        end
    end
end

registerotffeature {
    name         = "mathrules",
    description  = "check math rules",
    default      = true,
    initializers = {
        base = initialize,
        node = initialize,
    }
}
