%D \module
%D   [       file=page-lin,
%D        version=2007.11.29,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Line Numbering,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

% generic or not ... maybe not bother too much and simplify to mkiv only

\writestatus{loading}{ConTeXt Core Macros / Line Numbering}

\unprotect

% low level interface

\registerctxluafile{page-lin}{1.001}

\definesystemattribute[line-number]
\definesystemattribute[line-reference]

\appendtoksonce\doresetattribute{line-number}\to\everyforgetall
\appendtoksonce\dosetattribute{display-math}{1}\to\everybeforedisplayformula

\newcount\linenumber
\newbox  \linenumberscratchbox
\newcount\linenumberchunk
\newcount\linerefcounter

\newconditional\tracelinenumbering

\def\mkaddtextlinenumbers#1#2#3% box col max
  {\bgroup
   \def\skipnumber{\hbox{}}%
   \ifcase#3\relax
     \let\makenumber\mkskiplinenumber
   \or
     \let\makenumber\mkleftlinenumber
   \else\ifcase#2\relax
     \let\makenumber\mkskiplinenumber
   \or
     \let\makenumber\mkleftlinenumber
   \else
     \let\makenumber\mkrightlinenumber
   \fi\fi
   \mkprocesstextlinenumbers{#1}%
   \egroup}

\def\mkprocesstextlinenumbers#1%
  {\setbox\linenumberscratchbox\vbox{\forgetall\offinterlineskip\ctxlua{nodes.lines.boxed.stage_one(\number#1)}}%
   \ctxlua{nodes.lines.boxed.stage_two(\number#1,\number\linenumberscratchbox)}}% can move to lua code

% id nr shift width leftskip

\def\mkskiplinenumber #1#2#3#4#5{}
\def\mkleftlinenumber #1#2#3#4#5{\hbox{\llap{#2\quad\hskip#3\scaledpoint}}}
\def\mkrightlinenumber#1#2#3#4#5{\hbox{\rlap{\hskip#4\scaledpoint\hskip#3\scaledpoint\quad#2}}}

\def\makenumber#1#2{\hbox{\llap{#1\quad\hskip#2\scaledpoint}}\endgraf}%

\newconditional\boxcontentneedsprocessing

\def\mkdoprocesspagecontents  #1{\mkaddtextlinenumbers{#1}\plusone      \plusone}
\def\mkdoprocessboxcontents   #1{\mkaddtextlinenumbers{#1}\plusone      \plusone}
\def\mkdoprocesscolumncontents#1{\mkaddtextlinenumbers{#1}\currentcolumn\nofcolumns}

\def\mkcurrentstart{0}
\def\mkcurrentstep {1}

\def\mkdefinetextlinenumbering#1%
  {\begingroup
   \scratchcounter\ctxlua{tex.sprint(nodes.lines.boxed.register({start=\mkcurrentstart,step=\mkcurrentstep,tag="#1"}))}%
   \setxvalue{ln:c:#1}{\number\scratchcounter}%
   \endgroup}

\def\mkstarttextlinenumbering#1#2%
  {\globallet\mkprocesspagecontents  \mkdoprocesspagecontents
   \globallet\mkprocesscolumncontents\mkdoprocesscolumncontents
   \global\settrue\boxcontentneedsprocessing % see core-rul.mkiv
   \ifcase#2\relax
     % continue
   \or
     \mkdefinetextlinenumbering{#1}% restart
   \fi
   \dosetattribute{line-number}{\getvalue{ln:c:#1}}}

\def\mksetuptextlinenumbering#1%
  {\ctxlua{nodes.lines.boxed.setup(\getvalue{ln:c:#1},{start=\mkcurrentstart,step=\mkcurrentstep,tag="#1"})}}

\def\mkstoptextlinenumbering
  {\doresetattribute{line-number}}

\def\mksomelinereference#1#2#3%
  {\dontleavehmode\begingroup
   \global\advance\linerefcounter\plusone
   \dosetattribute{line-reference}\linerefcounter
   % this will change and is troublesome anyway
   #3\textreference[#2]{\noexpand\ctxlua{tex.sprint(nodes.lines.number(\the\linerefcounter))}}%
   \endgroup}

\def\mkstartlinereference#1{\mksomelinereference{#1}{lr:b:#1}{}\ignorespaces}
\def\mkstoplinereference #1{\removeunwantedspaces\mksomelinereference{#1}{lr:e:#1}{}}

\def\mklinestartreference#1[#2]{\in{#1}[lr:b:#2]} % not interfaced
\def\mklinestopreference #1[#2]{\in{#1}[lr:e:#2]} % not interfaced

% high level interface

\newif\ifnumberinglines
\newif\iftypesettinglines

\let\currentlinenumbering\empty

\chardef\linenumbermode      = 1 % 0=continue, 1=restart
\chardef\linenumberlocation  = 1 % 0=middle, 1=left, 2=right, 3=inner, 4=outer, 5=text
\chardef\linenumberalignment = 5 % 0=middle, 1=left, 2=right, 5=auto

\newevery \beforeeverylinenumbering \relax
\newevery \aftereverylinenumbering  \relax
\newevery \everylinenumber          \relax

\newdimen\linenumberwidth
\newdimen\linenumberdistance

\def\definelinenumbering
  {\dosingleempty\dodefinelinenumbering}

\def\dodefinelinenumbering[#1]%
  {\def\currentlinenumbering{#1}%
   \mkdefinetextlinenumbering\currentlinenumbering}

\def\setuplinenumbering
  {\dodoubleempty\dosetuplinenumbering}

\def\dosetuplinenumbering[#1][#2]%
  {\ifsecondargument
     \def\currentlinenumbering{#1}%
     \getparameters[\??rn#1][#2]%
   \else
     \let\currentlinenumbering\empty
     \getparameters[\??rn][#1]%
   \fi
   \mksetuptextlinenumbering\currentlinenumbering}

\def\linenumberparameter#1%
  {\csname\??rn\ifcsname\??rn\currentlinenumbering#1\endcsname\currentlinenumbering\fi#1\endcsname}

\def\linenumberattributes
  {\doattributes{\??rn\ifcsname\??rn\currentlinenumbering\c!style\endcsname\currentlinenumbering\fi}}

\definelinenumbering

\setuplinenumbering
  [\c!conversion=\v!numbers,
   \c!start=1,
   \c!step=1,
   \c!continue=v!no,
   \c!location=\v!left,
   \c!style=,
   \c!color=,
   \c!width=2em,
   \c!left=,
   \c!right=,
   \c!command=,
   \c!distance=\zeropoint,
   \c!align=\v!auto]

\def\startlinenumbering
  {\dodoubleempty\dostartlinenumbering}

% no intermediate changes in values, define a class, otherwise each range
% would need a number

\def\mkcurrentstart{\linenumberparameter\c!start}
\def\mkcurrentstep {\linenumberparameter\c!step }

% todo: text

\expandafter\chardef\csname\??rn:l:\v!middle  \endcsname \zerocount
\expandafter\chardef\csname\??rn:l:\v!left    \endcsname \plusone
\expandafter\chardef\csname\??rn:l:\v!margin  \endcsname \plusone
\expandafter\chardef\csname\??rn:l:\v!inmargin\endcsname \plusone
\expandafter\chardef\csname\??rn:l:\v!inleft  \endcsname \plusone
\expandafter\chardef\csname\??rn:l:\v!right   \endcsname \plustwo
\expandafter\chardef\csname\??rn:l:\v!inright \endcsname \plustwo
\expandafter\chardef\csname\??rn:l:\v!inner   \endcsname \plusthree
\expandafter\chardef\csname\??rn:l:\v!outer   \endcsname \plusfour
\expandafter\chardef\csname\??rn:l:\v!text    \endcsname \plusfive

\expandafter\chardef\csname\??rn:a:\v!middle    \endcsname \zerocount
\expandafter\chardef\csname\??rn:a:\v!right     \endcsname \plusone
\expandafter\chardef\csname\??rn:a:\v!flushleft \endcsname \plusone
\expandafter\chardef\csname\??rn:a:\v!left      \endcsname \plustwo
\expandafter\chardef\csname\??rn:a:\v!flushright\endcsname \plustwo
\expandafter\chardef\csname\??rn:a:\v!auto      \endcsname \plusfive

\def\dostartlinenumbering[#1][#2]% todo: c!continue
  {\begingroup
   \chardef\linenumbermode\plusone
   \ifsecondargument
     \def\currentlinenumbering{#1}%
     \doifnumberelse{#2}% downward compatible
       {\setvalue{\??rn#1\c!start}{#2}}%
       {\doif{#2}\v!continue{\chardef\linenumbermode\zerocount}}%
   \else\iffirstargument
     \doifnumberelse{#1}% downward compatible
       {\let\currentlinenumbering\empty
        \setvalue{\??rn\c!start}{#1}}%
       {\doifelse{#1}\v!continue
          {\let\currentlinenumbering\empty
           \chardef\linenumbermode\zerocount}
          {\def\currentlinenumbering{#1}}}%
   \fi\fi
   \doifelse{\linenumberparameter\c!continue}\v!yes
     {\chardef\linenumbermode\zerocount}%
   \numberinglinestrue
   \the\beforeeverylinenumbering
   \mkstarttextlinenumbering\currentlinenumbering\linenumbermode}

\def\stoplinenumbering
  {\mkstoptextlinenumbering
   \the\aftereverylinenumbering
   \endgroup}

% number placement

\let\mkskiplinenumber \gobblefivearguments

\def\mkdoinnerlinenumber{\doifoddpageelse\mkdoleftlinenumber\mkdorightlinenumber}
\def\mkdoouterlinenumber{\doifoddpageelse\mkdorightlinenumber\mkdoleftlinenumber}

\def\mkleftlinenumber
  {\ifcase\linenumberlocation
     \expandafter\mkdoleftlinenumber
   \or
     \expandafter\mkdoleftlinenumber
   \or
     \expandafter\mkdoleftlinenumber
   \or
     \expandafter\mkdoinnerlinenumber
   \or
     \expandafter\mkdoouterlinenumber
   \fi}

\def\mkrightlinenumber
  {\ifcase\linenumberlocation
     \expandafter\mkdorightlinenumber
   \or
     \expandafter\mkdorightlinenumber
   \or
     \expandafter\mkdorightlinenumber
   \or
     \expandafter\mkdoouterlinenumber
   \or
     \expandafter\mkdoinnerlinenumber
   \fi}

\def\mkaddtextlinenumbers#1#2#3% box col max
  {\bgroup
   \def\skipnumber{\hbox{}}%
   \ifcase#3\relax
     \let\makenumber\mkskiplinenumber
   \or
     \let\makenumber\mkleftlinenumber
   \else\ifcase#2\relax
     \let\makenumber\mkskiplinenumber
   \or
     \let\makenumber\mkdoleftlinenumber
     \ifcase\linenumberlocation\or
       \chardef\linenumberlocation\plusone
     \or
       \chardef\linenumberlocation\plustwo
     \or
       \chardef\linenumberlocation\plusone
     \or
       \chardef\linenumberlocation\plusone
     \or
       \chardef\linenumberlocation\plusone
     \fi
   \else
     \let\makenumber\mkdorightlinenumber
     \ifcase\linenumberlocation\or
       \chardef\linenumberlocation\plustwo
     \or
       \chardef\linenumberlocation\plusone
     \or
       \chardef\linenumberlocation\plustwo
     \or
       \chardef\linenumberlocation\plustwo
     \fi
   \fi\fi
   \mkprocesstextlinenumbers{#1}%
   \egroup}

\def\mkdoleftlinenumber #1#2#3#4#5%
  {\hbox{\llap{\dosomelinenumber{#1}{2}{#2}{#5}\hskip#3\scaledpoint}}}
\def\mkdorightlinenumber#1#2#3#4#5%
  {\hbox{\rlap{\hskip#4\scaledpoint\hskip#3\scaledpoint\dosomelinenumber{#1}{1}{#2}{#5}}}}

\def\dosomelinenumber#1#2#3#4% tag 1=left|2=right linenumber leftskip
  {\begingroup
   \def\currentlinenumbering{#1}%
   \chardef\linenumberlocation \executeifdefined{\??rn:l:\linenumberparameter\c!location}\plusone  % left
   \chardef\linenumberalignment\executeifdefined{\??rn:a:\linenumberparameter\c!align   }\plusfive % auto
   \doifelse{\linenumberparameter\c!width}\v!margin
     {\linenumberwidth\leftmarginwidth}
     {\linenumberwidth\linenumberparameter\c!width}%
   \linenumberdistance\linenumberparameter\c!distance\relax
   \ifcase#2\relax\or\hskip\linenumberdistance\fi\relax
   \ifnum\linenumberlocation=\plusfive
     \scratchdimen\dimexpr#4\scaledpoint-\linenumberdistance\relax
     \chardef\linenumberlocation\plusone
   \else
     \scratchdimen\zeropoint
   \fi
   \ifcase\linenumberalignment
     \chardef\linenumberlocation\zerocount % middle
   \or
     \chardef\linenumberlocation\plusone % left
   \or
     \chardef\linenumberlocation\plustwo % right
   \fi
   \ifconditional\tracelinenumbering\ruledhbox\else\hbox\fi to \linenumberwidth
     {\ifcase\linenumberlocation
        \hss % middle
      \or
        % left
      \or
        \hss % right
      \or
        \doifoddpageelse\relax\hss % inner
      \or
        \doifoddpageelse\hss\relax % outer
      \fi
      \linenumberattributes\c!style\c!color
        {\linenumberparameter\c!command
           {\linenumberparameter\c!left
            \convertnumber{\linenumberparameter\c!conversion}{#3}%
            \linenumberparameter\c!right}}%
      \ifcase\linenumberlocation
        \hss % middle
      \or
        \hss % left
      \or
        % right
      \or
        \doifoddpageelse\hss\relax % inner
      \or
        \doifoddpageelse\relax\hss % outer
      \fi}%
   \ifcase#2\relax\or\or\hskip\linenumberdistance\fi\relax
   \hskip-\scratchdimen
   \the\everylinenumber
   \endgroup}

% left right inner outer

% align: \alignedline\@@rnalign\v!right{\box0\hskip\@@rndistance}

% referencing

\def\someline [#1]{\mkstartlinereference{#1}\mkstoplinereference{#1}}
\def\startline[#1]{\mkstartlinereference{#1}}
\def\stopline [#1]{\mkstoplinereference {#1}}

\def\mkshowstartlinereference#1%
  {\ifconditional\tracelinenumbering
     \setbox\scratchbox\hbox{\llap
       {\vrule\!!width\onepoint\!!depth\strutdp\!!height.8\strutht\raise.85\strutht\hbox{\llap{\tt\txx#1}}}}%
     \smashbox\scratchbox\box\scratchbox
   \fi}
\def\mkshowstoplinereference#1%
  {\ifconditional\tracelinenumbering
     \setbox\scratchbox\hbox{\rlap
       {\raise.85\strutht\hbox{\rlap{\tt\txx#1}}\vrule\!!width\onepoint\!!depth\strutdp\!!height.8\strutht}}%
     \smashbox\scratchbox\box\scratchbox
   \fi}

\def\mkstartlinereference#1{\mksomelinereference{#1}{lr:b:#1}{\mkshowstartlinereference{#1}}\ignorespaces}
\def\mkstoplinereference #1{\removeunwantedspaces\mksomelinereference{#1}{lr:e:#1}{\mkshowstoplinereference{#1}}}

% eventually we will do this in lua

\def\doifelsesamelinereference#1#2#3%
  {\doifreferencefoundelse{lr:b:#1}
     {\let\fline\currenttextreference
      \doifreferencefoundelse{lr:e:#1}
        {\let\tline\currenttextreference
         \ifx\fline\tline#2\else#3\fi}
        {\unknownreference{#1}#2}}
     {\unknownreference{#1}#2}}

\def\inline#1[#2]%
  {\doifelsenothing{#1}
     {\doifelsesamelinereference{#2}
        {\in{\leftlabeltext\v!line}{\rightlabeltext\v!line}[lr:b:#2]}
        {\in{\leftlabeltext\v!lines}{}[lr:b:#2]--\in{}{\rightlabeltext\v!lines}[lr:e:#2]}}
     {\doifelsesamelinereference{#2}
        {\in{#1}[lr:b:#2]}
        {\in{#1}[lr:b:#2]--\in[lr:e:#2]}}}

\protect \endinput

\iffalse % \iftrue

    \appendtoks\ctxlua{nodes.lines.flowed.prepare()}\to\everyshipout
    \appendtoks\ctxlua{nodes.lines.flowed.cleanup()}\to\everybye

    \def\mkstarttextlinenumbering#1#2%
      {\ctxlua{nodes.lines.flowed.prepare("#1")}%
       \dosetattribute{line-number}{#2}}

    \def\mkstoptextlinenumbering
      {\doresetattribute{line-number}}

    \def\mkmaketextlinenumber#1#2%
      {\hbox \bgroup
       \ifcase#2\relax
         %
       \or
         \llap{#1\quad\hskip#2\scaledpoint}%
       \else
         \rlap{\hskip\textwidth\quad#1\hskip#2\scaledpoint}%
       \fi
       \egroup}

    \ctxlua{
        nodes.lines.scratchbox = \number\linenumberscratchbox ;
    %   callback.register('vpack_filter', nodes.lines.flowed.apply)
        callback.register('post_linebreak_filter', nodes.lines.flowed.apply)
    }

\fi
