%D \module
%D   [       file=typo-lbx,
%D        version=2021.10.10,
%D          title=\CONTEXT\ Typesetting Macros,
%D       subtitle=Local Boxes,
%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.

\writestatus{loading}{ConTeXt Typesetting Macros / Local Boxes}

% This time the usual musical timestamp is: New Scary Goldings ft. John Scofield
% MonoNeon & Louis Cole (late October 2021) (plus the playlist in loop mode) and
% further improved after watching Cory Wong's LIVE IN MPLS (9 FEB 2019) concert.

% maybe: \iflocalbox 0/1/2

\registerctxluafile{typo-lbx}{autosuffix}

%D The advantage is that it's deal with in the par builder which has some advantages
%D but when used in complex situation there can be side effects. Therefore is should
%D be considered a somewhat low level mechanism on top of which one can write more
%D mechanisms that provide more rendering control. Also, keep in mind that combining
%D many instances at the same time add some complizity. Therefore, for the moment it's
%D a playground.

\unprotect

% Local boxes are a real mess and although we already mnade it behave better it's
% still a trouble maker.

% \def\typo_localboxes_reset
%   {\localleftbox  {}% no index here, works grouped
%    \localrightbox {}%
%    \localmiddlebox{}}

\aliased\let\typo_localboxes_reset\resetlocalboxes

\appendtoks
    \typo_localboxes_reset
\to \everyforgetall

\definesystemattribute[localboxesmark][public]

%D We used to pass arguments but there might be many so ...

\newbox\localboxcontentbox

\installcorenamespace{localboxes}
\installcorenamespace{localboxesattribute}
\installcorenamespace{localboxesnamespace}
\installcorenamespace{localboxeslocations}
\installcorenamespace{localboxesresetters}

\installcommandhandler \??localboxes {localboxes} \??localboxes

\setuplocalboxes
  [\c!command=\localboxcontent,
   \c!width=\zeropoint,
   \c!location=\v!left,
   \c!distance=\zeropoint]

\newinteger\c_typo_localboxes
\newinteger\c_typo_localboxes_index

\appendtoks
    \global\advanceby\c_typo_localboxes\plusone
    \global\expandafter\integerdef\csname\??localboxesattribute\currentlocalboxes\endcsname\c_typo_localboxes
    \gletcsname\??localboxesnamespace\the\c_typo_localboxes\endcsname\currentlocalboxes
\to \everydefinelocalboxes

%D The optional argument forces setting the \quote {whole} paragraph properties (which is needed
%D when the assignment happens after e.g.\ \type {\everypar} but is also meant for the first
%D line.

%D Todo: reserve index 1 for this:

\def\typo_paragraphs_l#1{\localleftbox  \ifcstok{#1}\v!global par\fi}
\def\typo_paragraphs_r#1{\localrightbox \ifcstok{#1}\v!global par\fi}
\def\typo_paragraphs_m#1{\localmiddlebox\ifcstok{#1}\v!global par\fi}

\permanent\tolerant\protected\def\leftparbox  [#1]{\typo_paragraphs_l{#1}\bgroup\enforced\let\leftparbox \relax\let\next}
\permanent\tolerant\protected\def\rightparbox [#1]{\typo_paragraphs_r{#1}\bgroup\enforced\let\rightparbox\relax\let\next}
\permanent\tolerant\protected\def\middleparbox[#1]{\typo_paragraphs_m{#1}\bgroup\enforced\let\rightparbox\relax\let\next}

% called back:

\permanent\protected\def\localboxcontent
  {\box\localboxcontentbox}

\permanent\protected\def\localboxcommand
  {\ifcsname\??localboxesnamespace\number\localboxindex\endcsname
    %\cdef\currentlocalboxes{\lastnamedcs}%
     \expandafter\let\expandafter\currentlocalboxes\lastnamedcs
     \setbox\localboxcontentbox\hbox
       {\uselocalboxesstyleandcolor\c!style\c!color % sometimes redundant
        \localboxesparameter\c!command}%
   \fi}

%D We don't group because we set the local boxes. Also, watch out: by injecting the
%D existing local box we create nested ones. This is handled in the callback but if
%D really needed one can do something like (do we need a primitive?):

\mutable\lettonothing\currentlocalboxeslocation

\def\typo_localboxes_localbox
  {\ifx\currentlocalboxeslocation\v!right
     \localrightbox
   \orelse\ifx\currentlocalboxeslocation\v!left
     \localleftbox
   \else
     \localmiddlebox
   \fi}

\def\typo_localboxes_zero#1#2#3%
  {\ifcsname\??localboxesattribute#2\endcsname
     \c_typo_localboxes_index\lastnamedcs
     \cdef\currentlocalboxes{#2}%
     \edef\currentlocalboxeslocation{#1}%
     \typo_localboxes_localbox
        \s!index \c_typo_localboxes_index
        \bgroup
          \hpack
            \ifcstok{\localboxesparameter\c!repeat}\v!no
              \s!attr \localboxesmarkattribute \localboxmarkonce\c_typo_localboxes_index\relax
            \fi
            \s!to \zeropoint
          \bgroup
            \uselocalboxesstyleandcolor\c!style\c!color
            \hbox{#3}%
            \hss
          \egroup
        \egroup
   \fi}

\def\typo_localboxes_asis#1#2#3%
  {\ifcsname\??localboxesattribute#2\endcsname
     \c_typo_localboxes_index\lastnamedcs
     \cdef\currentlocalboxes{#2}%
     \edef\currentlocalboxeslocation{#1}%
     \typo_localboxes_localbox
        \s!index \c_typo_localboxes_index
        % todo: use \s!retain so that we work over a group
        \bgroup
          \hpack
            \ifcstok{\localboxesparameter\c!repeat}\v!no
              \s!attr \localboxesmarkattribute \localboxmarkonce\c_typo_localboxes_index\relax
            \fi
            % todo: use width if dimension, use distance if given
          \bgroup
            \uselocalboxesstyleandcolor\c!style\c!color
            \hbox{#3}% no \hss
          \egroup
        \egroup
   \fi}

\defcsname\??localboxeslocations\v!left     \endcsname{\typo_localboxes_zero\v!left  }
\defcsname\??localboxeslocations\v!right    \endcsname{\typo_localboxes_zero\v!right }
\defcsname\??localboxeslocations\v!lefttext \endcsname{\typo_localboxes_asis\v!left  }
\defcsname\??localboxeslocations\v!righttext\endcsname{\typo_localboxes_asis\v!right }
\defcsname\??localboxeslocations\v!middle   \endcsname{\typo_localboxes_asis\v!middle}

\letcsname\??localboxesresetters\v!left     \endcsname\v!left
\letcsname\??localboxesresetters\v!right    \endcsname\v!right
\letcsname\??localboxesresetters\v!lefttext \endcsname\v!left
\letcsname\??localboxesresetters\v!righttext\endcsname\v!right
\letcsname\??localboxesresetters\v!middle   \endcsname\v!middle

\permanent\tolerant\protected\def\resetlocalbox[#1]%
  {\ifcsname\??localboxesattribute#1\endcsname
     \c_typo_localboxes_index\lastnamedcs
     \cdef\currentlocalboxes{#1}%
     \ifcsname\??localboxesresetters\localboxesparameter\c!location\endcsname
       \edef\currentlocalboxeslocation{\lastnamedcs}%
       \typo_localboxes_localbox \s!index \c_typo_localboxes_index {}%
     \fi
   \fi}

\def\typo_localboxes_box#1%
  {\dowithnextboxcontent
     {\cdef\currentlocalboxes{#1}%
      \uselocalboxesstyleandcolor\c!style\c!color}
     {\ifcsname\??localboxeslocations\namedlocalboxesparameter{#1}\c!location\endcsname
        \expandafter\lastnamedcs
      \else
        \csname\??localboxeslocations\v!left\expandafter\endcsname
      \fi{#1}{\unhbox\nextbox}}}

\permanent\tolerant\protected\def\localbox[#1]%
  {\typo_localboxes_box{#1}\hbox}

\permanent\tolerant\protected\def\startlocalbox[#1]%
  {\dowithnextbox
     {\ifcsname\??localboxeslocations\namedlocalboxesparameter{#1}\c!location\endcsname
         \expandafter\lastnamedcs
       \else
         \csname\??localboxeslocations\v!left\expandafter\endcsname
       \fi{#1}{\unhbox\nextbox}}%
     \hbox\bgroup
     \cdef\currentlocalboxes{#1}%
     \uselocalboxesstyleandcolor\c!style\c!color
     \enforced\def\stoplocalbox{\removeunwantedspaces\egroup}%
     \ignorespaces}

\aliased\let\stoplocalbox\donothing

\permanent\tolerant\protected\def\startlocalboxrange[#1]%
  {\globalpushmacro\stoplocalboxrange
   \ifcsname\??localboxeslocations\namedlocalboxesparameter{#1}\c!location\endcsname
     \lastnamedcs{#1}{}%
   \fi}

\permanent\protected\def\stoplocalboxrange
  {\globalpopmacro\stoplocalboxrange}%

% using left and right with left lagging behind:
%
% \permanent\protected\def\localmarginlefttext#1%
%   {\setbox\localboxcontentbox\hpack
%      {\unhbox\localboxcontentbox
%       \setbox\localboxcontentbox\lastbox
%       \unhbox\localboxcontentbox}%
%    \hpack xoffset -\dimexpr
%       #1
%      +\localboxprogress
%      +\localboxleftoffset
%      +\wd\localboxcontentbox
%      +\localboxesparameter\c!distance
%    \relax{\box\localboxcontentbox}}
%
% \permanent\protected\def\localmarginrighttext#1%
%   {\hpack xoffset \dimexpr
%       #1
%      +\localboxrightoffset
%      +\localboxlocalwidth
%      -\localboxprogress
%      +\localboxesparameter\c!distance
%    \relax{\box\localboxcontentbox}}

% using middle:

\permanent\protected\def\localmarginlefttext#1%
  {\ifzeropt\localboxesparameter\c!width\relax
     % a but ugly hack ... for now
     \setbox\localboxcontentbox\hpack
       {\unhbox\localboxcontentbox
        \setbox\localboxcontentbox\lastbox
        \unhbox\localboxcontentbox}%
   \fi
   \hpack \s!xoffset {%
     -#1%
     -\localboxprogress
     -\wd\localboxcontentbox
     -(\localboxesparameter\c!distance)%
   }{\box\localboxcontentbox}}

\permanent\protected\def\localmarginrighttext#1%
  {\hpack \s!xoffset {%
      #1
     +\localboxlinewidth
     -\localboxprogress
     +{\localboxesparameter\c!distance}%
   }{\box\localboxcontentbox}}

% todo: use generic one above

\permanent\protected\def\localmargintext[#1]#2%
  {\dontleavehmode
   \ifcsname\??localboxesattribute#1\endcsname
     \c_typo_localboxes_index\lastnamedcs
     \cdef\currentlocalboxes{#1}%
     \edef\currentlocalboxeslocation{\localboxesparameter\c!location}%
     \ifx\currentlocalboxeslocation\v!right\localrightbox\orelse\ifx\currentlocalboxeslocation\v!left\localleftbox\else\localmiddlebox\fi
        \s!index \c_typo_localboxes_index
        \bgroup
          \hpack
            \ifcstok{\localboxesparameter\c!repeat}\v!no
              \s!attr \localboxesmarkattribute \localboxmarkonce\c_typo_localboxes_index\relax
            \fi
            to \zeropoint
          \bgroup
            \uselocalboxesstyleandcolor\c!style\c!color
            \hbox{#2}%
            \hss
          \egroup
        \egroup
   \fi}

\definelocalboxes
  [\v!leftmargin]
  [\c!command=\localmarginlefttext\zeropoint,
   \c!repeat=\v!no,
   \c!distance=\leftmargindistance,
 % \c!location=\v!left]
   \c!location=\v!middle]

\definelocalboxes
  [\v!rightmargin]
  [\c!command=\localmarginrighttext\zeropoint,
   \c!repeat=\v!no,
   \c!distance=\rightmargindistance,
 % \c!location=\v!right]
   \c!location=\v!middle]

\definelocalboxes
  [\v!leftedge]
  [\c!command=\localmarginlefttext\leftmargintotal,
   \c!repeat=\v!no,
   \c!distance=\leftedgedistance,
 % \c!location=\v!left]
   \c!location=\v!middle]

\definelocalboxes
  [\v!rightedge]
  [\c!command=\localmarginrighttext\rightmargintotal,
   \c!repeat=\v!no,
   \c!distance=\rightedgedistance,
 % \c!location=\v!right]
   \c!location=\v!middle]

%D Here is an example of usage:

%D \starttyping
%D \definelocalboxes
%D   [linenumber]
%D   [command=\LeftNumber,location=left,width=3em,style=\bs,color=darkred]
%D
%D \definelocalboxes
%D   [linenumbertwo] [linenumber]
%D   [command=\RightNumber,location=right,width=6em,style=\bf,color=darkgreen]
%D
%D \definelocalboxes
%D   [linetext]
%D   [command=\LeftText,location=lefttext,style=\bs,color=darkblue]
%D
%D \definelocalboxes
%D   [linetexttwo] [linetext]
%D   [command=\RightText,location=righttext,style=\bf,color=darkgray]
%D
%D % \def\LineNumberL{\the\localboxlinenumber}
%D % \def\LineNumberR{\the\localboxlinenumber}
%D
%D % \newinteger\MyLineNumberL
%D % \newinteger\MyLineNumberR
%D % \def\LineNumberL{\global\advanceby\MyLineNumberL\plusone\the\MyLineNumberL}
%D % \def\LineNumberR{\global\advanceby\MyLineNumberR\plusone\the\MyLineNumberR}
%D
%D \definecounter[MyLineNumberL]
%D \definecounter[MyLineNumberR]
%D
%D \setupcounter[MyLineNumberL][numberconversion=characters]
%D \setupcounter[MyLineNumberR][numberconversion=romannumerals]
%D
%D \def\LineNumberL{\incrementcounter[MyLineNumberL]\convertedcounter[MyLineNumberL]}
%D \def\LineNumberR{\incrementcounter[MyLineNumberR]\convertedcounter[MyLineNumberR]}
%D
%D \protected\def\LeftNumber {\hbox to \localboxesparameter{width}{\strut(\LineNumberL\hss)}}
%D \protected\def\RightNumber{\hbox to \localboxesparameter{width}{\strut(\hss\LineNumberR)}}
%D
%D % \protected\def\LeftNumber {\hbox to \localboxesparameter{width}{\strut\box\localboxcontentbox\hss)}}
%D % \protected\def\RightNumber{\hbox to \localboxesparameter{width}{\strut(\hss\box\localboxcontentbox)}}
%D
%D \protected\def\LeftText {\localboxcontentbox\quad}
%D \protected\def\RightText{\quad\localboxcontentbox}
%D
%D \start
%D     \localbox[linenumber]{}%
%D     \localbox[linenumbertwo]{}%
%D     \localbox[linetext]{L}%
%D     \startlocalbox[linetexttwo]
%D         R
%D     \stoplocalbox
%D     \dorecurse{100}{
%D         \samplefile{tufte}
%D         \par
%D     }
%D \stop
%D \stoptyping

\protect \endinput

