%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 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.

% generic or not ... maybe not bother too much and simplify to mkiv only
% get rid of \mk* (left over from experimental times)
%
% to be redone (was experiment) .. can be hooked into margin code

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

\unprotect

% todo: save settings
%
% low level interface
%
% we should use normal counters but then we need to sync settings

% some line
%
% \startlocallinenumbering
% some source code 1\par
% some source code 2\par
% some source code 3\par
% \stoplocallinenumbering
%
% some line

\registerctxluafile{page-lin}{1.001}

\definesystemattribute[linenumber]   [public]
\definesystemattribute[linereference][public]

\appendtoksonce
    \attribute\linenumberattribute\attributeunsetvalue
\to \everyforgetall

\newcount   \linenumber % not used
\newbox     \b_page_lines_scratch
\newcount   \c_page_lines_reference
\newconstant\c_page_lines_nesting

\newconditional\tracelinenumbering

% id nr shift width leftskip dir

\installcorenamespace{linenumberinginstance}

\let\makelinenumber\gobblesevenarguments % used at lua end

\newconditional\page_postprocessors_needed_box

\unexpanded\def\page_postprocessors_linenumbers_page   #1{\page_lines_add_numbers_to_box{#1}\plusone      \plusone   \zerocount}
\unexpanded\def\page_postprocessors_linenumbers_box    #1{\page_lines_add_numbers_to_box{#1}\plusone      \plusone   \zerocount}
\unexpanded\def\page_postprocessors_linenumbers_deepbox#1{\page_lines_add_numbers_to_box{#1}\plusone      \plusone   \plusone  }
\unexpanded\def\page_postprocessors_linenumbers_column #1{\page_lines_add_numbers_to_box{#1}\currentcolumn\nofcolumns\zerocount}

\def\page_lines_parameters_regular
  {continue = "\ifnum\c_page_lines_mode=\zerocount\v!yes\else\v!no\fi",
   start    = \number\linenumberingparameter\c!start,
   step     = \number\linenumberingparameter\c!step,
   method   = "\linenumberingparameter\c!method",
   tag      = "\currentlinenumbering"}

\def\page_lines_parameters_update
  {continue = "\ifnum\c_page_lines_mode=\zerocount\v!yes\else\v!no\fi"}

\def\page_lines_start_define
  {\setxvalue{\??linenumberinginstance\currentlinenumbering}{\ctxcommand{registerlinenumbering({\page_lines_parameters_regular})}}}

\def\page_lines_start_update
  {\ctxcommand{setuplinenumbering(\csname\??linenumberinginstance\currentlinenumbering\endcsname,{\page_lines_parameters_update})}}

\def\page_lines_setup
  {\ifcsname \??linenumberinginstance\currentlinenumbering\endcsname
     \ctxcommand{setuplinenumbering(\csname\??linenumberinginstance\currentlinenumbering\endcsname,{\page_lines_parameters_regular})}%
   \fi}

% we could make this a bit more efficient by putting the end reference
% in the same table as the start one but why make things complex ...

\let\dofinishlinereference\dofinishfullreference % at lua end

