%D \module
%D   [       file=pack-com, % used to be in core-mis,
%D        version=20120111,
%D          title=\CONTEXT\ Packing Macros,
%D       subtitle=Combinations,
%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 Packaging Macros / Combinations}

\unprotect

% \startfloatcombination will be redone ... we can decouple the floatcontent
% and caption and pass them to combinations so that we get better fit when the
% caption is wider than the float, testcase:
%
% \startfloatcombination [2*2]
%    \placefigure[local]{alpha}{\externalfigure[cow.pdf][width=1cm]}%
%    \placefigure[local]{beta} {\externalfigure[cow.pdf][width=2cm]}%
%    \placefigure[local]{gamma}{\externalfigure[cow.pdf][width=3cm]}
%    \placefigure[local]{delta}{\externalfigure[cow.pdf][width=4cm]}
% \stopfloatcombination

%D We could of course map combinations onto one of the table mechanisms but as it
%D has served us well for ages we keep this one. The code has been cleaned up a bit
%D and mkiv'd.
%D
%D Okay ... I might luafy this one eventually.

% \startcombination      {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2*1] {alpha} {a} {beta} {b} \stopcombination
% \startcombination[1*2] {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2]   {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2]   \combination {alpha} {a} \combination{beta} {b} \stopcombination

%D We do support some structure but the order matters and currently it's only window
%D dressing:

%D \starttyping
%D \let\startcontent\bgroup
%D \let\stopcontent \egroup
%D \let\startcaption\bgroup
%D \let\stopcaption \egroup
%D \stoptyping
%D
%D Of course we should have started with more structure as it would simply the code.
%D
%D \starttyping
%D \startcombination
%D     \startcontent
%D         \externalfigure[cow]
%D     \stopcontent
%D     \startcaption
%D         Some cow.
%D     \stopcaption
%D     \startcontent
%D         \externalfigure[cow]
%D     \stopcontent
%D     \startcaption
%D         The same cow.
%D     \stopcaption
%D \stopcombination
%D \stoptyping

\ifdefined\dotagcombination     \else \aliased\let\dotagcombination    \relax \fi
\ifdefined\dotagcombinationpair \else \aliased\let\dotagcombinationpair\relax \fi

\newsystemmode{combination}

\appendtoks
    \globalresetsystemmode{combination}%
\to \everyinsidefloat

\newinteger  \c_pack_combinations_nesting        % local

\newinteger  \c_pack_combinations_x              % global (counts down)
\newinteger  \c_pack_combinations_y              % global (also abused as total)
\newinteger  \c_pack_combinations_r              % global (row)
\newinteger  \c_pack_combinations_c              % global (column)
\newinteger  \c_pack_combinations_x_saved
\newinteger  \c_pack_combinations_y_saved
\newinteger  \c_pack_combinations_r_saved
\newinteger  \c_pack_combinations_c_saved
\newinteger  \c_pack_combinations_max            % global
\newinteger  \c_pack_combinations_max_saved
\newdimension\d_pack_combinations_ht             % global
\newdimension\d_pack_combinations_ht_saved
\newbox      \b_pack_combinations_captions       % global % can go
\newbox      \b_pack_combinations_captions_saved
\newbox      \b_pack_combinations_temp           % global % can go
\newbox      \b_pack_combinations_temp_saved
\newbox      \b_pack_combinations_content        % local
\newbox      \b_pack_combinations_content_saved
\newbox      \b_pack_combinations_caption        % local
\newbox      \b_pack_combinations_caption_saved

\installcorenamespace{combination}

\installcommandhandler \??combination {combination} \??combination

\initializeboxstack{\??combination captions}
\initializeboxstack{\??combination temp}

\c_strc_constructions_define_commands\conditionalfalse

% use push and pop value

\def\pack_combinations_push
  {\advanceby\c_pack_combinations_nesting\plusone
   \ifnum\c_pack_combinations_nesting>\plusone
     \c_pack_combinations_x_saved  \c_pack_combinations_x
     \c_pack_combinations_y_saved  \c_pack_combinations_y
     \c_pack_combinations_r_saved  \c_pack_combinations_r
     \c_pack_combinations_c_saved  \c_pack_combinations_c
     \c_pack_combinations_max_saved\c_pack_combinations_max
     \d_pack_combinations_ht_saved \d_pack_combinations_ht
     \setbox\b_pack_combinations_captions_saved\box\b_pack_combinations_captions
     \setbox\b_pack_combinations_temp_saved    \box\b_pack_combinations_temp
     \setbox\b_pack_combinations_content_saved \box\b_pack_combinations_content
     \setbox\b_pack_combinations_caption_saved \box\b_pack_combinations_caption
   \else
     \globalsetsystemmode{combination}% why global
   \fi}

\def\pack_combinations_pop
  {\ifnum\c_pack_combinations_nesting>\plusone
     \global\c_pack_combinations_x  \c_pack_combinations_x_saved
     \global\c_pack_combinations_y  \c_pack_combinations_y_saved
     \global\c_pack_combinations_r  \c_pack_combinations_r_saved
     \global\c_pack_combinations_c  \c_pack_combinations_c_saved
     \global\c_pack_combinations_max\c_pack_combinations_max_saved
     \global\d_pack_combinations_ht \d_pack_combinations_ht_saved
     \global\setbox\b_pack_combinations_captions\box\b_pack_combinations_captions_saved
     \global\setbox\b_pack_combinations_temp    \box\b_pack_combinations_temp_saved
     \setbox\b_pack_combinations_content        \box\b_pack_combinations_content_saved
     \setbox\b_pack_combinations_caption        \box\b_pack_combinations_caption_saved
   \else
     \globalresetsystemmode{combination}% why global
   \fi
   \advanceby\c_pack_combinations_nesting\minusone}

