%D \module
%D   [       file=page-otr,
%D        version=2012.01.25,
%D          title=\CONTEXT\ Page Macros,
%D       subtitle=Output Routines,
%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 Page Macros / Output Routines}

%D This module will get some of the code from other modules. At the
%D same time we provide a bit more control.

% When issuing two \par\penalty-\plustenthousand's, only the first
% triggers the otr. Is this an obscure feature or an optimization?

\registerctxluafile{page-otr}{}

\unprotect

\let\triggerpagebuilder\clf_triggerpagebuilder

\installcorenamespace{outputroutine}

\installswitchcommandhandler \??outputroutine {outputroutine} \??outputroutine

\newtoks\t_page_otr_commands
\newtoks\t_page_otr_tracers

\unexpanded\def\defineoutputroutinecommand[#name]% doing multiple on one go saves syncing
  {\processcommalist[#name]\page_otr_commands_define}

\unexpanded\def\page_otr_commands_define#name%
  {\ifcsname#name\endcsname \else
     \expandafter\let\csname#name\endcsname\relax
     \normalexpanded{\t_page_otr_commands{\the\t_page_otr_commands\noexpand\page_otr_commands_process{#name}}}%
   \fi}

\let\page_otr_commands_process\gobbleoneargument

\appendtoks
    \let\page_otr_commands_process\page_otr_specifics_preset
    \the\t_page_otr_commands
    \let\page_otr_commands_process\gobbleoneargument
\to \everyswitchoutputroutine

\unexpanded\def\page_otr_specifics_preset#name%
  {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
   \ifx\page_otr_specifics_command\empty
     \writestatus{\currentoutputroutine}{- \expandafter\strippedcsname\csname#name\endcsname}%
     \expandafter\let\csname#name\endcsname\relax
   \else
     \writestatus{\currentoutputroutine}{+ \expandafter\strippedcsname\csname#name\endcsname}%
     \expandafter\let\csname#name\expandafter\endcsname\page_otr_specifics_command
   \fi}

\unexpanded\def\page_otr_specifics_preset_normal#name%
  {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
   \ifx\page_otr_specifics_command\empty
     \expandafter\let\csname#name\endcsname\relax
   \else
     \expandafter\let\csname#name\expandafter\endcsname\page_otr_specifics_command
   \fi}

\unexpanded\def\page_otr_specifics_preset_traced#name%
  {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
   \ifx\page_otr_specifics_command\empty
     \writestatus{\currentoutputroutine}{preset: - \expandafter\strippedcsname\csname#name\endcsname}%
     \expandafter\let\csname#name\endcsname\relax
   \else
     \writestatus{\currentoutputroutine}{preset: + \expandafter\strippedcsname\csname#name\endcsname}%
     \expandafter\let\csname#name\expandafter\endcsname\page_otr_specifics_command
   \fi}

\let\page_otr_specifics_preset\page_otr_specifics_preset_normal

\unexpanded\def\traceoutputroutines
  {\the\t_page_otr_tracers}

\appendtoks
    \let\page_otr_specifics_preset\page_otr_specifics_preset_traced
\to \t_page_otr_tracers

%D We have a couple of output routines and the default one is
%D the single column routine. Then there is a multicolumn variant
%D that can be used mixed, and a columnset variant that is more
%D exclusive.

\installcorenamespace{otrtriggers}

\newconstant\c_page_otr_eject_penalty   \c_page_otr_eject_penalty   -\plustenthousand
\newconstant\c_page_otr_super_penalty   \c_page_otr_super_penalty   -\plustwentythousand
\newcount   \c_page_otr_trigger_penalty \c_page_otr_trigger_penalty -100010

\newif      \ifinotr % we keep this (name) for old times sake

\unexpanded\def\page_otr_message_b{\page_otr_message_s+}
\unexpanded\def\page_otr_message_e{\page_otr_message_s-}

\unexpanded\def\page_otr_message_s#sign#what%
  {\writestatus
    \currentoutputroutine
    {#sign\space          \space
     #what\space          \space
     p:\the\outputpenalty,\space
     r:\the\realpageno   ,\space
     c:\number\mofcolumns,\space
     v:\the\vsize        ,\space
     g:\the\pagegoal     ,\space
     t:\the\pagetotal
     \ifdim\pagetotal>\pagegoal
       ,\space
       d:\the\dimexpr\pagetotal-\pagegoal\relax
     \fi}}

\unexpanded\def\page_otr_trigger#penalty%
  {\begingroup
   \par
   \penalty#penalty%
   \endgroup}

\unexpanded\def\installoutputroutine#invoke#action% \invoke \action
  {\global\advance\c_page_otr_trigger_penalty\minusone
   \edef#invoke{\page_otr_trigger{\number\c_page_otr_trigger_penalty}}%
   \setvalue{\??otrtriggers\number\c_page_otr_trigger_penalty}{#action}}

\unexpanded\def\page_otr_triggered_output_routine_traced
  {\ifcsname\??otrtriggers\the\outputpenalty\endcsname
     \page_otr_message_b{special}%
     \csname\??otrtriggers\the\outputpenalty\endcsname % \lastnamedcs can be gone
     \page_otr_message_e{special}%
   \else
     \page_otr_message_b{normal}%
     \page_otr_command_routine
     \page_otr_message_e{normal}%
   \fi}

\unexpanded\def\page_otr_triggered_output_routine_normal
  {\ifcsname\??otrtriggers\the\outputpenalty\endcsname
     \lastnamedcs
   \else
     \page_otr_command_routine
   \fi}

\let\page_otr_triggered_output_routine\page_otr_triggered_output_routine_normal

\appendtoks
    \let\page_otr_triggered_output_routine\page_otr_triggered_output_routine_traced
\to \t_page_otr_tracers

%D The real routine handler:

\ifdefined\everybeforeoutput \else \newtoks\everybeforeoutput \fi
\ifdefined\everyafteroutput  \else \newtoks\everyafteroutput  \fi

\def\page_otr_set_engine_output_routine#content%
  {\global\output
     {\inotrtrue
      \the\everybeforeoutput
      #content\relax
      \the\everyafteroutput}}

% Just as fuzzy (and in 'one' we are okay with \aftergroup anyway):
%
% \ifdefined\everybeforeoutputgroup \else \newtoks\everybeforeoutputgroup \fi
% \ifdefined\everyafteroutputgroup  \else \newtoks\everyafteroutputgroup  \fi
%
% \def\page_otr_set_engine_output_routine#content%
%   {\the\everybeforeoutputgroup
%    \global\output
%      {\inotrtrue
%       \the\everybeforeoutput
%       #content\relax
%       \the\everyafteroutput
%       \aftergroup\the\aftergroup\everyafteroutputgroup}}
%
% \appendtoks
%     \ifnum\c_page_postponed_mode=\plusone
%         \page_postponed_blocks_flush % and then not in \page_otr_construct_and_shipout
%     \fi
% \to \everyafteroutputgroup

\page_otr_set_engine_output_routine\page_otr_triggered_output_routine

\installoutputroutine\synchronizeoutput % use \triggerpagebuilder instead
  {\ifvoid\normalpagebox\else
     \unvbox\normalpagebox
     % not \pagediscards as it does more harm than good
   \fi}

\installoutputroutine\discardpage
  {\setbox\scratchbox\box\normalpagebox}

% todo: \resetpagebreak -> everyejectpage

\def\page_otr_trigger_output_routine
  {\par
   \ifvmode
     \penalty\c_page_otr_eject_penalty
   \fi
   \resetpagebreak}

\def\page_otr_fill_and_eject_page
  {\par
   \ifvmode
     \vfill
     \penalty\c_page_otr_eject_penalty
   \fi
   \resetpagebreak}

\def\page_otr_eject_page
  {\par
   \ifvmode
     \ifdim\pagetotal>\pagegoal \else
       \normalvfil
     \fi
     \penalty\c_page_otr_eject_penalty
   \fi
   \resetpagebreak}

\def\page_otr_eject_page_and_flush_inserts % can be an installed one
  {\par
   \ifvmode
     \ifdim\pagetotal>\pagegoal \else
       \normalvfil
     \fi
     \penalty\c_page_otr_super_penalty
   \fi
   \resetpagebreak}

\def\page_otr_check_for_pending_inserts
  {\ifnum\outputpenalty>\c_page_otr_super_penalty \else
     \ifnum\insertpenalties>\zerocount
       % something is being held over so we force a new page
       \page_otr_force_another_page
     \fi
   \fi}

\def\page_otr_force_another_page
  {% we should actually remove the dummy line in the otr
   \hpack to \hsize{}%
   \kern-\topskip
   \nobreak
   \vfill
   \penalty\c_page_otr_super_penalty
   \resetpagebreak}

%D For those who've read the plain \TEX\ book, we provide the next
%D macro:

\unexpanded\def\bye
  {\writestatus\m!system{Sorry, you're not done yet, so no goodbye!}}

%D We define a few constants because that (1) provides some checking
%D and (2) is handier when aligning definitions (checks nicer). Most
%D routines will use ard codes names but sometimes we want to adapt,
%D which is why we have these:

\definesystemconstant{page_otr_command_routine}
\definesystemconstant{page_otr_command_package_contents}
\definesystemconstant{page_otr_command_set_vsize}
\definesystemconstant{page_otr_command_set_hsize}
\definesystemconstant{page_otr_command_synchronize_hsize}
\definesystemconstant{page_otr_command_next_page}
\definesystemconstant{page_otr_command_next_page_and_inserts}
\definesystemconstant{page_otr_command_set_top_insertions}
\definesystemconstant{page_otr_command_set_bottom_insertions}
\definesystemconstant{page_otr_command_flush_top_insertions}
\definesystemconstant{page_otr_command_flush_bottom_insertions}
\definesystemconstant{page_otr_command_check_if_float_fits}
\definesystemconstant{page_otr_command_set_float_hsize}
\definesystemconstant{page_otr_command_flush_float_box}
\definesystemconstant{page_otr_command_side_float_output}
\definesystemconstant{page_otr_command_synchronize_side_floats}
\definesystemconstant{page_otr_command_flush_floats}
\definesystemconstant{page_otr_command_flush_side_floats}
\definesystemconstant{page_otr_command_flush_saved_floats}
\definesystemconstant{page_otr_command_flush_all_floats}
\definesystemconstant{page_otr_command_flush_margin_blocks}
\definesystemconstant{page_otr_command_test_column}
\definesystemconstant{page_otr_command_flush_facing_floats}

\definesystemconstant{singlecolumn}
\definesystemconstant{multicolumn}   % will move
\definesystemconstant{columnset}     % will move
\definesystemconstant{pagecolumn}    % will move

\defineoutputroutinecommand
  [\s!page_otr_command_routine,
   \s!page_otr_command_package_contents,
   \s!page_otr_command_set_vsize,
   \s!page_otr_command_set_hsize,
   \s!page_otr_command_synchronize_hsize, % for columns of different width
   \s!page_otr_command_next_page,
   \s!page_otr_command_next_page_and_inserts,
   \s!page_otr_command_set_top_insertions,
   \s!page_otr_command_set_bottom_insertions,
   \s!page_otr_command_flush_top_insertions,
   \s!page_otr_command_flush_bottom_insertions,
   \s!page_otr_command_check_if_float_fits,
   \s!page_otr_command_set_float_hsize,
   \s!page_otr_command_flush_float_box,
   \s!page_otr_command_side_float_output, % name will change as will hooks
   \s!page_otr_command_synchronize_side_floats,
   \s!page_otr_command_flush_floats,
   \s!page_otr_command_flush_side_floats,
   \s!page_otr_command_flush_saved_floats,
   \s!page_otr_command_flush_all_floats,
   \s!page_otr_command_flush_margin_blocks,
   \s!page_otr_command_test_column,
   \s!page_otr_command_flush_facing_floats]

\appendtoks
    \setupoutputroutine[\s!singlecolumn]%
\to \everydump

\protect \endinput

