%D \module
%D   [       file=buff-ini, % was core-buf % blocks are moved to core-blk
%D        version=2000.01.05,
%D          title=\CONTEXT\ Buffer Macros,
%D       subtitle=Buffers,
%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 Buffer Macros / Buffers}

\unprotect

% Helpers:

\chardef\buffernestmode\plusone % 0: not nested, 1: startbuffer nested, 2: all buffers nested

\edefconvertedargument\emptybufferline{ }

\ifx\tmpblocks\undefined \newwrite\tmpblocks \fi

\newif\iftmpblockstarted

\long\def\flushbufferline#1%
  {\iftmpblockstarted
     \ifsegmentatebuffer
       \ifemptybufferline
         \immediate\write\tmpblocks{\string\stopbufferparagraph }%
         \immediate\write\tmpblocks{\string\startbufferparagraph}%
       \else
         \immediate\write\tmpblocks{#1}%
       \fi
     \else
       \immediate\write\tmpblocks{#1}%
     \fi
   \else
     \doifsomething{#1}
       {\tmpblockstartedtrue
        \immediate\write\tmpblocks{\string#1}}%
   \fi}

\long\def\processnextbufferlineA#1%
  {\relax % checken waarom eerdere macro dit nodig heeft / supp-mps run
   \defconvertedargument\next{#1 }%
   \doifinstringelse{\delcharacter\letterpercent}{\delcharacter\next}
     {\secondoftwoarguments}
     {\doifincsnameelse\endofblock\next
        {\ifnum\nestedbufferlevel=\zerocount
           \expandafter\firstoftwoarguments
         \else
           \decrement\nestedbufferlevel\relax
           \expandafter\secondoftwoarguments
         \fi}
        {\doifincsnameelse\beginofblock\next
           {\increment\nestedbufferlevel\relax
            \secondoftwoarguments}
           {\secondoftwoarguments}}}}

\long\def\processnextbufferlineB#1% #2#3%
  {\defconvertedargument\next{#1 }%
   \ifx\next\emptybufferline
     \ifsegmentatebuffer \emptybufferlinetrue \fi
     \expandafter\secondoftwoarguments% #3%
   \else
     \emptybufferlinefalse
     \doifinstringelse\endofblock\next
       {\expandafter\firstoftwoarguments }% #2}
       {\expandafter\secondoftwoarguments}% #3}%
   \fi}

\bgroup
\obeylines
\long\gdef\copybufferline#1
  {\processnextbufferline{#1}\closebufferfile{\flushbufferline{#1}\copybufferline}}
\egroup

\newif\ifsegmentatebuffer
\newif\ifemptybufferline

\def\currentbuffer{\jobname}

\def\setcurrentbuffer#1%
  {\doifelsenothing{#1}{\edef\currentbuffer{\jobname}}{\edef\currentbuffer{#1}}}

\def\resetbuffer
  {\dosingleempty\doresetbuffer}

\def\doresetbuffer[#1]%
  {\begingroup
   \setcurrentbuffer{#1}%
   \unlinkfile{\TEXbufferfile\currentbuffer}%
   \endgroup}

\def\dostartbuffer
  {\bgroup
   \obeylines % nodig, anders gaat 't fout als direct \starttable (bv)
   \doquadrupleempty\dodostartbuffer}

\def\dodostartbuffer[#1][#2][#3][#4]% upward compatible
  {\iffourthargument
     \def\next{\dododostartbuffer{#1}{#2}{#3}{#4}}%
   \else
     \def\next{\dododostartbuffer  {}{#1}{#2}{#3}}%
   \fi
   \next}

\def\dododostartbuffer#1#2#3#4%
  {%\showmessage\m!systems{15}{#2}%
   \doifelsevalue{\??bu#1\c!paragraph}\v!yes
     {\segmentatebuffertrue} % todo in mkiv
     {\doifnumberelse{\getvalue{\??bu#1\c!paragraph}}\segmentatebuffertrue\segmentatebufferfalse}%
   \doifvalue{\??bu#1\c!local}\v!yes
     {\chardef\buffernestmode\plustwo}% permit nesting
   \setcurrentbuffer{#2}%
   \doifelsenothing{#4}
     {\letbeundefined{\e!stop\v!buffer}% % \let\stopbuffer=\relax   % \undefined
      \edefconvertedargument\beginofblock{\e!start\v!buffer}%
      \edefconvertedargument\endofblock  {\e!stop \v!buffer}%
      \ifcase\buffernestmode
        \let\processnextbufferline\processnextbufferlineB
      \else
        \let\processnextbufferline\processnextbufferlineA
      \fi}
     {\letbeundefined{#4}% \letvalue{#4}=\relax     % \undefined
      \expandafter\defconvertedargument\expandafter\beginofblock\expandafter{\csname#3\endcsname}% we could use defconvertedcommand here (no \expandafter)
      \expandafter\defconvertedargument\expandafter\endofblock  \expandafter{\csname#4\endcsname}% we could use defconvertedcommand here (no \expandafter)
      \ifcase\buffernestmode
          \let\processnextbufferline\processnextbufferlineB
        \or
          \let\processnextbufferline\processnextbufferlineB
        \else
          \let\processnextbufferline\processnextbufferlineA
      \fi}%
   \def\closebufferfile
     {\ifsegmentatebuffer
        \immediate\write\tmpblocks{\string\stopbufferparagraph}%
      \fi
      \immediate\closeout\tmpblocks
      \egroup
      \getvalue{#4}}%
   \doifelsenothing{#2}
     {\edef\bufferfilename{\TEXbufferfile\jobname}}%
     {\edef\bufferfilename{\TEXbufferfile{#2}}}%
   \immediate\openout\tmpblocks\bufferfilename
   \ifsegmentatebuffer
     \immediate\write\tmpblocks{\string\startbufferparagraph}%
   \fi
   \newcounter\nestedbufferlevel
   \recatcodeuppercharacterstrue
   \setcatcodetable\vrbcatcodes
   \obeylines
   \copybufferline}

\letvalue{\e!start\v!buffer}\dostartbuffer

\let\endbuffer\undefined % to please the dep parser

\def\setbuffer
  {\dosingleempty\dosetbuffer}

\long\def\dosetbuffer[#1]#2\endbuffer % seldom used so we just pass #2
  {\begingroup
   \setcurrentbuffer{#1}%
   \edef\bufferfilename{\TEXbufferfile{\currentbuffer}}%
   \immediate\openout\tmpblocks\bufferfilename
   \defconvertedargument\ascii{#2}%
   \immediate\write\tmpblocks{\ascii}%
   \immediate\closeout\tmpblocks
   \endgroup}

\def\setupbuffer
  {\dodoubleempty\dosetupbuffer}

\def\dosetupbuffer[#1][#2]%
  {\ifsecondargument
     \getparameters[\??bu#1][#2]%
   \else
     \getparameters[\??bu][#1]%
   \fi}

\def\dodefinebuffer[#1][#2]%
  {\iffirstargument % else problems
     \doglobal\increment\nofdefinedbuffers
     \letvalue{\??bu#1\c!number   }\nofdefinedbuffers
     \letvalue{\??bu#1\c!paragraph}\v!no
     \setevalue{\e!start#1}{\noexpand\dostartbuffer[#1][def-\nofdefinedbuffers][\e!start#1][\e!stop#1]}%
     \unexpanded\setevalue{\e!get  #1}{\noexpand\dogetbuffer  [#1][def-\nofdefinedbuffers]}%
     \unexpanded\setevalue{\e!type #1}{\noexpand\dotypebuffer [#1][def-\nofdefinedbuffers]}%
     \getparameters[\??bu#1][#2]%
   \fi}

\def\definebuffer
  {\dodoubleempty\dodefinebuffer}

\unexpanded\def\getbuffer
  {\dodoubleempty\dogetbuffer}

\def\dogetbuffer[#1][#2]%
  {\ifsecondargument
     \dodogetbuffer[#1][#2]%
   \else
     \dodogetbuffer[][#1]%
   \fi}

\def\dogetbufferasis{\readjobfile{\TEXbufferfile{\currentbuffer}}\donothing\donothing}%

\def\dodogetbuffer[#1][#2]%
  {\getvalue{\??bu#1\c!before}%
   \dobuffer{16}{#2}\dogetbufferasis
   \getvalue{\??bu#1\c!after}}

\unexpanded\def\typebuffer
  {\dodoubleempty\dotypebuffer}

\def\dogetfilebuffer{\typefile{\TEXbufferfile{\currentbuffer}}}

\def\dotypebuffer[#1][#2]%
  {\iffirstargument
     \dobuffer{17}{#1}\dogetfilebuffer
   \else
     \dobuffer{17}{#2}\dogetfilebuffer
   \fi}

\def\dobuffer#1#2#3%
  {\doifelsenothing{#2}
     {\dodobuffer#3\jobname}
     {\processcommalist[#2]{\dodobuffer#3}}}

\def\dodobuffer#1#2% command name
  {\pushmacro\currentbuffer
   \edef\currentbuffer{\ifcsname\??bu#2\c!number\endcsname def-\csname\??bu#2\c!number\endcsname\else#2\fi}%
   \beginrestorecatcodes
   #1%
   \endrestorecatcodes
   \popmacro\currentbuffer}

\def\processTEXbuffer{\getbuffer} % handy

% seldom used, only in a few projects that demanded more speed

\def\dostartmemorybuffer
  {\dosingleempty\dostartmemorybuffer}

\long\def\dostartmemorybuffer[#1]#2\stopbuffer
  {\setbuffer[#1]#2\endbuffer}

\let\dostartfilebuffer\startbuffer

\def\usememorybuffers{\let\startbuffer\dostartmemorybuffer}
\def\usefilebuffers  {\let\startbuffer\dostartfilebuffer}

% this features is soldom used (complex examns where we need to fetch
% special parts of a text
%
% this is not yet supported in mkiv (relatively easy to do but there
% we don't have the par tags but need to grab 'm

\def\skippedbufferparagraphs{0}

\let\startbufferparagraph\relax
\let\stopbufferparagraph \par   % \relax

\newcount\currentbufferparagraph

\def\getbufferparagraphs
  {\dodoubleempty\dogetbufferparagraphs}

\def\dosetbufferoffset#1%
  {\doifnumberelse{\getvalue{\??bu#1\c!paragraph}}
     {\currentbufferparagraph-\getvalue{\??bu#1\c!paragraph}}
     {\currentbufferparagraph \zerocount}%
   \relax}

\def\dogetbufferparagraphs[#1][#2]%
  {\iffirstargument
     \ifsecondargument
       \dosetbufferoffset{#1}%
       \doifelse{#2}\v!all
         {\def\startbufferparagraph{\normalbufferparagraph{#1}}}
         {\def\startbufferparagraph{\filterbufferparagraph{#1}{#2}}}%
       \def\stopbufferparagraph{\dostopbufferparagraph{#1}}%
       \def\next{\getparagraphedbuffer[#1]}%
     \else
       \dosetbufferoffset\empty
       \def\startbufferparagraph{\filterbufferparagraph{}{#1}}%
       \def\stopbufferparagraph{\dostopbufferparagraph{}}%
       \def\next{\getparagraphedbuffer[]}%
     \fi
   \else
     \dosetbufferoffset\empty
     \def\startbufferparagraph{\normalbufferparagraph{}}%
     \def\stopbufferparagraph{\dostopbufferparagraph{}}%
     \def\next{\getparagraphedbuffer[]}%
   \fi
   \next}

\def\dogetparagraphbuffer{\readjobfile{\TEXbufferfile{\currentbuffer}}\donothing\donothing}

\def\getparagraphedbuffer[#1]%
  {\dobuffer{16}{#1}\dogetparagraphbuffer}

\def\dostopbufferparagraph#1%
  {\getvalue{\??bu#1\c!after}\par}

\def\dostartbufferparagraph#1%
  {\par\getvalue{\??bu#1\c!before}}

\def\normalbufferparagraph
  {\advance\currentbufferparagraph \plusone
   \ifnum\currentbufferparagraph>\zerocount
     \expandafter\dostartbufferparagraph
   \else
     \expandafter\gobblebufferparagraph
   \fi}

\def\filterbufferparagraph#1#2%
  {\advance\currentbufferparagraph \plusone
   \ifcase\currentbufferparagraph
     \expandafter\gobblebufferparagraph
   \else
     \doifinsetelse{\the\currentbufferparagraph}{#2}
       {\expandafter\dostartbufferparagraph}
       {\expandafter\fakebufferparagraph}%
   \fi
   {#1}}

\long\def\gobblebufferparagraph#1#2\stopbufferparagraph
  {}

\def\fakebufferparagraph#1%
  {\bgroup
   \def\stopbufferparagraph{\dostopbufferparagraph{#1}\egroup\egroup}%
   \setbox\scratchbox\vbox\bgroup\dostartbufferparagraph{#1}}

% definitions

\definebuffer[\v!hiding] \setupbuffer[\v!hiding][\c!local=\v!yes]

\setupbuffer
  [\c!paragraph=\v!no,
   \c!before=,
   \c!after=]

\protect \endinput