\definelabel
  [\v!combination] % handy for configuring
  [\c!numberconversion=\v!character,
   \c!text=]

\c_strc_constructions_define_commands\conditionaltrue

\setupcombination
  [\c!width=\v!fit,
   \c!height=\v!fit,
   \c!distance=\emwidth,
   \c!location=\v!bottom, % can be something {top,left}
   \c!before=\blank,
   \c!after=,
   \c!inbetween={\blank[\v!medium]},
  %\c!style=,
  %\c!color=,
   \c!nx=2, % new
   \c!ny=1, % new
   \c!align=\v!middle]

\let\setupcombinations\setupcombination % for the moment (we might distinguish)

\installcorenamespace{combinationlocation}
\installcorenamespace{combinationalternative}

\appendtoks
    \c_strc_constructions_define_commands\conditionalfalse
    \expanded
      {\definelabel
         [\v!combination:\currentcombination]%
         [\v!combination\ifempty\currentcombinationparent\else:\currentcombinationparent\fi]}%
         [\s!counter=\currentcombination,\c!levels=1]%
    \c_strc_constructions_define_commands\conditionaltrue
\to \everydefinecombination

\let\m_pack_combinations_leftfiller \relax
\let\m_pack_combinations_rightfiller\relax
\let\m_pack_combinations_valigner   \relax

\defcsname\??combinationlocation\v!left  \endcsname{\let\m_pack_combinations_leftfiller \relax}
\defcsname\??combinationlocation\v!right \endcsname{\let\m_pack_combinations_rightfiller\relax}
\defcsname\??combinationlocation\v!top   \endcsname{\let\m_pack_combinations_valigner   \depthonlybox}
\defcsname\??combinationlocation\v!middle\endcsname{\let\m_pack_combinations_valigner   \halfwaybox}

\def\pack_combinations_location_reset
  {\let\m_pack_combinations_rightfiller\hfil
   \let\m_pack_combinations_leftfiller \hfil
   \let\m_pack_combinations_valigner   \firstofoneargument}

\pack_combinations_location_reset

