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

local type, next = type, next
local gmatch = string.gmatch

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

-- goodies=name,colorscheme=,featureset=
--
-- goodies=auto

-- goodies

fonts.goodies      = fonts.goodies      or { }
fonts.goodies.data = fonts.goodies.data or { }
fonts.goodies.list = fonts.goodies.list or { }

local data = fonts.goodies.data
local list = fonts.goodies.list

function fonts.goodies.report(what,trace,goodies)
    if trace_goodies or trace then
        local whatever = goodies[what]
        if whatever then
            logs.report("fonts", "goodie '%s' found in '%s'",what,goodies.name)
        end
    end
end

local function getgoodies(filename) -- maybe a merge is better
    local goodies = data[filename] -- we assume no suffix is given
    if goodies ~= nil then
        -- found or tagged unfound
    elseif type(filename) == "string" then
        local fullname = resolvers.find_file(file.addsuffix(filename,"lfg")) or "" -- prefered suffix
        if fullname == "" then
            fullname = resolvers.find_file(file.addsuffix(filename,"lua")) or "" -- fallback suffix
        end
        if fullname == "" then
            logs.report("fonts", "goodie file '%s.lfg' is not found",filename)
            data[filename] = false -- signal for not found
        else
            goodies = dofile(fullname) or false
            if not goodies then
                logs.report("fonts", "goodie file '%s' is invalid",fullname)
                return nil
            elseif trace_goodies then
                logs.report("fonts", "goodie file '%s' is loaded",fullname)
            end
            goodies.name = goodies.name or "no name"
            for name, fnc in next, list do
                fnc(goodies)
            end
            goodies.initialized = true
            data[filename] = goodies
        end
    end
    return goodies
end

function fonts.goodies.register(name,fnc)
    list[name] = fnc
end

fonts.goodies.get = getgoodies

-- register goodies file

local preset_context = fonts.define.specify.preset_context