\unexpanded\def\page_lines_some_reference#1#2#3%
  {\dontleavehmode\begingroup
   \global\advance\c_page_lines_reference\plusone
   \attribute\linereferenceattribute\c_page_lines_reference
   #3%
   % for the moment we use a simple system i.e. no prefixes etc .. todo: store as number
   \normalexpanded{\strc_references_set_named_reference{line}{#2}{conversion=\linenumberingparameter\c!conversion}{\the\c_page_lines_reference}}% kind labels userdata text
   \endgroup}

\def\page_lines_reference_start#1{\page_lines_some_reference{#1}{lr:b:#1}{}\ignorespaces}
\def\page_lines_reference_stop #1{\removeunwantedspaces\page_lines_some_reference{#1}{lr:e:#1}{}}

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

\newif\ifnumberinglines   % will change
\newif\iftypesettinglines % will change

\installcorenamespace{linenumbering}

\installcommandhandler \??linenumbering {linenumbering} \??linenumbering

\setnewconstant\c_page_lines_mode     \plusone  % 0=continue, 1=restart
\setnewconstant\c_page_lines_location \plusone  % 0=middle, 1=left, 2=right, 3=inner, 4=outer, 5=text, 6=begin, 7=end
\setnewconstant\c_page_lines_alignment\plusfive % 0=middle, 1=left, 2=right, 5=auto

\newdimen\d_page_lines_width
\newdimen\d_page_lines_distance

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

\appendtoks
   \page_lines_setup
\to \everysetuplinenumbering

\appendtoks
   \page_lines_start_define
\to \everydefinelinenumbering

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

\definelinenumbering
  []

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

% todo: text

\installcorenamespace{linennumberinglocation}
\installcorenamespace{linennumberingalternative}

\expandafter\let\csname\??linennumberinglocation\v!middle       \endcsname \zerocount
\expandafter\let\csname\??linennumberinglocation\v!left         \endcsname \plusone
\expandafter\let\csname\??linennumberinglocation\v!margin       \endcsname \plusone
\expandafter\let\csname\??linennumberinglocation\v!inmargin     \endcsname \plusone
\expandafter\let\csname\??linennumberinglocation\v!inleft       \endcsname \plusone
\expandafter\let\csname\??linennumberinglocation\v!right        \endcsname \plustwo
\expandafter\let\csname\??linennumberinglocation\v!inright      \endcsname \plustwo
\expandafter\let\csname\??linennumberinglocation\v!inner        \endcsname \plusthree
\expandafter\let\csname\??linennumberinglocation\v!outer        \endcsname \plusfour
\expandafter\let\csname\??linennumberinglocation\v!text         \endcsname \plusfive
\expandafter\let\csname\??linennumberinglocation\v!begin        \endcsname \plussix
\expandafter\let\csname\??linennumberinglocation\v!end          \endcsname \plusseven

\expandafter\let\csname\??linennumberingalternative\v!middle    \endcsname \zerocount
\expandafter\let\csname\??linennumberingalternative\v!right     \endcsname \plusone
\expandafter\let\csname\??linennumberingalternative\v!flushleft \endcsname \plusone
\expandafter\let\csname\??linennumberingalternative\v!left      \endcsname \plustwo
\expandafter\let\csname\??linennumberingalternative\v!flushright\endcsname \plustwo
\expandafter\let\csname\??linennumberingalternative\v!auto      \endcsname \plusfive

% \startlinenumbering[<startvalue>|continue|settings|name]
% \startlinenumbering[name][<startvalue>|continue|settings]

\unexpanded\def\startlinenumbering
  {\dodoubleempty\page_lines_start}

\def\page_lines_start % we stay downward compatible
  {\begingroup
   \ifsecondargument
     \expandafter\page_lines_start_two
   \else\iffirstargument
     \doubleexpandafter\page_lines_start_one
   \else
     \doubleexpandafter\page_lines_start_zero
   \fi\fi}

\def\page_lines_start_zero[#1][#2]%
  {\edef\m_argument{\linenumberingparameter\c!continue}%
   \ifx\m_argument\v!continue
     \c_page_lines_mode\zerocount
   \else
     \c_page_lines_mode\plusone
   \fi
   \page_lines_start_followup}

\def\page_lines_start_one[#1][#2]% [continue|<number>|settings] % historic
  {\edef\m_argument{#1}%
   \ifx\m_argument\v!continue
     \c_page_lines_mode\zerocount
     \let\currentlinenumbering\empty
   \else
     \c_page_lines_mode\plusone
     \ifx\m_argument\v!empty
       \let\currentlinenumbering\empty
     \else
       \doifassignmentelse{#1}
         {\let\currentlinenumbering\empty
          \setupcurrentlinenumbering[#1]}
         {\doifnumberelse\m_argument
            {\let\currentlinenumbering\empty
             \letlinenumberingparameter\c!start\m_argument}
            {\let\currentlinenumbering\m_argument}}%
     \fi
     \edef\p_continue{\linenumberingparameter\c!continue}%
     \ifx\p_continue\v!yes
       \c_page_lines_mode\zerocount
     \fi
   \fi
   \page_lines_start_followup}

\def\page_lines_start_two[#1][#2]% [tag][continue|<number>|settings]
  {\edef\currentlinenumbering{#1}%
   \edef\m_argument{#2}%
   \ifx\m_argument\v!continue
     \c_page_lines_mode\zerocount
   \else
     \c_page_lines_mode\plusone
     \ifx\m_argument\v!empty \else
       \doifassignmentelse{#2}
         {\setupcurrentlinenumbering[#2]}
         {\doifnumber\m_argument
            {\letlinenumberingparameter\c!start\m_argument}}%
     \fi
     \edef\p_continue{\linenumberingparameter\c!continue}%
     \ifx\p_continue\v!yes
       \c_page_lines_mode\zerocount
     \fi
   \fi
   \page_lines_start_followup}

\def\page_lines_start_followup
  {\numberinglinestrue
   \the\beforeeverylinenumbering
   \globallet\page_postprocessors_page  \page_postprocessors_linenumbers_page
   \globallet\page_postprocessors_column\page_postprocessors_linenumbers_column
   \global\settrue\page_postprocessors_needed_box % see core-rul.mkiv
   \ifcase\c_page_lines_mode\relax
     \page_lines_start_update % continue
   \or
     \page_lines_start_define % only when assignment
   \fi
   \attribute\linenumberattribute\getvalue{\??linenumberinginstance\currentlinenumbering}\relax}

\unexpanded\def\stoplinenumbering
  {\attribute\linenumberattribute\attributeunsetvalue
   \the\aftereverylinenumbering
   \endgroup}

% number placement .. will change into (the new) margin code

\def\page_lines_number_inner_indeed{\doifoddpageelse\page_lines_number_left_indeed\page_lines_number_right_indeed}
\def\page_lines_number_outer_indeed{\doifoddpageelse\page_lines_number_right_indeed\page_lines_number_left_indeed}

\def\page_lines_number_left
  {\ifcase\c_page_lines_location
     \expandafter\page_lines_number_left_indeed
   \or
     \expandafter\page_lines_number_left_indeed
   \or
     \expandafter\page_lines_number_left_indeed
   \or
     \expandafter\page_lines_number_inner_indeed
   \or
     \expandafter\page_lines_number_outer_indeed
   \or
     \expandafter\page_lines_number_text_indeed
   \or
     \expandafter\page_lines_number_begin_indeed
   \or
     \expandafter\page_lines_number_end_indeed
   \fi}

\def\page_lines_number_right
  {\ifcase\c_page_lines_location
     \expandafter\page_lines_number_right_indeed
   \or
     \expandafter\page_lines_number_right_indeed
   \or
     \expandafter\page_lines_number_right_indeed
   \or
     \expandafter\page_lines_number_outer_indeed
   \or
     \expandafter\page_lines_number_inner_indeed
   \or
     \expandafter\page_lines_number_text_indeed
   \or
     \expandafter\page_lines_number_end_indeed
   \or
     \expandafter\page_lines_number_begin_indeed
   \fi}

\newconditional\c_page_lines_fake_number
\newconstant   \b_page_lines_number
\newconstant   \c_page_lines_column
\newconstant   \c_page_lines_last_column

\def\page_lines_add_numbers_to_box#1#2#3#4% box col max nesting
  {\bgroup
   \b_page_lines_number     #1\relax
   \c_page_lines_column     #2\relax
   \c_page_lines_last_column#3\relax
   \c_page_lines_nesting    #4\relax
   \fullrestoreglobalbodyfont
   \let\makelinenumber\page_lines_make_number % used at lua end
   \setbox\b_page_lines_scratch\vbox
     {\forgetall
      \offinterlineskip
      \ctxcommand{linenumbersstageone(\number\b_page_lines_number,\ifcase\c_page_lines_nesting false\else true\fi)}}%
   \ctxcommand{linenumbersstagetwo(\number\b_page_lines_number,\number\b_page_lines_scratch)}% can move to lua code
   \egroup}

\let\page_lines_make_number_indeed\relax

\def\page_lines_make_number#1#2%
  {\edef\currentlinenumbering{#1}%
   \ifcase#2\relax
     \settrue \c_page_lines_fake_number
   \else
     \setfalse\c_page_lines_fake_number
   \fi
   \c_page_lines_location \executeifdefined{\??linennumberinglocation   \linenumberingparameter\c!location}\plusone \relax % left
   \c_page_lines_alignment\executeifdefined{\??linennumberingalternative\linenumberingparameter\c!align   }\plusfive\relax % auto
   \ifcase\c_page_lines_last_column\relax
     \settrue \c_page_lines_fake_number
   \or
    % one column
     \ifcase\c_page_lines_location
       \settrue \c_page_lines_fake_number
       \let\page_lines_make_number_indeed\page_lines_number_fake_indeed
     \or
       \let\page_lines_make_number_indeed\page_lines_number_left
     \or
       \let\page_lines_make_number_indeed\page_lines_number_right
     \or % inner
       \let\page_lines_make_number_indeed\page_lines_number_inner_indeed
     \or % outer
       \let\page_lines_make_number_indeed\page_lines_number_outer_indeed
     \or % text
       \let\page_lines_make_number_indeed\page_lines_number_text_indeed
     \or
       \let\page_lines_make_number_indeed\page_lines_number_begin_indeed
     \or
       \let\page_lines_make_number_indeed\page_lines_number_end_indeed
     \fi
   \else\ifcase\c_page_lines_column\relax
     \settrue \c_page_lines_fake_number
   \or
     \let\page_lines_make_number_indeed\page_lines_number_left
     \ifcase\c_page_lines_location\or
       \c_page_lines_location\plusone
     \or
       \c_page_lines_location\plustwo
     \else
       \c_page_lines_location\plusone
     \or
       \c_page_lines_location\plusone
     \or
       \c_page_lines_location\plusone
     \or
       \c_page_lines_location\plusone % todo
     \or
       \c_page_lines_location\plusone % todo
     \fi
   \else
     \let\page_lines_make_number_indeed\page_lines_number_right
     \ifcase\c_page_lines_location\or
       \c_page_lines_location\plustwo
     \or
       \c_page_lines_location\plusone
     \or
       \c_page_lines_location\plustwo
     \or
       \c_page_lines_location\plustwo
     \or
       \c_page_lines_location\plustwo % todo
     \or
       \c_page_lines_location\plustwo % todo
     \fi
   \fi\fi
   \page_lines_make_number_indeed{#1}}

\let\page_lines_number_fake_indeed\gobblesixarguments % needs checking

\def\page_lines_number_text_indeed#1#2#3#4#5#6% beware, one needs so compensate for this in the \hsize
  {\hbox{\page_lines_number_construct{#1}{2}{#2}{#5}\hskip#3\scaledpoint}}

\def\page_lines_number_left_indeed#1#2#3#4#5#6%
  {\naturalhbox to \zeropoint
     {\ifcase\istltdir#6\else \hskip-#4\scaledpoint \fi
      \llap{\page_lines_number_construct{#1}{2}{#2}{#5}\kern#3\scaledpoint}}}

\def\page_lines_number_right_indeed#1#2#3#4#5#6%
  {\naturalhbox to \zeropoint
     {\ifcase\istltdir#6\else \hskip-#4\scaledpoint \fi
      \rlap{\hskip\dimexpr#4\scaledpoint+#3\scaledpoint\relax\page_lines_number_construct{#1}{1}{#2}{#5}}}}

\def\page_lines_number_begin_indeed#1#2#3#4#5#6%
  {\ifcase\istltdir#6\relax
     \c_page_lines_location\plusone
     \expandafter\page_lines_number_left_indeed
   \else
     \c_page_lines_location\plustwo
     \expandafter\page_lines_number_left_indeed
   \fi{#1}{#2}{#3}{#4}{#5}{#6}}

\def\page_lines_number_end_indeed#1#2#3#4#5#6%
  {\ifcase\istltdir#6\relax
     \c_page_lines_location\plustwo
     \expandafter\page_lines_number_left_indeed
   \else
     \c_page_lines_location\plusone
     \expandafter\page_lines_number_left_indeed
   \fi{#1}{#2}{#3}{#4}{#5}{#6}}

\def\page_lines_number_construct#1#2#3#4% tag 1=left|2=right linenumber leftskip
  {\begingroup
   \def\currentlinenumbering{#1}%
   \def\linenumber{#3}% unsafe
   \doifelse{\linenumberingparameter\c!width}\v!margin
     {\d_page_lines_width\leftmarginwidth}
     {\d_page_lines_width\linenumberingparameter\c!width}%
   \d_page_lines_distance\linenumberingparameter\c!distance\relax
   \ifcase#2\relax\or\hskip\d_page_lines_distance\fi\relax
   \ifnum\c_page_lines_location=\plusfive
     \scratchdimen\dimexpr#4\scaledpoint-\d_page_lines_distance\relax
     \c_page_lines_location\plusone
   \else
     \scratchdimen\zeropoint
   \fi
   \ifcase\c_page_lines_alignment
     \c_page_lines_location\zerocount % middle
   \or
     \c_page_lines_location\plusone % left
   \or
     \c_page_lines_location\plustwo % right
   \fi
   \ifconditional\tracelinenumbering\ruledhbox\else\hbox\fi to \d_page_lines_width
     {\ifcase\c_page_lines_location
        \hss % middle
      \or
        % left
      \or
        \hss % right
      \or
        \doifoddpageelse\relax\hss % inner
      \or
        \doifoddpageelse\hss\relax % outer
      \fi
      \ifconditional\c_page_lines_fake_number
        % we need to reserve space
      \else
        \uselinenumberingstyleandcolor\c!style\c!color
        \linenumberingparameter\c!command
          {\linenumberingparameter\c!left
           \convertnumber{\linenumberingparameter\c!conversion}{#3}%
           \linenumberingparameter\c!right}%
      \fi
      \ifcase\c_page_lines_location
        \hss % middle
      \or
        \hss % left
      \or
        % right
      \or
        \doifoddpageelse\hss\relax % inner
      \or
        \doifoddpageelse\relax\hss % outer
      \fi}%
   \ifcase#2\relax
     \hskip-\scratchdimen
   \or
     \hskip-\scratchdimen
   \or
     \hskip\dimexpr\d_page_lines_distance-\scratchdimen\relax
   \fi
   \relax
   \the\everylinenumber
   \endgroup}

% referencing

\unexpanded\def\someline [#1]{\page_lines_reference_start{#1}\page_lines_reference_stop{#1}} % was just a def
\unexpanded\def\startline[#1]{\page_lines_reference_start{#1}}
\unexpanded\def\stopline [#1]{\page_lines_reference_stop {#1}}

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

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

\def\page_lines_reference_start#1{\page_lines_some_reference{#1}{lr:b:#1}{\page_lines_reference_show_start{#1}}\ignorespaces}
\def\page_lines_reference_stop #1{\removeunwantedspaces\page_lines_some_reference{#1}{lr:e:#1}{\page_lines_reference_show_stop{#1}}}

% eventually we will do this in lua

\def\currentreferencelinenumber{\ctxcommand{filterreference("linenumber")}}

\let\m_page_lines_from\empty
\let\m_page_lines_to  \empty

\unexpanded\def\doifelsesamelinereference#1#2#3%
  {\doifreferencefoundelse{lr:b:#1}
     {\edef\m_page_lines_from{\currentreferencelinenumber}%
      \doifreferencefoundelse{lr:e:#1}
        {\edef\m_page_lines_to{\currentreferencelinenumber}%
         %[\m_page_lines_from,\m_page_lines_to]
         \ifx\m_page_lines_from\m_page_lines_to#2\else#3\fi}
        {#2}}
     {#2}}

\unexpanded\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]}}}

\unexpanded\def\inlinerange[#1]%
  {\doifelsesamelinereference{#1}
     {\in[lr:b:#1]}
     {\in[lr:b:#1]\endash\in[lr:e:#1]}}

\protect \endinput
