%D \module
%D   [       file=page-sid,
%D        version=2000.10.20,
%D          title=\CONTEXT\ Page Macros,
%D       subtitle=Side Floats,
%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 / Side Floats}

\unprotect

%D These macro deal with side floats. We started with Daniel Comenetz macros as
%D published in TUGBoat Volume 14 (1993), No.\ 1: Anchored Figures at Either Margin.
%D I extended and patched the macros to suite our needs which results in a messy
%D module.
%D
%D A complication is that we need to deal with spacing differently before and after
%D the float. Also, whitespace can interfere as does the prevdepth. There is no real
%D universal solution. So, by now not much is left of that code, if only because we
%D need to match \CONTEXT\ spacing module, because we have more placement options
%D and control and because the math hackery is not suitable for \CONTEXT\ anyway.
%D
%D This code had been redone many times because we kept running into spacing issues
%D and it's not that much fun (or rewarding). It's probably the module that made
%D me go into distraction mode most often (like watching amusing Walk of The
%D Earth, sophisticated Massive Attack video clips, impressive Davie504 movies
%D and so on).

\newdimension   \d_page_sides_margin
\newdimension   \d_page_sides_height       % includes the topskip
\newdimension   \d_page_sides_width
\newdimension   \d_page_sides_hsize
\newdimension   \d_page_sides_vsize
\newdimension   \d_page_sides_vsize_reset
\newdimension   \d_page_sides_progress
\newdimension   \d_page_sides_page_total
\newdimension   \d_page_sides_shape_down_shift

\newdimension   \d_page_sides_leftoffset
\newdimension   \d_page_sides_rightoffset

%newbox         \b_page_sides_bottom
\newbox         \b_page_sides_spill_over

\newinteger     \c_page_sides_lines_done
\newinteger     \c_page_sides_checks_done
\newinteger     \c_page_sides_n_of_lines
\newinteger     \c_page_sides_n_of_hang
\newconstant    \c_page_sides_float_type
\newinteger     \c_page_sides_hangafter

\newconditional \c_page_sides_short
\newconditional \c_page_sides_flag
\newconditional \c_page_sides_shape_down
\newconditional \c_page_sides_keep_together
\newconditional \c_page_sides_force_shape    % not default now

\newdimension   \d_page_sides_shift
\newdimension   \d_page_sides_extrashift
\newdimension   \d_page_sides_leftshift
\newdimension   \d_page_sides_rightshift
\newdimension   \d_page_sides_leftskip
\newdimension   \d_page_sides_rightskip
\newdimension   \d_page_sides_maximum
\newdimension   \d_page_sides_topskip
\newdimension   \d_page_sides_bottomskip
\newdimension   \d_page_sides_midskip
\newdimension   \d_page_sides_downshift
\newdimension   \d_page_sides_pagetotal
\newdimension   \d_page_sides_topoffset
\newdimension   \d_page_sides_bottomoffset
\newdimension   \d_page_sides_toptotal
\newdimension   \d_page_sides_bottomtotal

\newconstant    \c_page_sides_align
\newconstant    \c_page_sides_skipmode
\newconstant    \c_page_sides_tolerance

\newconstant    \c_page_sides_method % sort of obsolete

\newdimension   \d_page_sides_progression

\newinteger     \c_page_sides_m_of_lines
\newconditional \c_page_sides_delayed

%newconditional \c_page_sides_check_same_page

\newif          \iftracesidefloats % public (might change)

%D Defaults:

\d_page_sides_vsize_reset -\onepoint
%d_page_sides_vsize_reset  \zeropoint % could be an option, needs testing

%D We have some basic (and colorful) tracing:

\def\page_sides_floats_legend
  {\showmessage\m!floatblocks{16}\empty
   \glet\page_sides_floats_legend\relax}

\installtextracker{floats.anchoring}
  {\page_sides_floats_legend
   \tracesidefloatstrue}
  {\tracesidefloatsfalse}

%D The horizontal shifts depend on the location: left or right in the text, margin
%D or edge. These shifts are rather stable and don't interfere with the page flow
%D as much as the vertical ones do.

\def\page_sides_process_float_backspace  {\global\c_page_sides_float_type\plusone  \page_sides_handle_float}
\def\page_sides_process_float_leftedge   {\global\c_page_sides_float_type\plustwo  \page_sides_handle_float}
\def\page_sides_process_float_leftmargin {\global\c_page_sides_float_type\plusthree\page_sides_handle_float}
\def\page_sides_process_float_left       {\global\c_page_sides_float_type\plusfour \page_sides_handle_float}
\def\page_sides_process_float_right      {\global\c_page_sides_float_type\plusfive \page_sides_handle_float}
\def\page_sides_process_float_rightmargin{\global\c_page_sides_float_type\plussix  \page_sides_handle_float}
\def\page_sides_process_float_rightedge  {\global\c_page_sides_float_type\plusseven\page_sides_handle_float}
\def\page_sides_process_float_cutspace   {\global\c_page_sides_float_type\pluseight\page_sides_handle_float}
\def\page_sides_process_float_margin     {\global\c_page_sides_float_type\pluseight\page_sides_handle_float}

\def\page_sides_check_horizontal_skips
  {\ifcase\c_page_sides_skipmode
   \or % high
   \or % low
   \or % fit
     \global\d_page_sides_margin\zeropoint
   \fi}