function fonts.initializers.common.goodies(tfmdata,value)
    local goodies = tfmdata.goodies or { } -- future versions might store goodies in the cached instance
    for filename in gmatch(value,"[^, ]+") do
        -- we need to check for duplicates
        local ok = getgoodies(filename)
        if ok then
            goodies[#goodies+1] = ok
        end
    end
    tfmdata.goodies = goodies -- shared ?
end

-- featuresets

local function flattened(t,tt)
    -- first set value dominates
    local tt = tt or { }
    for i=1,#t do
        local ti = t[i]
        if type(ti) == "table" then
            flattened(ti,tt)
        elseif tt[ti] == nil then
            tt[ti] = true
        end
    end
    for k, v in next, t do
        if type(k) ~= "number" then
            if type(v) == "table" then
                flattened(v,tt)
            elseif tt[k] == nil then
                tt[k] = v
            end
        end
    end
    return tt
end

fonts.flattened_features = flattened

function fonts.goodies.prepare_features(goodies,name,set)
    if set then
        local ff = fonts.flattened_features(set)
        local fullname = goodies.name .. "::" .. name
        local n, s = preset_context(fullname,"",ff)
        goodies.featuresets[name] = s -- set
        if trace_goodies then
            logs.report("fonts", "feature set '%s' gets number %s and name '%s'",name,n,fullname)
        end
        return n
    end
end

local function initialize(goodies,tfmdata)
    local featuresets = goodies.featuresets
    local goodiesname = goodies.name
    if featuresets then
        if trace_goodies then
            logs.report("fonts", "checking featuresets in '%s'",goodies.name)
        end
        for name, set in next, featuresets do
            fonts.goodies.prepare_features(goodies,name,set)
        end
    end
end

fonts.goodies.register("featureset",initialize)

function fonts.initializers.common.featureset(tfmdata,set)
    local goodies = tfmdata.goodies -- shared ?
    if goodies then
        local features = tfmdata.shared.features
        local what
        for i=1,#goodies do
            -- last one counts
            local g = goodies[i]
            what = (g.featuresets and g.featuresets[set]) or what
        end
        if what then
            for feature, value in next, what do
                if features[feature] == nil then
                    features[feature] = value
                end
            end
            tfmdata.mode = features.mode or tfmdata.mode
        end
    end
end

-- colorschemes

fonts.goodies.colorschemes      = fonts.goodies.colorschemes      or { }
fonts.goodies.colorschemes.data = fonts.goodies.colorschemes.data or { }

local colorschemes = fonts.goodies.colorschemes

function fonts.initializers.common.colorscheme(tfmdata,scheme)
    if type(scheme) == "string" then
        local goodies = tfmdata.goodies
        -- todo : check for already defined in shared
        if goodies then
            local what
            for i=1,#goodies do
                -- last one counts
                local g = goodies[i]
                what = (g.colorschemes and g.colorschemes[scheme]) or what
            end
            if what then
                -- this is font bound but we can share them if needed
                -- just as we could hash the conversions (per font)
                local hash, reverse = tfmdata.luatex.unicodes, { }
                for i=1,#what do
                    local w = what[i]
                    for j=1,#w do
                        local name = w[j]
                        local unicode = hash[name]
                        if unicode then
                            reverse[unicode] = i
                        end
                    end
                end
                tfmdata.colorscheme = reverse
                return
            end
        end
    end
    tfmdata.colorscheme = false
end

local fontdata      = fonts.ids
local fcs           = fonts.color.set
local has_attribute = node.has_attribute
local traverse_id   = node.traverse_id
local a_colorscheme = attributes.private('colorscheme')
local glyph         = node.id("glyph")

function fonts.goodies.colorschemes.coloring(head)
    local lastfont, lastscheme
    for n in traverse_id(glyph,head) do
        local a = has_attribute(n,a_colorscheme)
        if a then
            local f = n.font
            if f ~= lastfont then
                lastscheme, lastfont = fontdata[f].colorscheme, f
            end
            if lastscheme then
                local sc = lastscheme[n.char]
                if sc then
                    fcs(n,"colorscheme:"..a..":"..sc) -- slow
                end
            end
        end
    end
end

function fonts.goodies.colorschemes.enable()
    tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring")
    function fonts.goodies.colorschemes.enable() end
end

-- installation (collected to keep the overview)

fonts.otf.tables.features['goodies']     = 'Goodies on top of built in features'
fonts.otf.tables.features['featurset']   = 'Goodie Feature Set'
fonts.otf.tables.features['colorscheme'] = 'Goodie Color Scheme'

fonts.otf.features.register('goodies')
fonts.otf.features.register('featureset')
fonts.otf.features.register('colorscheme')

table.insert(fonts.triggers, 1, "goodies")
table.insert(fonts.triggers, 2, "featureset") -- insert after
table.insert(fonts.triggers,    "colorscheme")

fonts.initializers.base.otf.goodies     = fonts.initializers.common.goodies
fonts.initializers.node.otf.goodies     = fonts.initializers.common.goodies

fonts.initializers.base.otf.featureset  = fonts.initializers.common.featureset
fonts.initializers.node.otf.featureset  = fonts.initializers.common.featureset

fonts.initializers.base.otf.colorscheme = fonts.initializers.common.colorscheme
fonts.initializers.node.otf.colorscheme = fonts.initializers.common.colorscheme

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

local function initialize(goodies)
    local mathgoodies = goodies.mathematics
    local virtuals = mathgoodies and mathgoodies.virtuals
    local mapfiles = mathgoodies and mathgoodies.mapfiles
    local maplines = mathgoodies and mathgoodies.maplines
    if virtuals then
        for name, specification in next, virtuals do
            mathematics.make_font(name,specification)
        end
    end
    if mapfiles then
        for i=1,#mapfiles do
            fonts.map.loadfile(mapfiles[i]) -- todo: backend function
        end
    end
    if maplines then
        for i=1,#maplines do
            fonts.map.loadline(maplines[i]) -- todo: backend function
        end
    end
end

fonts.goodies.register("mathematics", initialize)

-- The following file (husayni.lfg) is the experimental setup that we used
-- for Idris font. For the moment we don't store this in the cache and quite
-- probably these files sit in one of the paths:
--
-- tex/context/fonts/goodies
-- tex/fonts/goodies/context
-- tex/fonts/data/foundry/collection
--
-- see lfg files in distribution