\def\pack_combinations_location_step#1%
  {\csname\??combinationlocation#1\endcsname}

% formally ok:
%
% \protected\def\stopcombination
%   {\egroup
%    \egroup}
%
% more robust:
%
% \protected\def\stopcombination
%   {{}{}{}{}{}{}{}{}% catches (at most 4) missing entries
%    \egroup
%    \egroup}
%
% even better:
%
% \protected\def\stopcombination
%   {\bgroup
%    \scratchtoks{{}}%
%    \dorecurse\c_pack_combinations_y
%      {\toksapp{{}{}}}%
%    \expandafter\egroup\the\scratchtoks
%    \egroup
%    \dostoptagged
%    \egroup}
%
% faster

\ifdefined\startcontent \else \aliased\let\startcontent\relax \fi
\ifdefined\stopcontent  \else \aliased\let\stopcontent \relax \fi
\ifdefined\startcaption \else \aliased\let\startcaption\relax \fi
\ifdefined\stopcaption  \else \aliased\let\stopcaption \relax \fi

\protected\def\pack_common_content_start{\bgroup\ignorespaces}
\protected\def\pack_common_content_stop {\removeunwantedspaces\egroup}
\protected\def\pack_common_caption_start{\bgroup\ignorespaces}
\protected\def\pack_common_caption_stop {\removeunwantedspaces\egroup}

\newtoks\everycombination

\aliased\lettonothing\combination

\lettonothing\p_nx_ny

\installcorenamespace{combinationmethod}

% \defcsname\??combinationmethod:\v!start\endcsname
%   {}
%
% \defcsname\??combinationmethod\endcsname
%   {\vbox}
%
% \defcsname\??combinationmethod:\v!stop\endcsname
%   {}

\permanent\tolerant\protected\def\startcombination[#S#1]#*[#S#2]% can be simplified
  {\bgroup % so we can grab a group
   \pack_combinations_push
   \cdef\currentcombination{#1}%
   \edef\p_nx_ny{#2}%
   %
   \ifempty\p_nx_ny
     \ifhastok={#1}%
       \lettonothing\currentcombination
       \setupcurrentcombination[#1]%
       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
     \orelse\ifhastok*\currentcombination
       \edef\p_nx_ny{\currentcombination*\plusone*}%
       \lettonothing\currentcombination
     \orelse\ifchknum\currentcombination\or
       \edef\p_nx_ny{\currentcombination*\plusone*}%
       \lettonothing\currentcombination
     \else
       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
     \fi
   \orelse\ifhastok={#2}%
     \setupcurrentcombination[#2]%
     \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
   \else
     \edef\p_nx_ny{\p_nx_ny*\plusone*}%
   \fi
   \begincsname\??combinationmethod\combinationparameter\c!method:\v!start\endcsname
   %
% test first:
%
%    \ifempty\p_nx_ny
%      \ifhastok={#1}%
%        \lettonothing\currentcombination
%        \setupcurrentcombination[#1]%
%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
%      \orelse\ifhastok*{\currentcombination}%
%        \edef\p_nx_ny{\currentcombination*\plusone*}%
%        \lettonothing\currentcombination
%      \orelse\ifchknum\currentcombination\or
%        \edef\p_nx_ny{\currentcombination*\plusone*}%
%        \lettonothing\currentcombination
%      \else
%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
%      \fi
%    \orelse\ifhastok={#2}%
%      \setupcurrentcombination[#2]%
%      \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
%    \else
%      \edef\p_nx_ny{\p_nx_ny*\plusone*}%
%    \fi
   %
   \forgetall
   %
   \expand\everycombination
   %
   \enforced\let\startcontent\pack_common_content_start
   \enforced\let\stopcontent \pack_common_content_stop
   \enforced\let\startcaption\pack_common_caption_start
   \enforced\let\stopcaption \pack_common_caption_stop
   %
   \edef\p_height  {\combinationparameter\c!height}%
   \edef\p_width   {\combinationparameter\c!width}%
   \edef\p_location{\combinationparameter\c!location}%
   \edef\p_distance{\combinationparameter\c!distance}%
   %
   \pack_combinations_location_reset
   \rawprocesscommacommand[\p_location]\pack_combinations_location_step
   %
   \dostarttaggedchained\t!combination\currentcombination\currentcombination\??combination
%    \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
   \ifcsname\??combinationmethod\combinationparameter\c!method\endcsname
     \lastnamedcs\else\vbox
   \fi\ifx\p_height\v!fit\else to {\p_height} \fi \bgroup
   \enforced\lettonothing\combination % permits \combination{}{} handy for cld
   \expanded{\pack_combinations_start_indeed[\p_nx_ny]}}

\permanent\protected\def\stopcombination
  {\bgroup\expanded{\egroup{}\ntimes{{}{}}\c_pack_combinations_y}% brr
   \dostoptagged
   \egroup
   \begincsname\??combinationmethod\combinationparameter\c!method:\v!stop\endcsname
   \pack_combinations_pop
   \egroup}

\let\pack_combinations_check_x_y\relax

\aliased\let\combinationwidth\!!zeropoint

\protected\def\pack_combinations_start_indeed[#1*#2*#3]%
  {\global\c_pack_combinations_x#1\relax
   \global\c_pack_combinations_y#2\relax
   \setexpandedcombinationparameter\c!nx{\the\c_pack_combinations_x}% in case we access it
   \setexpandedcombinationparameter\c!ny{\the\c_pack_combinations_y}% in case we access it
   \pack_combinations_check_x_y
   \dotagcombination
   \global\setbox\b_pack_combinations_captions\emptybox
   \global\c_pack_combinations_max\c_pack_combinations_x
   \multiplyby\c_pack_combinations_y\c_pack_combinations_x
   \tabskip\zeroskip
   \enforced\permanent\protected\edef\combinationwidth % \immutable
     {\todimension{%
        (\hsize-\numexpr\c_pack_combinations_x-\plusone\relax\dimexpr\combinationparameter\c!distance\relax)/\c_pack_combinations_x
      }}%
   \global\c_pack_combinations_r\zerocount
   \global\c_pack_combinations_c\zerocount
   \halign         \ifx\p_width\v!fit\else to {\p_width} \fi \bgroup % repetitive preamble
 % \halign noskips \ifx\p_width\v!fit\else to {\p_width} \fi \bgroup % repetitive preamble
   \aligntab
   \m_pack_combinations_leftfiller
   \aligncontent
   \m_pack_combinations_rightfiller
   \aligntab
   \tabskip\zeropoint \s!plus 1fill % \fillskip
   \aligncontent
   \cr
   \pack_combinations_pickup}

%D I've first considered using a constructor directly but it's more overhead and
%D some settings conflict with already used combination settings so instead we plug
%D in labels. This also permits extensions later on.

\lettonothing\p_pack_combinations_alternative

\appendtoks
    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
\to \everydefinecombination

\def\pack_combinations_pickup
  {\ifnum\c_pack_combinations_x=\c_pack_combinations_max
      \global\advance\c_pack_combinations_r\plusone
      \global        \c_pack_combinations_c\plusone
   \else
      \global\advance\c_pack_combinations_c\plusone
   \fi
   \dostarttaggednodetail\t!combinationpair     % better make this text
   \dotagcombinationpair\c_pack_combinations_c\c_pack_combinations_r % for tracing
   \dostarttaggednodetail\t!combinationcontent
   \expandafterpars\pack_combinations_pickup_content_indeed}

\def\pack_combinations_pickup_content_indeed
  {\dowithnextboxcs\pack_combinations_pickup_content\hbox}

\def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
  {\dostoptagged
   \setbox\b_pack_combinations_content\box\nextbox
   \dostarttaggednodetail\t!combinationcaption
   \expandnamespacemacro\??combinationalternative\p_pack_combinations_alternative\v!text}

\defcsname\??combinationalternative\v!text\endcsname
  {\expandafterpars\pack_combinations_alternative_text_indeed}

\defcsname\??combinationalternative\v!label\endcsname
  {\expandafterpars\pack_combinations_alternative_label_indeed}

\def\pack_combinations_alternative_text_indeed
  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
     \afterassignment\pack_combinations_caption_first
     \let\nexttoken=}

\def\pack_combinations_alternative_label_indeed
  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
     \hsize\wd\b_pack_combinations_content
     \usealignparameter\combinationparameter
     \usecombinationstyleandcolor\c!style\c!color
     \begstrut
       \expanded{\strc_labels_command[\v!combination\ifempty\currentcombination\else:\currentcombination\fi]}%
     \endstrut
   \egroup}

\appendtoks
    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
    \ifx\p_pack_combinations_alternative\v!label
      \ifcstok{\combinationparameter\c!continue}\v!yes\else
        \expanded{\strc_labels_reset{\v!combination\ifempty\currentcombination\else:\currentcombination\fi}{1}}%
     \fi
    \fi
\to \everycombination

\def\pack_combinations_pickup_caption
  {\dostoptagged
   \dostoptagged
   \setbox\b_pack_combinations_caption\box\nextbox
   \pack_combinations_pickup_package_pair}

\def\pack_combinations_caption_first
  {\futurelet\nexttoken\pack_combinations_caption_second}

\def\pack_combinations_caption_second
  {\ifx\nexttoken\egroup
     % the caption is empty
   \orelse\ifx\nexttoken\stopcaption % language
     % the caption is empty (new per 2014-05-24)
   \orelse\ifx\nexttoken\pack_combinations_float_hack_b
     % make sure we honor empty captions (new per 2023-04-20)
   \else
     % todo: \p_pack_combinations_alternative\v!none: no style, strut etc
     \hsize\wd\b_pack_combinations_content
     \usealignparameter\combinationparameter
     \usecombinationstyleandcolor\c!style\c!color
     \bgroup
     \ifcstok{\combinationparameter\c!strut}\v!no
       % make sure we have empty captions (new per 2023-04-20)
       \aftergroup\egroup
     \else
       \aftergroup\endstrut
       \aftergroup\egroup
       \begstrut
     \fi
   \fi}

\def\pack_combinations_pickup_package_pair % we need to store the caption row
  {\vbox
     {\forgetall
      \m_pack_combinations_valigner{\box\b_pack_combinations_content}%
      % we need to save the caption for a next alignment line
      \pack_combinations_save_caption}%
   \ifnum\c_pack_combinations_y>\plusone
     \global\advanceby\c_pack_combinations_y\minusone
     \global\advanceby\c_pack_combinations_x\minusone
     \ifcase\c_pack_combinations_x
       \doubleexpandafter\pack_combinations_pickup_package_pair_a
     \else
       \doubleexpandafter\pack_combinations_pickup_package_pair_b
     \fi
   \else
     \singleexpandafter\pack_combinations_pickup_package_pair_c
   \fi}

\def\pack_combinations_pickup_package_pair_a
  {\cr
   \pack_combinations_flush_captions
   \noalign
     {\forgetall
      \global\setbox\b_pack_combinations_captions\emptybox
      \nointerlineskip
      \combinationparameter\c!after
      \combinationparameter\c!before
      \vss
      \nointerlineskip}%
   \global\c_pack_combinations_x\c_pack_combinations_max
   \pack_combinations_pickup}

\def\pack_combinations_pickup_package_pair_b
  {\aligntab
   \aligntab
   \aligntab
   \kern{\p_distance}%
   \aligntab
   \pack_combinations_pickup}

\def\pack_combinations_pickup_package_pair_c
  {\cr
   \pack_combinations_flush_captions
   \egroup}

\installcorenamespace{combinationcaption}

\def\pack_combinations_save_caption
  {\ifdim\htdp\b_pack_combinations_caption>\d_pack_combinations_ht
     \global\d_pack_combinations_ht\htdp\b_pack_combinations_caption
   \fi
   \savebox{\??combinationcaption:\the\c_pack_combinations_nesting}{\the\c_pack_combinations_x}{\box\b_pack_combinations_caption}}

\let\pack_combinations_flush_captions_indeed\relax

\def\pack_combinations_flush_captions
  {\noalign
     {\ifdim\d_pack_combinations_ht>\zeropoint
        \nointerlineskip % indeed
        \combinationparameter\c!inbetween
        \global\c_pack_combinations_x\c_pack_combinations_max
        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_yes
      \else
        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_nop
      \fi}%
   \pack_combinations_flush_captions_indeed
   \crcr}

\def\pack_combinations_flush_captions_yes
  {\vpack to \d_pack_combinations_ht\bgroup
     \foundbox{\??combinationcaption:\the\c_pack_combinations_nesting}{\the\c_pack_combinations_x}%
     \vss
   \egroup
   \global\advanceby\c_pack_combinations_x\minusone
   \ifnum\c_pack_combinations_x>\zerocount % \c_pack_combinations_max
     \expandafter\pack_combinations_flush_captions_yes_followup
   \else
     \global\d_pack_combinations_ht\zeropoint
     \initializeboxstack{\??combinationcaption:\number-\c_pack_combinations_nesting}%
   \fi}

\let\pack_combinations_flush_captions_nop\donothing

\def\pack_combinations_flush_captions_yes_followup
  {\aligntab
   \aligntab
   \aligntab
   \aligntab
   \pack_combinations_flush_captions_indeed}

%D \macros
%D   {startfloatcombination}
%D
%D \setupexternalfigures[directory={../sample}]
%D \startbuffer
%D \placefigure
%D   [left,none]
%D   {}
%D   {\startfloatcombination[2*2]
%D      \placefigure{alpha}{\externalfigure[cow.pdf][width=1cm]}
%D      \placefigure{beta} {\externalfigure[cow.pdf][width=2cm]}
%D      \placefigure{gamma}{\externalfigure[cow.pdf][width=3cm]}
%D      \placefigure{delta}{\externalfigure[cow.pdf][width=4cm]}
%D    \stopfloatcombination}
%D
%D \input tufte
%D \stopbuffer
%D
%D \typebuffer \getbuffer

\definecombination
  [\v!float]
% [\c!strut=\v!no] % not needed, we intercept anyway

\protected\def\pack_combinations_float_hack_a#1%
  {\strc_floats_build_box_separate_split{#1}%
   \box\b_strc_floats_separate_content}

\protected\def\pack_combinations_float_hack_b#1%
  {\box\b_strc_floats_separate_caption}

\permanent\tolerant\protected\def\startfloatcombination[#1]#*[#2]%
  {\ifinsidefloat\else\dontleavehmode\fi % tricky, floatcombinations fail to align well otherwise
   \vbox\bgroup
   \strc_floats_build_box_separate_set
  %\insidecolumnstrue % trick, forces no centering, todo: proper switch/feature
   \postcenterfloatmethod\zerocount
   \forcelocalfloats
   \enforced\permanent\protected\def\stopfloatcombination{\pack_combinations_stop_float{#1}}}

\aliased\let\stopfloatcombination\relax

\def\pack_combinations_float_check_x_y
  {\ifnum\numexpr\c_pack_combinations_x*\c_pack_combinations_y\relax<\noflocalfloats\relax
       \global\c_pack_combinations_x\noflocalfloats
       \global\c_pack_combinations_y\plusone
   \fi
   \let\pack_combinations_check_x_y\relax}%

\def\pack_combinations_stop_float#1%
  {\scratchtoks\emptytoks
   \dorecurse\noflocalfloats
     {\appendetoks
        {\pack_combinations_float_hack_a{\recurselevel}}%
        {\pack_combinations_float_hack_b{\recurselevel}}%
      \to\scratchtoks}% brrr
   \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y
 % \expanded{\startcombination[#1]\the\scratchtoks}\stopcombination
   \expanded{\startcombination[\v!float][#1]\the\scratchtoks}\stopcombination
   \resetlocalfloats
   \egroup}

%D \macros
%D   {definepairedbox, setuppairedbox, placepairedbox}
%D
%D Paired boxes, formally called legends, but from now on a legend is just an
%D instance, are primarily meant for typesetting some text alongside an
%D illustration. Although there is quite some variation possible, the functionality
%D is kept simple, if only because in most cases such pairs are typeset sober.
%D
%D The location specification accepts a pair, where the first keyword specifies the
%D arrangement, and the second one the alignment. The first key of the location pair
%D is one of \type {left}, \type {right}, \type {top} or \type {bottom}, while the
%D second key can also be \type {middle}.
%D
%D The first box is just collected in an horizontal box, but the second one is a
%D vertical box that gets passed the bodyfont and alignment settings.
%D
%D In many cases the table builders can be used instead, but as this mechanism is a
%D traditional \CONTEXT\ one we keep it around.

%D \macros
%D   {setuplegend, placelegend}
%D
%D It makes sense to typeset a legend to a figure in \TEX\ and not in a drawing
%D package. The macro \type {\placelegend} combines a figure (or something else) and
%D its legend. This command is just a paired box.
%D
%D The legend is placed according to \type {location}, being \type {bottom} or \type
%D {right}. The macro macro is used as follows.
%D
%D \starttyping
%D \placefigure
%D   {whow}
%D   {\placelegend
%D      {\externalfigure[cow]}
%D      {\starttabulate
%D       \NC 1 \NC head \NC \NR
%D       \NC 2 \NC legs \NC \NR
%D       \NC 3 \NC tail \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend
%D      {\externalfigure[cow]}
%D      {\starttabulate[|l|l|l|l|]
%D       \NC 1 \NC head \NC 3 \NC tail \NC \NR
%D       \NC 2 \NC legs \NC   \NC      \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {\starttabulate
%D       \NC 1 \NC head \NC \NR
%D       \NC 2 \NC legs \NC \NR
%D       \NC 3 \NC tail \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {head \par legs \par tail}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {\startitemize[packed]
%D       \item head \item legs \item  tail \item belly \item horns
%D       \stopitemize}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2,width=.8\hsize]
%D      {\externalfigure[cow]}
%D      {\startitemize[packed]
%D       \item head \item legs \item  tail \item belly \item horns
%D       \stopitemize}}
%D
%D \def\MytTestTwo#1#2%
%D   {\placefigure
%D      {whow}
%D      {\placelegend[location={#1,#2}]
%D         {\externalfigure[cow]}
%D         {\starttabulate
%D          \NC 1 \NC head \NC \NR
%D          \NC 2 \NC legs \NC \NR
%D          \NC 3 \NC tail \NC \NR
%D          \stoptabulate}}}
%D
%D \def\MytTestOne#1{\processcommalist[left,right,top,bottom]{\MytTestTwo{#1}}}
%D
%D \processcommalist[left,right,top,bottom,middle]\MytTestOne
%D \stoptyping
%D
%D More structure is also possible (the order matters!):
%D
%D \starttyping
%D \startplacefigure[title=whow]
%D    \startplacelegend[location={bottom,middle},color=red]
%D         \startcontent
%D             \externalfigure[cow]
%D         \stopcontent
%D         \startcaption
%D             \starttabulate[|l|l|]
%D                 \NC 1 \NC head \NC \NR
%D                 \NC 2 \NC legs \NC \NR
%D                 \NC 3 \NC tail \NC \NR
%D             \stoptabulate
%D         \stopcaption
%D    \stopplacelegend
%D \stopplacefigure
%D \stoptyping

% A few new features per April 9, 2026 ... probably some more later (MS/HH).
%
% \definepairedbox
%   [mylegend]
%   [location={right,top},
%    width=\hsize,
%    alternative=vertical,
%    align={normal,snapping},
%    maxwidth=1tw,
%    distance=fit]
%
% \setupfloat
%   [intermezzo]
%   [location=left]
%
% \startbuffer
% \startMPcode
%   draw fullcircle scaled 3cm ;
%   fill unitsquare scaled 2cm withcolor darkblue ;
% \stopMPcode
% \stopbuffer
%
% \startplaceintermezzo[location=none]
%   \placesidebyside
%   % {\unframed [offset=overlay,align=normal] {\typebuffer[before=,after=]}}
%     {\typebuffer}
%     {\getbuffer}
% \stopplaceintermezzo
%
% \startplaceintermezzo[location=none]
%   \placesidebyside
%     {\unframed [offset=overlay,align=normal,width=.5\hsize] {\typebuffer[before=,after=]}}
%     {\getbuffer}
% \stopplaceintermezzo
%
% \startplaceintermezzo[location=none]
%   \startplacemylegend
%   % {\unframed [offset=overlay,align=normal] {\typebuffer[before=,after=]}}
%   % {\typebuffer[before=,after=]}
%     {\typebuffer}
%     {\getbuffer}
%   \stopplacemylegend
% \stopplaceintermezzo
%
% \startbuffer
% \startMPcode
%   draw function(2,"x","sin(x)",0,4*pi,0.01)
%     xsized (.2TextWidth)
%     withpen pencircle scaled 3
%     withcolor darkred ;
% \stopMPcode
% \stopbuffer
%
% \startplaceintermezzo[location=none]
%   \startplacemylegend
%     {\unframed [offset=overlay,align={normal,snapping}] {\typebuffer[before=,after=]}}
%   % {\typebuffer}
%     {\getbuffer}
%   \stopplacemylegend
% \stopplaceintermezzo
%
% \startplaceintermezzo[location=none]
%   \startplacemylegend
%   % {\hsize7cm \samplefile{ward}}
%     {\typebuffer}
%     {\getbuffer}
%   \stopplacemylegend
% \stopplaceintermezzo

\newsystemmode{pairedbox}

\appendtoks
    \globalresetsystemmode{pairedbox}%
\to \everyinsidefloat

\installcorenamespace {pairedbox}

\installcommandhandler \??pairedbox {pairedbox} \??pairedbox

\setuppairedbox
 [\c!n=1,
  \c!distance=\bodyfontsize, % \v!fit
 %\c!before=,
 %\c!after=,
 %\c!color=,
 %\c!style=,
  \c!inbetween={\blank[\v!medium]},
  \c!width=\hsize,
  \c!height=\vsize,
  \c!maxwidth=\textwidth,   % \makeupwidth,
  \c!maxheight=\textheight, % \makeupheight,
 %\c!bodyfont=,
 %\c!align=,
 %\c!alternative=\v!horizontal,
  \c!location=\v!bottom]

% watch the hsize/vsize tricks

\newbox      \b_pack_pairedboxes_first
\newbox      \b_pack_pairedboxes_second
\newdimension\d_pack_pairedboxes_size

\appendtoks
   \protected\frozen\instance\edefcsname\e!setup\currentpairedbox\e!endsetup\endcsname{\setuppairedbox     [\currentpairedbox]}%
   \protected\frozen\instance\edefcsname\e!place\currentpairedbox           \endcsname{\placepairedbox     [\currentpairedbox]}% one argument is mandate anyway
   \protected\frozen\instance\edefcsname\e!start\e!place\currentpairedbox   \endcsname{\startplacepairedbox[\currentpairedbox]}% one argument is mandate anyway
   \protected\frozen\instance\edefcsname\e!stop \e!place\currentpairedbox   \endcsname{\stopplacepairedbox                    }%
\to \everydefinepairedbox

\permanent\tolerant\protected\def\startplacepairedbox[#1]#*[#S#2]%
  {\bgroup
   \cdef\currentpairedbox{#1}%
   \setupcurrentpairedbox[#2]%
   \pairedboxparameter\c!before
   \bgroup
   \edef\p_location{\pairedboxparameter\c!location}%
   \edef\p_n       {\pairedboxparameter\c!n}%
   %
   \enforced\let\startcontent\pack_common_content_start
   \enforced\let\stopcontent \pack_common_content_stop
   \enforced\let\startcaption\pack_common_caption_start
   \enforced\let\stopcaption \pack_common_caption_stop
   %
   \globalsetsystemmode{pairedbox}%
   \pack_pairedboxes_before
   \expandafterpars\pack_pairedboxes_first_pickup}

\permanent\protected\def\stopplacepairedbox{} % we just pick up two boxes

\aliased\let\placepairedbox\startplacepairedbox    % we just pick up two boxes

% \def\pack_pairedboxes_first_pickup
%   {\dowithnextboxcs\pack_pairedboxes_first\hbox
%      \bgroup
%      \let\next=}

\def\pack_pairedboxes_first_pickup
  {\dowithnextboxcs\pack_pairedboxes_first
     \ifcstok{\pairedboxparameter\c!alternative}\v!vertical
       \vbox \s!prune \bgroup
         \usealignparameter\pairedboxparameter
     \else
       \hbox \bgroup
     \fi
     \let\next=}

\def\pack_pairedboxes_first
  {\pack_pairedboxes_between
   \expandafterpars\pack_pairedboxes_second_pickup}

\def\pack_pairedboxes_second_pickup
  {\dowithnextboxcs\pack_pairedboxes_second\vbox
     \bgroup
     \pack_pairedboxes_inside_second
     \let\next=}

\def\pack_pairedboxes_second
  {\pack_pairedboxes_after
   \egroup
   \pairedboxparameter\c!after
   \egroup}

\newconditional\c_pack_pairedboxes_horizontal \c_pack_pairedboxes_horizontal\conditionaltrue

\installcorenamespace{pairedboxnature}
\installcorenamespace{pairedboxalign}

\let\pack_pairedboxes_flush      \relax
\let\pack_pairedboxes_fill_top   \relax
\let\pack_pairedboxes_fill_bottom\relax

\defcsname\??pairedboxnature\v!left\endcsname
  {\c_pack_pairedboxes_horizontal\conditionaltrue
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_left}

\defcsname\??pairedboxnature\v!right\endcsname
  {\c_pack_pairedboxes_horizontal\conditionaltrue
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_right}

\defcsname\??pairedboxnature\v!top\endcsname
  {\c_pack_pairedboxes_horizontal\conditionalfalse
   \let\pack_pairedboxes_fill_top\relax
   \let\pack_pairedboxes_fill_bottom\vss
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_top}

\defcsname\??pairedboxnature\v!bottom\endcsname
  {\c_pack_pairedboxes_horizontal\conditionalfalse
   \let\pack_pairedboxes_fill_top\vss
   \let\pack_pairedboxes_fill_bottom\relax
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_bottom}

\def\pack_pairedboxes_flush_distance
  {\ifzeropt\scratchdistance
     \hss
   \else
     \kern\scratchdistance
   \fi}

\def\pack_pairedboxes_flush_left
  {\box\b_pack_pairedboxes_second
   \pack_pairedboxes_flush_distance
   \box\b_pack_pairedboxes_first}

\def\pack_pairedboxes_flush_right
  {\box\b_pack_pairedboxes_first
   \pack_pairedboxes_flush_distance
   \box\b_pack_pairedboxes_second}

\def\pack_pairedboxes_flush_top
  {\box\b_pack_pairedboxes_second
   \endgraf
   \nointerlineskip
   \pairedboxparameter\c!inbetween
   \box\b_pack_pairedboxes_first}

\def\pack_pairedboxes_flush_bottom
  {\box\b_pack_pairedboxes_first
   \endgraf
   \nointerlineskip
   \pairedboxparameter\c!inbetween
   \box\b_pack_pairedboxes_second}

\let\pack_pairedboxes_align_l\relax
\let\pack_pairedboxes_align_r\relax
\let\pack_pairedboxes_align_t\relax
\let\pack_pairedboxes_align_b\relax

\defcsname\??pairedboxalign\v!left\endcsname % 0
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\hss
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\relax}

\defcsname\??pairedboxalign\v!right\endcsname % 1
  {\let\pack_pairedboxes_align_l\hss
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\relax}

\defcsname\??pairedboxalign\v!high\endcsname % 2
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\vss}

\defcsname\??pairedboxalign\v!low\endcsname % 3
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\vss
   \let\pack_pairedboxes_align_b\relax}

\defcsname\??pairedboxalign\v!middle\endcsname % 4
  {\let\pack_pairedboxes_align_l\hss
   \let\pack_pairedboxes_align_r\hss
   \let\pack_pairedboxes_align_t\vss
   \let\pack_pairedboxes_align_b\vss}

\defcsname\??pairedboxalign\v!bottom\endcsname{\csname\??pairedboxalign\v!low \endcsname}
\defcsname\??pairedboxalign   \v!top\endcsname{\csname\??pairedboxalign\v!high\endcsname}

\def\pack_pairedbox_valign#1{\setbox#1\vpack to \d_pack_pairedboxes_size{\pack_pairedboxes_align_t\box#1\pack_pairedboxes_align_b}}
\def\pack_pairedbox_halign#1{\setbox#1\hpack to \d_pack_pairedboxes_size{\pack_pairedboxes_align_l\box#1\pack_pairedboxes_align_r}}

\def\pack_pairedboxes_before
  {\ifempty\p_location
      \csname\??pairedboxnature\v!left  \endcsname
      \csname\??pairedboxalign \v!middle\endcsname
   \else
     \getfromcommacommand[\p_location][1]%
     \csname\??pairedboxnature
       \ifcsname\??pairedboxnature\commalistelement\endcsname\commalistelement\else\v!left\fi
     \endcsname
     \getfromcommacommand[\p_location][2]%
     \csname\??pairedboxalign
       \ifcsname\??pairedboxalign\commalistelement\endcsname\commalistelement\else\v!middle\fi
     \endcsname
   \fi}

\def\pack_pairedboxes_between
  {\usebodyfontparameter\pairedboxparameter
   \setbox\b_pack_pairedboxes_first\box\nextbox
   \ifconditional\c_pack_pairedboxes_horizontal
     \pack_pairedboxes_between_horizontal
   \else
     \pack_pairedboxes_between_vertical
   \fi
   \ifnum\p_n>\plusone
     \setrigidcolumnhsize\hsize{\pairedboxparameter\c!distance}\p_n
   \fi}

% \def\pack_pairedboxes_between_horizontal
%   {\scratchdistance{\pairedboxparameter\c!distance}%
%    \scratchwidth{\pairedboxparameter\c!maxwidth}%
%    \setlocalhsize
%    \hsize{\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance}%
%    \hsize{\pairedboxparameter\c!width}% can be \hsize
%    \scratchdimen{\wd\b_pack_pairedboxes_first+\scratchdistance}%
%    \ifdim{\hsize+\scratchdimen}>\scratchwidth
%      \hsize{\scratchwidth-\scratchdimen}%
%    \fi}

\def\pack_pairedboxes_between_horizontal
  {\scratchdistance
     \ifcstok{\pairedboxparameter\c!distance}\v!fit
       \zeropoint
     \else
       \lastnamedcs
     \fi
   \scratchwidth{\pairedboxparameter\c!maxwidth}%
   \setlocalhsize
   \hsize{\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance}%
   \hsize{\pairedboxparameter\c!width}% can be \hsize
   \scratchdimen{\wd\b_pack_pairedboxes_first+\scratchdistance}%
   \ifdim{\hsize+\scratchdimen}>\scratchwidth
     \hsize{\scratchwidth-\scratchdimen}%
   \fi}

\def\pack_pairedboxes_between_vertical
  {\scratchwidth{\pairedboxparameter\c!maxwidth}%
   \hsize\wd\b_pack_pairedboxes_first
   \hsize{\pairedboxparameter\c!width}% can be \hsize
   \ifdim\hsize>\scratchwidth\relax
     \hsize\scratchwidth
   \fi}

\def\pack_pairedboxes_after
  {\setbox\b_pack_pairedboxes_second\vpack
     {\ifnum\p_n>\plusone
        \rigidcolumnbalance\nextbox
      \else
        \box\nextbox
      \fi}%
   \ifconditional\c_pack_pairedboxes_horizontal
     \pack_pairedboxes_pack_horizontal
   \else
     \pack_pairedboxes_pack_vertical
   \fi}

\def\pack_pairedboxes_pack_horizontal
  {\dontleavehmode\hbox \ifzeropt\scratchwidth\else to \scratchwidth \fi\bgroup
     \forgetall
     \d_pack_pairedboxes_size\ht
       \ifdim\ht\b_pack_pairedboxes_first>\ht\b_pack_pairedboxes_second
         \b_pack_pairedboxes_first
       \else
         \b_pack_pairedboxes_second
       \fi
     \vsize\d_pack_pairedboxes_size
     \scratchheight{\pairedboxparameter\c!height}%
     \ifdim\d_pack_pairedboxes_size<\scratchheight % can be \vsize
       \d_pack_pairedboxes_size\scratchheight
     \fi
     \scratchheight{\pairedboxparameter\c!maxheight}%
     \ifdim\d_pack_pairedboxes_size>\scratchheight
       \d_pack_pairedboxes_size\scratchheight
     \fi
     \pack_pairedbox_valign\b_pack_pairedboxes_first
     \pack_pairedbox_valign\b_pack_pairedboxes_second
     \pack_pairedboxes_flush
   \egroup}

\def\pack_pairedboxes_pack_vertical
  {\dontleavehmode\vpack\bgroup
     \forgetall
     \d_pack_pairedboxes_size\wd
       \ifdim\wd\b_pack_pairedboxes_first>\wd\b_pack_pairedboxes_second
         \b_pack_pairedboxes_first
       \else
         \b_pack_pairedboxes_second
       \fi
     \pack_pairedbox_halign\b_pack_pairedboxes_first
     \pack_pairedbox_halign\b_pack_pairedboxes_second
     \d_pack_pairedboxes_size\ht\b_pack_pairedboxes_second
     \vsize\d_pack_pairedboxes_size
     \scratchheight{\pairedboxparameter\c!height}%
     \ifdim\ht\b_pack_pairedboxes_second<\scratchheight % can be \vsize
       \d_pack_pairedboxes_size\scratchheight
     \fi
     \scratchheight{\pairedboxparameter\c!maxheight}%
     \ifdim\d_pack_pairedboxes_size>\scratchheight
       \d_pack_pairedboxes_size\scratchheight
     \fi
     \ifdim\d_pack_pairedboxes_size>\ht\b_pack_pairedboxes_second
       \setbox\b_pack_pairedboxes_second\vpack to \d_pack_pairedboxes_size
         {\pack_pairedboxes_fill_top
          \box\b_pack_pairedboxes_second
          \pack_pairedboxes_fill_bottom}% \kern\zeropoint
     \fi
     \pack_pairedboxes_flush
   \egroup}

\def\pack_pairedboxes_inside_second
  {\forgetall
   \setupalign[\pairedboxparameter\c!align]%
   \usepairedboxstyleandcolor\c!style\c!color
   \tolerantTABLEbreaktrue % hm.
   \blank[\v!disable]% use fast one
   \everypar{\begstrut}} % also flushers here (see bTABLE)

\definepairedbox[\v!legend]

\permanent\protected\def\placeontopofeachother{\bgroup\dowithnextboxcs\pack_topofeachother_one\hbox}
\permanent\protected\def\placesidebyside      {\bgroup\dowithnextboxcs\pack_sidebyside_one    \hbox}

\def\pack_topofeachother_one{\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_topofeachother_two\hbox}
\def\pack_sidebyside_one    {\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_sidebyside_two    \hbox}

\def\pack_topofeachother_two{\setbox\scratchboxtwo\box\nextbox
                             \halign{\hss\aligncontent\hss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
                             \egroup\egroup}
\def\pack_sidebyside_two    {\setbox\scratchboxtwo\box\nextbox
                             \valign{\vss\aligncontent\vss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
                             \egroup\egroup}

\protect \endinput
