%D \module
%D   [       file=m-crappyspec,
%D        version=2022.07.01, % about
%D          title=\CONTEXT\ Extra Modules,
%D       subtitle=Meeting weird demands,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

%D This module is for Benjamin Buchmuller who has to meet rather weird demands with
%D respect to the number of characters per line. So, it's not about quality but more
%D sort of previous century typewriter specification.
%D
%D \setmainparbuilder[crappyspec]

\startluacode

local type = type

local nuts            = nodes.nuts

local glyph_code      = nodes.nodecodes.glyph
local hlist_code      = nodes.nodecodes.hlist
local vlist_code      = nodes.nodecodes.vlist
local disc_code       = nodes.nodecodes.disc

local line_code       = nodes.listcodes.line

local traverselist    = nuts.traverselist
local traversecontent = nuts.traversecontent

local flushlist       = nuts.flushlist
local copylist        = nuts.copylist
local getcharspec     = nuts.getcharspec
local getreplace      = nuts.getreplace
local getparstate     = nuts.getparstate
local getlist         = nuts.getlist
local getwidth        = nuts.getwidth
local setwidth        = nuts.setwidth

local texset          = tex.set
local texsetdimen     = tex.setdimen
local texgetdimen     = tex.getdimen
local texgetcount     = tex.getcount
local texlinebreak    = tex.linebreak

local characters      = fonts.hashes.characters

local function countglyphs(h)
    local c = 0
    if h then
        for n, t, s in traversecontent(h) do
            if t == glyph_code then
                if true then
                    local g, f = getcharspec(n)
                    local d = characters[f][g] -- maybe cache
                    local u = d and d.unicode
                    if type(u) == "table" then
                       c = c + #u
                    else
                       c = c + 1
                    end
                else
                    c = c + 1
                end
            elseif t == disc_code then
                c = c + countglyphs(getreplace(n),c)
            elseif t == hlist_code or s == vlist_code then
                c = c + countglyphs(getlist(h),c)
            else
                -- leader
            end
        end
    end
    return c
end

local function overflow(h, max)
    for n, t, s in traverselist(h) do
        if t == hlist_code and s == line_code then
            local c = countglyphs(getlist(n))
            if c > max then
                return c, getwidth(n)
            end
        end
    end
    return false
end

function builders.paragraphs.constructors.methods.crappyspec(head)
    -- todo: prevent useless calls
    local p = getparstate(head)
    if p then
        local hsize = p.hsize
        local max   = texgetcount("crappyspeccount")
        local step  = texgetdimen("crappyspecstep")
        if step <= 65536 then
            step = 65536
        end
        while true do
            local n = copylist(head)
            local l, d = texlinebreak(n, { hsize = hsize })
            local c, w = overflow(l,max)
            if w and hsize > 10*step then -- safeguard
                flushlist(l)
                hsize = hsize - step
            else
                texsetdimen("crappyspecdimen",hsize)
                flushlist(head)
                head = l
                local hsize = p.hsize
                for n, t, s in traverselist(head) do
                    if t == hlist_code and s == line_code then
                     -- todo: add delta to rightskip or so
                        setwidth(n,hsize)
                    end
                end
                texset("prevdepth",d.prevdepth)
                break
            end
        end
    end
    return head
end
\stopluacode

\newinteger  \crappyspeccount
\newdimension\crappyspecstep   \crappyspecstep \onepoint
\newdimension\crappyspecdimen

\defineparbuilder [crappyspec] % implemented in the builder namespace
\defineparbuilder [default]    % implemented in the builder namespace

\continueifinputfile{m-crappyspec.mkxl}

\setupbodyfont
  [pagella]

\setuptolerance
  [verytolerant,stretch]

\showmakeup
  [line]

\setmainparbuilder
  [crappyspec]

\dontcomplain

\protected\def\CrappyTraced
  {\par \strut \rlap \bgroup\infofont
     (\enspace
         max   = \the\crappyspeccount \quad
         step  = \the\crappyspecstep  \quad
         hsize = \the\hsize           \quad
         used  = \the\crappyspecdimen \enspace
     )
   \egroup \par}

\starttext

\crappyspeccount60  \samplefile{tufte} \CrappyTraced \par
\crappyspeccount40  \samplefile{tufte} \CrappyTraced \par
%   \crappyspecstep 2pt \samplefile{tufte} \CrappyTraced \par

\samplefile{tufte} \CrappyTraced
\startitemize
    \startitem
        \samplefile{tufte} \CrappyTraced
    \stopitem
    \startitem
        \samplefile{ward}  \CrappyTraced
    \stopitem
\stopitemize

\startnarrower[6*left,right]
    \samplefile{tufte} \CrappyTraced
\stopnarrower

\starthanging [distance=4em,n=2] {test}
    \samplefile{tufte} \CrappyTraced
\stophanging

\page % \stoptext

\setuppapersize[landscape,letter]

\samplefile{knuth} \CrappyTraced
\startitemize[width=5em]
    \startitem
        \samplefile{knuth} \CrappyTraced
    \stopitem
    \startitem
        {\smallcaps \darkblue \samplefile{knuth}} \CrappyTraced
    \stopitem
\stopitemize
\crappyspeccount60
\startitemize[width=5em]
    \startitem
        \samplefile{knuth} \CrappyTraced
    \stopitem
    \startitem
        {\smallcaps \darkgreen \samplefile{knuth}} \CrappyTraced
    \stopitem
\stopitemize

\page

\crappyspecstep  1pt \crappyspeccount 60 % 5.5
\crappyspecstep  2pt \crappyspeccount 60 % 3.2
\crappyspecstep  5pt \crappyspeccount 60 % 1.4
\crappyspecstep 10pt \crappyspeccount 60 % 0.9

\crappyspecstep  1pt \crappyspeccount 80 % 0.8
\crappyspecstep  2pt \crappyspeccount 80 % 0.8
\crappyspecstep  5pt \crappyspeccount 80 % 0.8
\crappyspecstep 10pt \crappyspeccount 80 % 0.6

% \crappyspecstep  1pt \crappyspeccount 120 % 0.25
% \crappyspecstep  2pt \crappyspeccount 120 % 0.25
% \crappyspecstep  5pt \crappyspeccount 120 % 0.25

% \testfeatureonce{100}{\samplefile{tufte}\par\CrappyTraced} % \page \elapsedtime

\stoptext