\def\page_sides_apply_horizontal_shift
  {\ifdim\d_page_sides_maximum>\zeropoint
     \ifcase\c_page_sides_float_type
       % invalid
     \or
       % backspace
     \or
       \global\d_page_sides_shift{%
         -\d_page_sides_maximum
         -\rightorleftpageaction \leftedgedistance   \rightedgedistance
         -\rightorleftpageaction \leftmarginwidth    \rightmarginwidth
         -\rightorleftpageaction \leftmargindistance \rightmargindistance
         -\compensatedinnermakeupmargin
       }%
     \or
       \global\d_page_sides_shift{%
         -\d_page_sides_maximum
         -\rightorleftpageaction \leftmargindistance \rightmargindistance
         -\compensatedinnermakeupmargin
       }%
     \or
       % left
     \or
       % right
     \or
       \global\d_page_sides_shift{%
         -\d_page_sides_maximum
         -\rightorleftpageaction \leftmargindistance \rightmargindistance
         -\compensatedinnermakeupmargin
       }%
     \or
       \global\d_page_sides_shift{%
         -\d_page_sides_maximum
         -\rightorleftpageaction \leftedgedistance   \rightedgedistance
         -\rightorleftpageaction \leftmarginwidth    \rightmarginwidth
         -\rightorleftpageaction \leftmargindistance \rightmargindistance
         -\compensatedinnermakeupmargin
       }%
     \or
       % cutspace
     \fi
   \fi
   \ifzeropt\d_page_sides_shift
     \ifnum\c_page_sides_float_type=\plusfour
       \global\advanceby\d_page_sides_shift\d_page_sides_extrashift
       \global\d_page_sides_extrashift\zeropoint
     \orelse\ifnum\c_page_sides_float_type=\plusfive
       \global\advanceby\d_page_sides_shift\d_page_sides_extrashift
       \global\d_page_sides_extrashift\zeropoint
     \fi
   \else
     \ifnum\c_page_sides_float_type<\plusfour
       \global\c_page_sides_float_type\plusfour
     \orelse\ifnum\c_page_sides_float_type>\plusfive
       \global\c_page_sides_float_type\plusfive
     \fi
   \fi}

\def\page_sides_set_skips
  {\global\d_page_sides_rightskip\zeropoint
   \global\d_page_sides_leftskip \zeropoint
   \ifcase\c_page_sides_float_type
   \or % backspace
     \global\d_page_sides_leftskip{%
       +\rightorleftpageaction \backspace           \cutspace
       +\compensatedinnermakeupmargin
     }%
   \or % leftedge
     \global\d_page_sides_leftskip{%
       +\rightorleftpageaction \leftmargindistance  \rightmargindistance
       +\rightorleftpageaction \leftmarginwidth     \rightmarginwidth
       +\rightorleftpageaction \leftedgedistance    \rightedgedistance
       +\compensatedinnermakeupmargin
     }%
   \or % leftmargin
     \global\d_page_sides_leftskip{%
       +\rightorleftpageaction \leftmargindistance  \rightmargindistance
       +\compensatedinnermakeupmargin
     }%
   \or % leftside
   \or % rightside
   \or % rightmargin
     \global\d_page_sides_rightskip{%
       +\rightorleftpageaction \rightmargindistance \leftmargindistance
       +\compensatedinnermakeupmargin
     }%
   \or % rightedge
     \global\d_page_sides_rightskip{%
       +\rightorleftpageaction \rightmargindistance \leftmargindistance
       +\rightorleftpageaction \rightmarginwidth    \leftmarginwidth
       +\rightorleftpageaction \rightedgedistance   \leftedgedistance
       +\compensatedinnermakeupmargin
     }%
   \or % cutspace
     \global\d_page_sides_rightskip{%
       +\rightorleftpageaction \cutspace            \backspace
       +\compensatedinnermakeupmargin
     }%
   \fi
   \global\d_page_sides_leftoffset \d_page_sides_rightskip
   \global\d_page_sides_rightoffset\d_page_sides_leftskip
   \ifdim\d_page_sides_rightskip>\zeropoint
     \global\advanceby\d_page_sides_rightskip\rightskip
   \fi
   \ifdim\d_page_sides_leftskip >\zeropoint
     \global\advanceby\d_page_sides_leftskip \leftskip
   \fi}

%D Shifts get applied to the float box:

\def\page_sides_relocate_float#1%
  {\global\setbox\floatbox\hpack
     {\ifnum\c_page_sides_float_type=\plusfour
        \kern\d_page_sides_leftshift
      \orelse\ifnum\c_page_sides_float_type=\plusone
        \kern\d_page_sides_leftshift
      \fi
      \ifnum\c_page_sides_float_type>\plusfour
        \kern-\d_page_sides_extrashift
      \else
        \kern\d_page_sides_shift
      \fi
      \vbox{#1\ifnum\c_page_sides_align=\plusfour \removedepth \fi}%
      \ifnum\c_page_sides_float_type>\plusfour
        \kern\d_page_sides_shift
      \else
        \kern-\d_page_sides_extrashift
      \fi
      \ifnum\c_page_sides_float_type=\pluseight
        \kern\d_page_sides_rightshift
      \orelse\ifnum\c_page_sides_float_type=\plusfive
        \kern\d_page_sides_rightshift
      \fi}}

%D The vertical skips are a nightmare and this mechanism is about as complex
%D as one can get it.

\def\page_sides_check_vertical_skips
  {\ifdim\d_page_sides_topskip   <\zeropoint\d_page_sides_topskip   \zeropoint\fi
   \ifdim\d_page_sides_bottomskip<\zeropoint\d_page_sides_bottomskip\zeropoint\fi
   \ifdim\d_page_sides_midskip   <\zeropoint\d_page_sides_midskip   \zeropoint\fi
   %
   \global\d_page_sides_toptotal   {\d_page_sides_topskip   +\d_page_sides_topoffset   }%
   \global\d_page_sides_bottomtotal{\d_page_sides_bottomskip+\d_page_sides_bottomoffset}%
   \ifcase\c_page_sides_skipmode
   \or % high
     \global\d_page_sides_toptotal   \d_page_sides_topoffset
   \or % low
     \global\d_page_sides_bottomtotal\d_page_sides_bottomoffset
   \or % fit
     \global\d_page_sides_toptotal   \d_page_sides_topoffset
     \global\d_page_sides_bottomtotal\d_page_sides_bottomoffset
   \fi}

%D These shifts get (selectively) applied with a bit of optional tracing.

\def\page_sides_apply_vertical_shift_normal
  {\global\setbox\floatbox\hpack % why extra box
     {\vpack
        {\forgetall
         \hsize\wd\floatbox
         \vskip\privatescratchdimen
         \offinterlineskip
         \box\floatbox
         % somehow we need this \scratchbox magic, but at least it's the same as the
         % tracer now
         \setbox\scratchbox\emptyhbox
         \wd\scratchbox\hsize
         \ht\scratchbox\d_page_sides_bottomtotal
         \box\scratchbox
         \vskip-\d_page_sides_bottomtotal
         \ifnum\c_page_sides_align=\plusfive
           \vskip-\lineheight
         \fi}}}

\def\page_sides_apply_vertical_shift_traced
  {\global\setbox\floatbox\hpack % why extra box
     {\backgroundline[trace:r]{\ruledhpack{\vpack
        {\forgetall
         \hsize\wd\floatbox
         \vskip\privatescratchdimen
         \offinterlineskip
         \backgroundline
           [trace:g]%
           {\ruledhpack{\box\floatbox}}%
         \par
         \blackrule
           [\c!color=trace:s,%
            \c!height=\d_page_sides_bottomtotal,%
            \c!depth=\zeropoint,%
            \c!width=\hsize]%
         \vskip-\d_page_sides_bottomtotal
         \ifnum\c_page_sides_align=\plusfive
           \vskip-\lineheight
         \fi}}}}}

\def\page_sides_apply_vertical_shift
  {\ifnum\c_page_sides_align=\plusfour
     \getnoflines{\ht\floatbox}%
     \privatescratchdimen{\noflines\lineheight-\strutdp}%
     \getrawnoflines\d_page_sides_toptotal
     \advanceby\privatescratchdimen\noflines\lineheight
     \page_sides_force_depth
     \ht\floatbox\privatescratchdimen
     \dp\floatbox\zeropoint
   \else
   \fi
   \ifcase\c_page_sides_align \else
     \global\d_page_sides_toptotal\zeropoint
   \fi
   \privatescratchdimen
     \ifnum\c_page_sides_float_type<\plusfour
       \d_page_sides_toptotal
     \orelse\ifnum\c_page_sides_float_type>\plusfive
       \d_page_sides_toptotal
     \else
       \zeropoint
     \fi
   % the top of the box is at the previous baseline
   \ifcase\c_page_sides_align
       % 0 normal
     \advanceby\privatescratchdimen\strutdp % or \openstrutdepth
   \or % 1 height
     \advanceby\privatescratchdimen\strutdp % or \openstrutdepth
   \or % 2 line
   \or % 3 depth
     \advanceby\privatescratchdimen\lineheight % or \openlineheight
     \advanceby\privatescratchdimen\strutdp    % or \openstrutdepth
   \or % 4 grid
     \privatescratchdimen\zeropoint
   \or
     \advanceby\privatescratchdimen\strutht % or \openstrutheight
   \fi
   % new
   \global\c_page_sides_lines_done\zerocount
   \ifconditional\c_page_sides_shape_down
     \global\d_page_sides_shape_down_shift{%
        \privatescratchdimen
       +\htdp\floatbox
     }%
     \advanceby\privatescratchdimen\c_page_sides_n_of_lines\lineheight
\advanceby\privatescratchdimen2\lineheight
   \else
     \global\d_page_sides_shape_down_shift\zeropoint
     \advanceby\privatescratchdimen\c_page_sides_n_of_lines\lineheight
   \fi
   \iftracesidefloats
     \page_sides_apply_vertical_shift_traced % uses \privatescratchdimen
   \else
     \page_sides_apply_vertical_shift_normal % uses \privatescratchdimen
   \fi
   \ifnum\c_page_sides_float_type<\plusfour
     \global\d_page_sides_toptotal\zeropoint
   \orelse\ifnum\c_page_sides_float_type>\plusfive
     \global\d_page_sides_toptotal\zeropoint
   \fi
   \global\d_page_sides_downshift\zeropoint}

%D We have a few virtual dimensions. I'm not sure what to do with \type
%D {\pagedepth} and \type {\pageshrink} in the next two. If we ever need
%D that it will become options.

\permanent\def\d_page_sides_flush_criterium
  {\dimexpr
      \d_page_sides_vsize
     -\d_page_sides_bottomtotal
     -\pagetotal
   \relax}

\permanent\def\d_page_sides_room_criterium
  {\dimexpr
      \d_page_sides_vsize
     -\d_page_sides_bottomtotal % added here too
     -\pagetotal
   \relax}

%D In order to get a consistent spacing we force a strutdepth unless the
%D preceding material has more depth than that already. This way anchoring
%D becomes predictable.

% \protected\def\page_sides_force_depth
%   {\iftracesidefloats
%     \begingroup
%      \c_page_force_strut_depth_trace_mode\plusone
%      \ifconditional\c_page_sides_check_same_page
%        \forcestrutdepthplus
%      \else
%        \forcestrutdepth
%     \fi
%     \endgroup
%    \else
%      \ifconditional\c_page_sides_check_same_page
%        \forcestrutdepthplus
%      \else
%        \forcestrutdepth
%      \fi
%    \fi
%    \page_otr_command_set_vsize} % new

% test case:
%
% \starttext
%     \strut\vskip180mm \input ward
%     \subject{Test}
%     \placefigure[right,none]{none}{\blackrule[width=4cm,height=3cm]} test
% \stoptext

\protected\def\page_sides_force_depth
  {\iftracesidefloats
     \enabletrackers[otr.forcestrutdepth]% \c_page_force_strut_depth_trace_mode\plusone
   \fi
 % \unless\ifvmode
 %   \writestatus{side floats}{confusion}
 % \fi
   % flush what we have and check
   \forcestrutdepth
   % trigger pagebuilder, \pageboundary gives nicer tracing
   \iffalse
     \penalty\zerocount % works too
   \else
    %\tracingpages\plusone \tracingonline\plustwo
     \pageboundary\plustenthousand % becomes a penalty (after triggering the callback) (experimental!)
    %\tracingpages\zerocount
   \fi
   \page_otr_command_set_vsize} % new, no longer really needed

\def\page_sides_flush_floats
  {\ifconditional\c_page_sides_shape_down\else
     \par
   \fi
   \ifdim\d_page_sides_flush_criterium>\zeropoint
     \page_sides_flush_floats_progress
     \page_sides_flush_floats_after_next
   \fi
   \page_sides_flush_floats_reset}

\def\page_sides_flush_floats_text
  {\par
   % what with \c_anch_backgrounds_text_level>\plusone
   \ifdim\d_page_sides_flush_criterium>\zeropoint
     \page_sides_flush_floats_progress
     \page_sides_flush_floats_after_none
   \fi
   \page_sides_flush_floats_reset}

\def\page_sides_flush_floats_reset
  {\global\d_page_sides_vsize\d_page_sides_vsize_reset
   % also here if used at all \global\holdinginserts\zerocount
   \global\c_page_sides_short\conditionalfalse
   \global\c_page_sides_flag\conditionalfalse
   \global\c_page_sides_checks_done\zerocount}

\def\page_sides_flush_floats_after_none % we force a flush
  {\ifdim\d_page_sides_midskip>\zeropoint
      \blank[\the\d_page_sides_midskip]
   \fi
   \ignoreparskip
   \blank[\v!disable]}

\def\page_sides_flush_floats_after_next % we have two successive ones
  {\ifdim\d_page_sides_bottomskip>\zeropoint
      \blank[\the\d_page_sides_bottomskip]
   \fi
   \ignoreparskip
   \blank[\v!disable]}

%D A rudimentary checker:

\permanent\protected\def\doifelsesidefloat
  {\par
   \ifdim\d_page_sides_room_criterium>\zeropoint % -\pagedepth
     \expandafter\firstoftwoarguments
   \else
     \expandafter\secondoftwoarguments
   \fi}

\aliased\let\doifsidefloatelse\doifelsesidefloat

%D Sometimes we need to fill up the space alongside a side float and this
%D is where we define the helpers. A user can enforce a smaller step. We use
%D large steps when possible.

\installcorenamespace{sidefloatsteps}

\defcsname\??sidefloatsteps\v!line  \endcsname{\strut}
\defcsname\??sidefloatsteps\v!big   \endcsname{\strut}
\defcsname\??sidefloatsteps\v!medium\endcsname{\halflinestrut} % was \halfstrut
\defcsname\??sidefloatsteps\v!small \endcsname{\noheightstrut} % was \quarterstrut

\def\page_sides_flush_floats_tracer
  {\dontleavehmode
   \ruledhpack\bgroup\backgroundline[trace:b]{%
     \llap{\smash{\vrule\s!width4\points\s!height.4\points\s!depth.4\points}}%
     \ifnum\recurselevel=\plusone
       \llap{\smash{\smallinfofont\the\scratchdimen}\hskip.5\leftmargindistance}%
     \orelse\ifodd\recurselevel
       \llap{\smash{\smallinfofont\recurselevel}\hskip.5\leftmargindistance}%
     \fi
     \page_sides_flush_floats_normal
     \kern\hsize
   \egroup}}

\let\m_page_sides_strut\relax

\def\page_sides_flush_floats_normal
  {\ifdim\scratchdimen>\struthtdp
     \strut
   \else
     \m_page_sides_strut
   \fi}

\def\page_sides_flush_floats_progress
  {\begingroup
   \page_sides_force_depth
   \parskip\zeroskip
   \let\page_sides_flush_floats\relax
   \edef\m_page_sides_strut
     {\ifcsname\??sidefloatsteps\rootfloatparameter\c!step\endcsname
        \lastnamedcs
      \else
        \noheightstrut
      \fi}%
   \forgetall
   \offinterlineskip
%    \localcontrolledrepeating
   \localcontrolledendless
     {\scratchdimen\d_page_sides_flush_criterium
      \ifdim\scratchdimen>\onepoint      % good enough, can become configurable
        \ifnum\currentloopiterator>\plushundred % safeguard, sort of deadcycles
          \quitloop
        \orelse\iftracesidefloats
          \page_sides_flush_floats_tracer\par
        \else
          \page_sides_flush_floats_normal\par
        \fi
      \else
        \page_sides_force_depth
        \quitloop
      \fi}%
   \endgroup}

%D We force a parskip and ignore it afterwards. We can nil it by setting the
%D \type {spacebeforeside} parameter. We can have a leading blank so we need
%D to make sure that we use blank to inject the parskip and then ignore
%D the one injected by the engine.

\def\page_sides_inject_before
  {\page_sides_force_depth
   \ifdim\parskip>\zeropoint
     \ifdim\parskip>\d_strc_floats_top
       \ifdim\d_strc_floats_top>\zeropoint
         \ignoreparskip
         \blank[\v!white]%
       \else
         \checkedblank[\rootfloatparameter\c!spacebeforeside]%
       \fi
     \else
       \checkedblank[\rootfloatparameter\c!spacebeforeside]%
     \fi
   \else
     \checkedblank[\rootfloatparameter\c!spacebeforeside]%
   \fi}

%D We are now done with \type {spacebefore} and the parskip is already
%D injected. The dummy line makes sure that we anchor properly and it
%D also can serve as tracer.

\def\page_sides_inject_dummy_line_normal
  {\hpack to \availablehsize{\strut\hss}}

\def\page_sides_inject_dummy_line_traced
  {\ruledhpack to \availablehsize{\backgroundline[trace:c]{\page_sides_inject_dummy_line_normal}}}

\def\page_sides_inject_dummy_lines
  {\par
   \nointerlineskip
 % \ifnum\lastpenalty>\zerocount
 %   \penalty\plustenthousand
 % \fi
   \dontleavehmode
   \iftracesidefloats
     \page_sides_inject_dummy_line_traced
   \else
     \page_sides_inject_dummy_line_normal
   \fi
   \par
   % on an empty page we have topskip, say 12pt
   \ignoreparskip
   % this can be 18.5pt
   \kern-{\lineheight+\strutdp}%
   % so we can actually have a -2.5pt skip on top
   \ignoreparskip
   \blank[\v!samepage]
   \blank[\v!disable]
   % now say we are negative now
   \ifdim\pagetotal<\zeropoint
      % then we're at the top of the page ... quite messy .. i really need to
      % make the page builder a bit more flexible .. should we do something now?
   \fi}

%D Checkers:

\def\page_sides_check_floats_after_par
  {\page_sides_check_floats_indeed
   \ifdim\d_page_sides_pagetotal=\pagetotal \else
     \glet\page_sides_check_floats\page_sides_check_floats_indeed
     \page_sides_flush_floats
     \global\c_page_sides_n_of_lines\zerocount % here !
   \fi}

\protected\def\page_sides_flush_floats_after_par
  {\global\d_page_sides_pagetotal\pagetotal
   \glet\page_sides_check_floats\page_sides_check_floats_after_par}

\protected\def\page_sides_forget_floats
  {\global\d_page_sides_vsize\d_page_sides_vsize_reset
   \global\c_page_sides_n_of_lines\zerocount
   % also here if used at all \global\holdinginserts\zerocount
   \global\c_page_sides_short\conditionalfalse
   \global\c_page_sides_flag\conditionalfalse}

%D Here comes the output routine. We either go the fast route or we use the
%D normal one (stored in \type {\page_otr_command_side_float_output}. We no
%D longer have this fuzzy code around with penalties and indentation and
%D such.

\def\page_sides_output_routine
  {\page_otr_command_side_float_output
   \ifconditional\c_page_sides_short
     \global\c_page_sides_short\conditionalfalse
   \else
     \global\d_page_sides_vsize\d_page_sides_vsize_reset
     \global\c_page_sides_n_of_lines\zerocount
   \fi}

\def\page_sides_place_float
  {\ifnum\c_page_sides_float_type=\plusfour \kern\d_page_sides_toptotal \fi
   \ifnum\c_page_sides_float_type=\plusfive \kern\d_page_sides_toptotal \fi
   \ifconditional\c_page_sides_shape_down
     \page_sides_place_float_normal
   \else
     \ifgridsnapping
       \page_sides_place_float_grid
     \else
       \page_sides_place_float_normal
     \fi
     \par
     \kern-\d_page_sides_height
     \penalty10001 % oeps, this will change
     \normalbaselines
   \fi}

\def\page_sides_place_float_normal
  {\page_sides_push_float_inline\firstofoneargument}

%D The following needs some more work .. consider this a quick hack. We probably
%D need an mkiv hanging grid option.

% \def\page_sides_place_snap_to_grid#1%
%   {\edef\p_grid{\floatparameter\c!grid}%
%    \ifempty\p_grid\else
%      \snaptogrid[none]%
%    \fi
%    \hpack{#1}%
%
% \def\page_sides_place_float_grid
%   {\getrawnoflines\d_page_sides_height % raw ?
%    \d_page_sides_height\noflines\lineheight
%    \page_sides_push_float_inline\page_sides_place_snap_to_grid}

%D This is a hack: we will pick up on this after we're done with the mvl
%D balancer.

\def\page_sides_place_snap_to_grid#1%
  {\begingroup % todo: prevent snapping
   \setbox\scratchbox\hpack{\vrule width 1mm #1}%
   \dp\scratchbox\strutdp
   \ht\scratchbox\dimexpr\d_page_sides_height-\strutdp\relax
   \boxyoffset\scratchbox-\strutdp
   \box\scratchbox
   \endgroup}

\def\page_sides_place_float_grid
  {\getrawnoflines\d_page_sides_height % raw ?
   \d_page_sides_height\noflines\lineheight
   \vkern-\strutht
   \page_sides_push_float_inline\page_sides_place_snap_to_grid}

%D End of hack.

\let\strc_floats_mark_par_as_free\relax

% \def\page_sides_push_float_inline#1%
%   {\begingroup
%    \reseteverypar % needed !
%    \parskip\zeroskip  % needed !
%    \nointerlineskip
%    \page_sides_set_skips
%    \page_floats_report_total
%    \relax
%   %\lefttoright % not needed in lmtx
%    \strc_floats_mark_par_as_free
%    \ifcase\c_page_sides_float_type
%      % invalid
%    \or % backspace
%      \noindent#1{\llap{\rlap{\box\floatbox}\kern\d_page_sides_leftskip}}\hfill
%    \or % leftedge
%      \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
%    \or % leftmargin
%      \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
%    \or % leftside
%      \noindent#1{\box\floatbox}\hfill
%    \or % rightside
%      \hfill#1{\box\floatbox}%
%    \or % rightmargin
%      \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
%    \or % rightedge
%      \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
%    \or % cutspace
%      \hfill#1{\rlap{\kern\d_page_sides_rightskip\llap{\box\floatbox}}}%
%    \fi
%    \endgroup}

\def\page_sides_push_float_inline_indeed#1%
  {\ifcase\c_page_sides_float_type
     % invalid
   \or % backspace
     \noindent#1{\llap{\rlap{\box\floatbox}\kern\d_page_sides_leftskip}}\hfill
   \or % leftedge
     \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
   \or % leftmargin
     \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
   \or % leftside
     \noindent#1{\box\floatbox}\hfill
   \or % rightside
     \hfill#1{\box\floatbox}%
   \or % rightmargin
     \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
   \or % rightedge
     \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
   \or % cutspace
     \hfill#1{\rlap{\kern\d_page_sides_rightskip\llap{\box\floatbox}}}%
   \fi}

\def\page_sides_push_float_inline#1%
  {\ifconditional\c_page_sides_shape_down
     \page_sides_set_skips
     \page_floats_report_total
     \global\setbox\floatbox\hbox to \hsize\bgroup
       \page_sides_push_float_inline_indeed#1%
     \egroup
   \else
     \begingroup
     \reseteverypar    % needed !
     \parskip\zeroskip % needed !
     \nointerlineskip
     \page_sides_set_skips
     \page_floats_report_total
     \relax
    %\lefttoright % not needed in lmtx
     \strc_floats_mark_par_as_free
     \page_sides_push_float_inline_indeed#1%
     \endgroup
   \fi}

% \def\page_sides_analyse_progress
%   {\d_page_sides_progress\d_page_sides_vsize
%    \ifconditional\c_page_sides_flag
%      \advanceby\d_page_sides_progress-\d_page_sides_page_total
%      \global\c_page_sides_flag\conditionalfalse
%    \else
%      \advanceby\d_page_sides_progress-\pagetotal
%    \fi}

% test case
%
% \usemodule[art-01]
% \starttext
%     \dorecurse{40}{\line{#1}}
%     \placefigure[left]{}{}
%     \input ward
%     \startitemize
%         \item word \item word \item word \item word
%     \stopitemize
%     \input ward
%     \page
% \stoptext

\def\page_sides_analyse_progress
  {%\page_otr_command_set_vsize % this is new, otherwise topfloats are not taken into account
   \d_page_sides_progress\d_page_sides_vsize
   \ifconditional\c_page_sides_flag
     \advanceby\d_page_sides_progress-\d_page_sides_page_total
     \global\c_page_sides_flag\conditionalfalse
   \else
     \ifdim{\d_page_sides_progress+\d_page_sides_bottomtotal}>\pagegoal
       % we adapt pagegoal because we can already have placed something with
       % everypar and we hope that it triggers a flush, see test above
       \pagegoal{\pagegoal-\d_page_sides_bottomtotal}%
     \fi
     \advanceby\d_page_sides_progress-\pagetotal
   \fi}

\def\page_sides_analyse_space_stage_one
  {\global\c_page_sides_flag\conditionaltrue
   % \ifdim\pagegoal=\maxdimen
   %     \pagegoal\textheight % maybe
   % \fi
   \global\d_page_sides_page_total\pagetotal % global
   \ifnum\c_page_sides_float_type<\plusfour
     \global\d_page_sides_width \zeropoint
   \orelse\ifnum\c_page_sides_float_type>\plusfive
     \global\d_page_sides_width\zeropoint
   \else
     \global\d_page_sides_width{\wd\floatbox+\d_page_sides_margin}%
   \fi
   \ifdim\d_page_sides_width<\zeropoint
     \global\d_page_sides_width\zeropoint
   \fi
   \global\d_page_sides_hsize {\hsize-\d_page_sides_width}%
   \global\d_page_sides_height{\htdp\floatbox+\d_page_sides_toptotal}%
   \global\d_page_sides_vsize {\d_page_sides_height+\d_page_sides_page_total}%
   \scratchdimenone\d_page_sides_vsize
   \scratchdimentwo\pagegoal
   \ifcase\c_page_sides_tolerance
     \ifcase\c_page_sides_method
       % method 0 : raw
     \or
       % method 1 : safe (default)
       \advanceby\scratchdimentwo -\strutdp
     \or
       % method 2 : tight (grid default)
       \advanceby\scratchdimenone -\onepoint
     \fi
   \or
     % tolerant
     \advanceby\scratchdimentwo -.5\strutdp
   \or
     % verytolerant
   % \advanceby\scratchdimenone -\onepoint (maybe)
   \else
     \advanceby\scratchdimentwo -\strutdp
   \fi}

\def\page_sides_analyse_space_stage_two
  {% how about \pagedepth
   \ifdim\scratchdimenone>\scratchdimentwo
     \global\c_page_floats_room\conditionalfalse
   \else
     \ifdim{\pagegoal-\d_page_sides_vsize}<\d_page_sides_bottomtotal
      % just weird: \global\advanceby\d_page_sides_vsize \scratchdimenone
       \global\c_page_sides_short\conditionaltrue
       % why was this \global\holdinginserts\plusone
     \else
       \global\advanceby\d_page_sides_vsize \d_page_sides_bottomtotal % wins over inbetween
       \global\c_page_sides_short\conditionalfalse
     \fi
     \global\c_page_floats_room\conditionaltrue
   \fi}

\def\page_sides_analyse_space
  {\page_sides_analyse_space_stage_one
%    \ifconditional\c_page_sides_check_same_page
%      \ifdim\d_spac_prevcontent>\zeropoint
%        \ifdim{\scratchdimenone+\d_spac_prevcontent}>\scratchdimentwo
%          \clf_pushatsame
%          \setbox\scratchbox\vpack{\clf_popatsame}%
%          \page
%          \box\scratchbox
%          \vskip-\lineskip
%          \page_sides_analyse_space_stage_one
%        \fi
%      \fi
%    \fi
   \page_sides_analyse_space_stage_two
   \ifconditional\c_page_sides_shape_down
     \global\c_page_floats_room\conditionaltrue
   \fi}

%D As we have no clear end of one or more paragraphs we only have pre float
%D skips.

\newconstant\c_page_sides_page_method % will be: \c_page_sides_page_method\plusone

\def\page_otr_force_new_page_one
  {\vskip\d_page_sides_height
   \penalty\outputpenalty
   \vskip{\strutdp-\d_page_sides_height}%
   \prevdepth\strutdp}
  %\ignoreparskip}

% \def\page_sides_handle_float#1%
%   {\page_sides_initialize_checker
%    \page_sides_check_horizontal_skips
%    \page_sides_check_vertical_skips
%    \page_sides_apply_horizontal_shift
%    \page_sides_check_previous_float
%    \page_sides_inject_before
%    \page_sides_inject_dummy_lines
%    \page_sides_relocate_float{#1}%
%    \page_sides_apply_vertical_shift
%    \page_sides_analyse_space
%    \ifconditional\c_page_floats_room
%      \global\c_page_sides_delayed\conditionalfalse
%      % we're ok
%    \else
%      \global\c_page_sides_delayed\conditionaltrue
%      \global\c_page_sides_m_of_lines\c_page_sides_n_of_lines
%      \ifcase\c_page_sides_page_method
%        \page_otr_fill_and_eject_page
%      \or
%        \page_otr_force_new_page_one
%      \else
%        \page_otr_fill_and_eject_page
%      \fi
%      \global\c_page_sides_n_of_lines\c_page_sides_m_of_lines
%      \page_sides_analyse_space
%     %\page_sides_inject_before
%      \page_sides_inject_dummy_lines
%    \fi
%    \page_sides_place_float
%    \global\c_page_sides_delayed\conditionalfalse
%    \page_sides_check_floats_reset
%    \page_sides_wrapup}

\def\page_sides_handle_float#1%
  {\page_sides_initialize_checker
   \page_sides_check_horizontal_skips
   \page_sides_check_vertical_skips
   \page_sides_apply_horizontal_shift
   \page_sides_check_previous_float
   \page_sides_inject_before
   \page_sides_inject_dummy_lines
   \page_sides_relocate_float{#1}%
   \page_sides_apply_vertical_shift
   \page_sides_analyse_space
   \ifconditional\c_page_floats_room
     \global\c_page_sides_delayed\conditionalfalse
     % we're ok
   \else
     \ifconditional\c_page_sides_keep_together
       \clf_interceptsamepagecontent\b_page_sides_spill_over
     \fi
     \global\c_page_sides_delayed\conditionaltrue
     \global\c_page_sides_m_of_lines\c_page_sides_n_of_lines
     \ifcase\c_page_sides_page_method
       \page_otr_fill_and_eject_page
     \or
       \page_otr_force_new_page_one
     \else
       \page_otr_fill_and_eject_page
     \fi
     \ifvoid\b_page_sides_spill_over\else
       \box\b_page_sides_spill_over
     \fi
     \page_sides_analyse_space
    %\page_sides_inject_before
     \page_sides_inject_dummy_lines
   \fi
   \page_sides_place_float
   \global\c_page_sides_delayed\conditionalfalse
   \page_sides_check_floats_reset
   \page_sides_wrapup}

\def\page_sides_wrapup
  {% we need to do this aftergroup
   \aftergroup\par
   \aftergroup\ignoreparskip
   \aftergroup\ignorespaces
   \aftergroup\page_sizes_delay_float}%

\def\page_sides_local_float_flush
  {\ifconditional\c_page_sides_shape_down
     \ifnum\localboxlinenumber=\c_page_sides_n_of_lines\relax
       \hpack to \localboxlinewidth
         xoffset -\the\localboxlinewidth
         yoffset -\d_page_sides_shape_down_shift
       {\box\floatbox}%
     \fi
   \fi}

%D Experimental and tricky:
%D
%D \starttext
%D     \samplefile{lorem} \blank[20*line]
%D     \startplacefigure[location={right,15*hang,force}]
%D    %\startplacefigure[location={right,15*hang}]
%D         \framed[width=30mm,height=20mm]{!!}
%D     \stopplacefigure
%D     \dorecurse{10}{\samplefile{lorem}}
%D \stoptext

\definelocalboxes
  [\v!left:\v!float]
  [\c!command=\page_sides_local_float_flush,
   \c!location=\v!middle]

\def\page_sizes_delay_float
  {\ifconditional\c_page_sides_shape_down
     \localbox[\v!left:\v!float]{}%
   \fi}%

\def\page_sides_check_floats_indeed
  {\page_sides_analyse_progress
   \ifdim\d_page_sides_progress>\zeropoint
     \page_sides_check_floats_set
   \else
     \page_sides_check_floats_reset
   \fi
   \parskip\s_spac_whitespace_parskip} % not needed

% \let\page_sides_check_floats\page_sides_check_floats_indeed

\let\page_sides_check_floats\relax

\def\page_sides_initialize_checker
  {\ifrelax\page_sides_check_floats
     \glet\page_sides_check_floats\page_sides_check_floats_indeed
     \clf_enablesidefloatchecker
     \glet\page_sides_initialize_checker\relax
   \fi}

\protected\def\page_sides_check_floats_tracer
  {\begingroup
   \dontleavehmode
   \ifnum\c_page_sides_float_type>\plusfour
     \rlap
       {\hskip\availablehsize % d_page_sides_width % kern
        \color[trace:o]%
          {\rlap{\kern.25\bodyfontsize\showstruts\strut}%
           \vrule\s!height.5\points\s!depth.5\points\s!width\d_page_sides_width}}%
   \else
     \hskip-\d_page_sides_width % kern
     \color[trace:o]%
       {\vrule\s!height.5\points\s!depth.5\points\s!width\d_page_sides_width
        \llap{\showstruts\strut\kern.25\bodyfontsize}}%
   \fi
   \endgroup}

% tricky test:

% \starttext
%     \dorecurse{33}{\line{#1}}
%     \placefigure[left]{}{}
%     \input ward
%     \startitemize
%         \item word \item word \item word \item word
%     \stopitemize
%     \input ward
%     \page
%     \placefigure[left]{}{}
%     \dontleavehmode \begingroup \input ward \par \endgroup
%     \dontleavehmode \begingroup \input ward \par \endgroup
%     \dontleavehmode \begingroup \input ward \par \endgroup
%     \input ward
% \stoptext

\protected\def\page_sides_check_floats_set_shape
  {\privatescratchtoks\emptytoks
   \privatescratchcounter\c_page_sides_n_of_lines
   \privatescratchdimen{\hsize-\d_page_sides_width}%
   \dorecurse\c_page_sides_n_of_lines
     {\toksapp\privatescratchtoks{\zeropoint\hsize}}%
   \ifnum\c_page_sides_n_of_hang>\c_page_sides_n_of_lines
     \advanceby\c_page_sides_n_of_hang -\c_page_sides_n_of_lines\relax
     \advanceby\privatescratchcounter\c_page_sides_n_of_hang
     \dorecurse\c_page_sides_n_of_hang % weird, shouldn't that be scratchcounter
       {\ifnum\c_page_sides_float_type>\plusfour
          \toksapp\privatescratchtoks{\zeropoint\privatescratchdimen}%
        \else
          \toksapp\privatescratchtoks{\d_page_sides_width\privatescratchdimen}%
        \fi}%
   \fi
   \parshape
     \numexpr\privatescratchcounter+\plusone\relax
     \the\privatescratchtoks
     \zeropoint \hsize
   \relax}

\protected\def\page_sides_check_floats_set_hang
  {\hangindent \ifnum\c_page_sides_float_type>\plusfour -\fi\d_page_sides_width
   \hangafter-\c_page_sides_n_of_hang
   \global\c_page_sides_hangafter\hangafter}

\protected\def\page_sides_check_floats_set
  {\edef\p_sidethreshold{\floatparameter\c!sidethreshold}%
   \ifconditional\c_page_sides_delayed
     % For Alan's hanging right float that moved to the next page.
     \d_page_sides_progress\zeropoint
   \fi
   \ifx\p_sidethreshold\v!old
     \d_page_sides_progression{\d_page_sides_progress+\strutht-\roundingeps}%
     \c_page_sides_n_of_hang\d_page_sides_progression
     \divideby\c_page_sides_n_of_hang \baselineskip\relax
   \else
     \d_page_sides_progression
       \ifempty\p_sidethreshold
         \d_page_sides_progress
       \else
         {\d_page_sides_progress-\p_sidethreshold}%
       \fi
     \getnoflines\d_page_sides_progression
     % this can be an option
     \ifdim{\noflines\lineheight}>{\pagegoal-\pagetotal}%
       \getrawnoflines\d_page_sides_progression
     \fi
     %
     \c_page_sides_n_of_hang\noflines
   \fi
   % indeed we can have a negative number of lines in which case we end up
   % here again later
   \global\c_page_sides_hangafter\zerocount
   \ifnum\c_page_sides_n_of_hang>\zerocount
     \ifcase\c_page_sides_n_of_lines
     \else
       \ifcase\c_page_sides_lines_done
         \global\c_page_sides_lines_done\c_page_sides_n_of_hang
       \else
         \privatescratchcounter\c_page_sides_lines_done
         \advanceby\privatescratchcounter-\c_page_sides_n_of_hang
         \global\advanceby\c_page_sides_n_of_lines-\privatescratchcounter
       \fi
     \fi
     \ifnum\c_page_sides_n_of_lines>\zerocount
       \page_sides_check_floats_set_shape
     \orelse\ifconditional\c_page_sides_force_shape
       \page_sides_check_floats_set_shape
     \else
       \page_sides_check_floats_set_hang
     \fi
   \fi
   \global\advanceby\c_page_sides_checks_done \plusone
   \iftracesidefloats
     \page_sides_check_floats_tracer
   \fi}

\protected\def\page_sides_check_floats_reset
  {\ifcase\c_page_sides_checks_done\else
     \ifcase\c_page_sides_hangafter\else
       % we need to deal with par's ending in a group which would restore
       % hang parameters
       \global\c_page_sides_hangafter\zerocount
       \hangindent\zeropoint
     \fi
   % \global % no, otherwise a next hangindent won't work
     \c_page_sides_checks_done\zerocount
   \fi}

\protected\def\page_sides_synchronize_floats
  {\ifinner \else
     \page_sides_check_floats
   \fi}

\protected\def\page_sides_check_previous_float
  {\page_sides_analyse_progress
   \ifdim\d_page_sides_progress>\zeropoint \relax
     \ifconditional\c_page_sides_short
       \global\c_page_sides_short\conditionalfalse
       \page_otr_fill_and_eject_page
     \else
       \kern\d_page_sides_progress
     \fi
   \fi}

% \def\adjustsidefloatdisplaylines % public, will change
%   {\aftergroup\page_sides_adjust_display_lines}
%
% \def\page_sides_adjust_display_lines
%   {\par
%    \noindent
%    \ignorespaces}

%D We need to hook it into the other otr's. This code will be adapted once we rename
%D the callers. We use \type {\def} as they can be redefined! Some will become obsolete

\permanent\protected\def\checksidefloat         {\page_sides_check_floats}
\permanent\protected\def\flushsidefloats        {\page_sides_flush_floats_text}
\permanent\protected\def\flushsidefloatsafterpar{\page_sides_flush_floats_after_par}
\permanent\protected\def\forgetsidefloats       {\page_sides_forget_floats}
%permanent\protected\def\synchronizesidefloats  {\page_sides_synchronize_floats}

\protect \endinput