%D \module
%D   [       file=m-punk,
%D        version=2008.04.15,
%D          title=\CONTEXT\ Modules,
%D       subtitle=Punk Support,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright=PRAGMA]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\ifx\luaversion\undefined \endinput \fi

% At some point the font generation code will move into the
% ConTeXt MkIV kernel.

\startluacode
do
    local concat   = table.concat
    local chardata = characters.data
    local fontdata = fonts.ids

    fonts.mp = fonts.mp or { }

    fonts.mp.version = fonts.mp.version or 1.11
    fonts.mp.inline  = true
    fonts.mp.cache   = containers.define("fonts", "mp", fonts.mp.version, true)

    metapost.characters = metapost.characters or { }

-- todo: use table share as in otf

    local characters, descriptions = { }, { }
    local factor, l, n, w, h, d, total, variants = 100, { }, 0, 0, 0, 0, 0, 0, true

    -- A next version of mplib will provide the tfm font information which
    -- gives better glyph dimensions, plus additional kerning information.

    local flusher = {
        startfigure = function(chrnum,llx,lly,urx,ury)
            l, n = { }, chrnum
            w, h, d = urx - llx, ury, -lly
            total = total + 1
            inline = fonts.mp.inline
        end,
        flushfigure = function(t)
            for i=1, #t do
                l[#l+1] = t[i]
            end
        end,
        stopfigure = function()
            local cd = chardata[n]
            if inline then
                descriptions[n] = {
                --  unicode = n,
                    name = cd and cd.adobename,
                    width = w*100,
                    height = h*100,
                    depth = d*100,
                    boundingbox = { 0, -d, w, h },
                }
                characters[n] = {
                    commands = { -- todo: xforms, should happen in backend
                        { "special", "pdf: " .. concat(l," ") },
                    }
                }
            else
                descriptions[n] = {
                --  unicode = n,
                    name = cd and cd.adobename,
                    width = w*100,
                    height = h*100,
                    depth = d*100,
                    boundingbox = { 0, -d, w, h },
                }
                characters[n] = {
                    commands = {
                        { "image", { stream = concat(l," "), bbox = { 0, -d*65536, w*65536, h*65536 } } },
                    }
                }
            end
        end
    }

    metapost.characters.instances = metapost.characters.instances or 10

    function metapost.characters.process(mpxformat, name, instances, scalefactor)
        statistics.starttiming(metapost.characters)
        scalefactor = scalefactor or 1
        instances = instances or metapost.characters.instances or 10
        local fontname = file.removesuffix(file.basename(name))
        local hash  = file.robustname(string.format("%s %05i %03i", fontname, scalefactor*1000, instances))
        local lists = containers.read(fonts.mp.cache(), hash)
        if not lists then
            statistics.starttiming(flusher)
            -- we can use a format per font
            local data = io.loaddata(resolvers.find_file(name))
            metapost.reset(mpxformat)
            metapost.set_outer_color(2) -- no outer color and no reset either
            lists = { }
            for i=1,instances do
                list = { }
                characters, descriptions = { }, { }
                metapost.process(
                    mpxformat,
                    {
                        "randomseed := " .. i*10 .. ";",
                        "scale_factor := " .. scalefactor .. " ;",
                        data
                    },
                    false,
                    flusher
                )
                lists[i] = {
                    designsize = 655360,
                    name = string.format("%s-%03i",hash,i),
                    parameters = {
                        slant         =   0,
                        space         = 333   * scalefactor,
                        space_stretch = 166.5 * scalefactor,
                        space_shrink  = 111   * scalefactor,
                        x_height      = 431   * scalefactor,
                        quad          =1000   * scalefactor,
                        extra_space   =   0
                    },
                    ["type"] = "virtual",
                    characters = characters,
                    descriptions = descriptions,
                --  embedding = "subset",
                --  mkiv:
                    spacer = "space",
                    unit = 1000,
                    shared = { },
                    unique = { },
                }
            end
            metapost.reset(mpxformat) -- saves memory
            lists = containers.write(fonts.mp.cache(), hash, lists)
            statistics.stoptiming(flusher)
        end
        variants = variants + #lists
        statistics.stoptiming(metapost.characters)
        return lists
    end

    function fonts.vf.aux.combine.commands.metafont(g,v)
        local size = g.specification.size
        local data = metapost.characters.process(v[2],v[3],v[4],size/655360)
        local list, t = { }, { }
        for d=1,#data do
            t = data[d]
            t = fonts.tfm.scale(t, -1000)
            local id = font.nextid()
            t.fonts = { { id = id } }
            fontdata[id] = t
            fonts.vf.aux.compose_characters(t)
            list[d] = font.define(t)
        end
        for k, v in pairs(t) do
            g[k] = v -- kind of replace, when not present, make nil
        end
        g.virtualized = true
        g.variants = list
    end

    fonts.define.methods.install( "punk", {
        { "metafont", "mfplain", "punkfont.mp", 10 },
    } )

    cases.actions[99] = function(current)
        local used = fontdata[current.font].variants
        if used then
            local f = math.random(1,#used)
            current.font = used[f]
            return current, true
        else
            return current, false
        end
    end

    metapost.characters.flusher = flusher

    statistics.register("metapost font generation", function()
        local time = statistics.elapsedtime(flusher)
        if total > 0 then
            return string.format("%i glyphs, %.3f seconds runtime, %i glyphs/second", total, time, total/time)
        else
            return string.format("%i glyphs, %.3f seconds runtime", total, time)
        end
    end)

    statistics.register("metapost font loading",function()
        local time = statistics.elapsedtime(metapost.characters)
        if variants > 0 then
            return string.format("%.3f seconds, %i instances, %0.3f instances/second", time, variants, variants/time)
        else
            return string.format("%.3f seconds, %i instances", time, variants)
        end
    end)

end
\stopluacode

\unexpanded\def\EnableRandomPunk {\setcharactercasing[99]}
\unexpanded\def\RandomPunk       {\groupedcommand\EnableRandomPunk\donothing}
\unexpanded\def\StartRandomPunk  {\begingroup\EnableRandomPunk}
\unexpanded\def\StopRandomPunk   {\endgroup}

\starttypescript [serif] [punk] [default]
    \setups[font:fallback:serif] % no style variants yet
    \definefontsynonym [Serif] [demo@punk]
\stoptypescript

\starttypescript [punk]
    \definetypeface [punk] [rm] [serif] [punk] [default]
\stoptypescript

\endinput

\usetypescript[punk]

\setupbodyfont[punk,14pt]

\starttext
    \definedfont[demo@punk at 10pt]hello world\par
    \definedfont[demo@punk at 12pt]hello world\par
    \definedfont[demo@punk at 16pt]hello world\par
    \definedfont[demo@punk at 20pt]hello world\par
\stoptext

