%D \module
%D   [       file=spac-ali,
%D        version=2009.10.16, % 1997.03.31, was core-spa.tex
%D          title=\CONTEXT\ Spacing Macros,
%D       subtitle=Alignments,
%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 Spacing Macros / Alignments}

\unprotect

%D The \type {new} and \type {old} methods are gone as we now have \type {flush*}
%D variants. Starting at the last day of 2011 both methods are merged into one and
%D caching has been added, which makes switching twice as fast.

\registerctxluafile{spac-ali}{autosuffix}

% Used once so ... replace it or use if more frequently ...

\installcorenamespace {alignswitch}

% maybe \setconstantvalue or so

\expandafter\integerdef\csname\??alignswitch\v!left  \endcsname\zerocount
\expandafter\integerdef\csname\??alignswitch\v!middle\endcsname\plusone
\expandafter\integerdef\csname\??alignswitch\v!right \endcsname\plustwo

\mutable\integerdef\alignmentswitch\zerocount

\permanent\protected\def\setalignmentswitch#1%
  {\integerdef\alignmentswitch\csname\??alignswitch\ifcsname\??alignswitch#1\endcsname#1\else\v!left\fi\endcsname}

% till here

\definesystemattribute[realign]   [public] % might be combined with the next one
\definesystemattribute[alignstate][public] % will make a single attributes for several states

\appendtoks
    \c_attr_realign   \attributeunsetvalue
    \c_attr_alignstate\attributeunsetvalue
\to \everyforgetall

\permanent\protected\def\signalinnerrealign{\clf_setrealign\plustwo}
\permanent\protected\def\signalouterrealign{\clf_setrealign\plusone}

\installcorenamespace{aligncommand}
\installcorenamespace{alignhorizontal}
\installcorenamespace{alignvertical}

% \installcorenamespace{alignmentnormalcache}
% \installcorenamespace{alignmentraggedcache}

% nasty hack:

\installcorenamespace{alignmentnormalcacheodd}
\installcorenamespace{alignmentnormalcacheeven}

\installcorenamespace{alignmentraggedcacheodd}
\installcorenamespace{alignmentraggedcacheeven}

\permanent\def\??alignmentnormalcache{\ifodd\realpageno\??alignmentnormalcacheodd\else\??alignmentnormalcacheeven\fi}
\permanent\def\??alignmentraggedcache{\ifodd\realpageno\??alignmentraggedcacheodd\else\??alignmentraggedcacheeven\fi}

\newtoks\everyresetalign % todo

% This will become a more advanced layout controller soon:

\newconditional\layoutlefttoright   \layoutlefttoright \conditionaltrue
\newconditional\displaylefttoright  \displaylefttoright\conditionaltrue
\newconditional\inlinelefttoright   \inlinelefttoright \conditionaltrue

\permanent\protected\def\lefttoright
  {\ifvmode
     \spac_directions_lefttoright_vmode
   \else
     \spac_directions_lefttoright_hmode
   \fi}

\permanent\protected\def\righttoleft
  {\ifvmode
     \spac_directions_righttoleft_vmode
   \else
     \spac_directions_righttoleft_hmode
   \fi}

\protected\def\spac_directions_lefttoright_vmode
  {\displaylefttoright\conditionaltrue
   \inlinelefttoright\conditionaltrue
   \textdirection\directionlefttoright
   \pardirection \directionlefttoright}

\protected\def\spac_directions_righttoleft_vmode
  {\displaylefttoright\conditionalfalse
   \inlinelefttoright\conditionalfalse
   \textdirection\directionrighttoleft
   \pardirection \directionrighttoleft}

\protected\def\spac_directions_lefttoright_hmode
  {\linedirection\directionlefttoright % linedir keeps subtype of skip
   \inlinelefttoright\conditionaltrue}

\protected\def\spac_directions_righttoleft_hmode
  {\linedirection\directionrighttoleft % linedir keeps subtype of skip
   \inlinelefttoright\conditionalfalse}

\permanent\protected\def\synchronizelayoutdirection
  {\ifconditional\layoutlefttoright
     \spac_directions_synchronize_lr
   \else
     \spac_directions_synchronize_rl
   \fi}

\permanent\protected\def\synchronizedisplaydirection
  {\ifconditional\displaylefttoright
     \spac_directions_synchronize_lr
   \else
     \spac_directions_synchronize_rl
   \fi}

\def\spac_directions_synchronize_lr
  {\inlinelefttoright\conditionaltrue
   \textdirection\directionlefttoright
   \pardirection \directionlefttoright}

\def\spac_directions_synchronize_rl
  {\inlinelefttoright\conditionalfalse
   \textdirection\directionrighttoleft
   \pardirection \directionrighttoleft}

\permanent\protected\def\synchronizeinlinedirection
  {% why not \linedirection here
   \textdirection\ifconditional\inlinelefttoright\directionlefttoright\else\directionrighttoleft\fi}

\permanent\protected\def\checkedlefttoright
  {\ifvmode
     \spac_directions_lefttoright_vmode
   \else
     \spac_directions_lefttoright_hmode_checked
   \fi}

\permanent\protected\def\checkedrighttoleft
  {\ifvmode
     \spac_directions_righttoleft_vmode
   \else
     \spac_directions_righttoleft_hmode_checked
   \fi}

\protected\def\spac_directions_lefttoright_hmode_checked
  {\ifconditional\inlinelefttoright\else
     \lefttoright
   \fi}

\protected\def\spac_directions_righttoleft_hmode_checked
  {\ifconditional\inlinelefttoright
     \righttoleft
   \fi}

\installcorenamespace{bidi}

\letcsname\??bidi\v!left \endcsname\checkedlefttoright \letcsname\??bidi l2r\endcsname\checkedlefttoright
\letcsname\??bidi\v!right\endcsname\checkedrighttoleft \letcsname\??bidi r2l\endcsname\checkedrighttoleft

\permanent\protected\def\usebidiparameter#1%
  {\begincsname\??bidi#1\c!bidi\endcsname}

\permanent\protected\def\showdirections
  {\dontleavehmode
   \begingroup\infofont\textdirection\directionlefttoright[\space
   layout:  \ifconditional\layoutlefttoright  l2r\else r2l\fi\space
   display: \ifconditional\displaylefttoright l2r\else r2l\fi\space
   inline:  \ifconditional\inlinelefttoright  l2r\else r2l\fi\space
   ]\endgroup}

\permanent\protected\def\righttolefthbox#1#{\reversehbox#1\bgroup\righttoleft\let\next} \aliased\let\rtlhbox\righttolefthbox
\permanent\protected\def\lefttorighthbox#1#{\naturalhbox#1\bgroup\lefttoright\let\next} \aliased\let\ltrhbox\lefttorighthbox
\permanent\protected\def\righttoleftvbox#1#{\reversevbox#1\bgroup\righttoleft\let\next} \aliased\let\rtlvbox\righttoleftvbox
\permanent\protected\def\lefttorightvbox#1#{\naturalvbox#1\bgroup\lefttoright\let\next} \aliased\let\ltrvbox\lefttorightvbox
\permanent\protected\def\righttoleftvtop#1#{\reversevtop#1\bgroup\righttoleft\let\next} \aliased\let\rtlvtop\righttoleftvtop
\permanent\protected\def\lefttorightvtop#1#{\naturalvtop#1\bgroup\lefttoright\let\next} \aliased\let\ltrvtop\lefttorightvtop

\permanent\protected\def\autodirhbox#1#{\hbox#1\bgroup\synchronizeinlinedirection\let\next}
\permanent\protected\def\autodirvbox#1#{\vbox#1\bgroup\synchronizeinlinedirection\let\next} % maybe also pardir or maybe just a \vbox
\permanent\protected\def\autodirvtop#1#{\vtop#1\bgroup\synchronizeinlinedirection\let\next} % maybe also pardir or maybe just a \vtop

\permanent\protected\def\leftorrighthbox{\ifconditional\displaylefttoright\expandafter\lefttorighthbox\else\expandafter\righttolefthbox\fi}
\permanent\protected\def\leftorrightvbox{\ifconditional\displaylefttoright\expandafter\lefttorightvbox\else\expandafter\righttoleftvbox\fi}
\permanent\protected\def\leftorrightvtop{\ifconditional\displaylefttoright\expandafter\lefttorightvtop\else\expandafter\righttoleftvtop\fi}

\permanent\protected\def\rtltext{\groupedcommand{\dontleavehmode\righttoleft\ignorespaces}\removeunwantedspaces}
\permanent\protected\def\ltrtext{\groupedcommand{\dontleavehmode\lefttoright\ignorespaces}\removeunwantedspaces}

% Tolerance and hyphenation

\ifdefined\lesshyphens \else \let\lesshyphens\relax \fi
\ifdefined\morehyphens \else \let\morehyphens\relax \fi
\ifdefined\nohyphens   \else \let\nohyphens  \relax \fi
\ifdefined\dohyphens   \else \let\dohyphens  \relax \fi

% \pretolerance 100
% \tolerance    200

\newconstant\c_spac_tolerance_default  \c_spac_tolerance_default  200
\newconstant\c_spac_tolerance_minimum  \c_spac_tolerance_minimum 1500
\newconstant\c_spac_tolerance_normal   \c_spac_tolerance_normal  3000
\newconstant\c_spac_tolerance_extreme  \c_spac_tolerance_extreme 4500

\def\spac_align_set_raggedness_left  {\plustwo\bodyfontsize}
\def\spac_align_set_raggedness_right {\plustwo\bodyfontsize}
\def\spac_align_set_raggedness_middle{\plussix\bodyfontsize} % overloaded below

% oeps, hsize can be 0pt in which case we get a strange division
% was: 6\bodyfontsize, fails on: \placefigure{x $x=x$ x}{}

\def\spac_align_set_raggedness_middle{\ifzeropt\hsize\plussix\bodyfontsize\else.5\hsize\fi}

\permanent\protected\def\setraggedness#1% tricky .. we keep the global tolerance otherwise ... to be reconsidered
  {\ifnum\tolerance<\c_spac_tolerance_minimum
     \tolerance\c_spac_tolerance_minimum % small values have unwanted side effects
   \else
     % todo: take set value or none .. better done elsewhere (200 is normal)
   \fi
   \ifrelax\dohyphens % was 2.5 in old implementation using scratch registers
     \hyphenpenalty\dimexpr2.8\hsize/\dimexpr#1\relax\relax % 50 in raggedright/raggedleft
  %\else
     % no need to do something as we're in \nohyphens
   \fi}

\protected\def\spac_align_set_tolerant
  {\tolerance\c_spac_tolerance_normal}

\protected\def\spac_align_set_very_tolerant
  {\tolerance\c_spac_tolerance_extreme}

\protected\def\spac_align_set_stretch
  {\emergencystretch\bodyfontsize}

\protected\def\spac_align_set_extreme_stretch
  {\emergencystretch10\bodyfontsize}

% Vertical

\newconstant\c_spac_align_state_vertical
\newconstant\c_spac_align_state_spread

\protected\def\spac_align_set_vertical_none
  {\enforced\let\raggedtopcommand   \relax
   \enforced\let\raggedbottomcommand\relax}

\protected\def\spac_align_set_vertical_lohi
  {\enforced\let\raggedtopcommand   \vfilll
   \enforced\let\raggedbottomcommand\vfilll}

\protected\def\spac_align_set_vertical_low
  {\enforced\let\raggedtopcommand   \vfilll
   \enforced\let\raggedbottomcommand\relax}

\protected\def\spac_align_set_vertical_high
  {\enforced\let\raggedtopcommand   \relax
   \enforced\let\raggedbottomcommand\vfilll}

\def\spac_align_flush_vertical
  {\ifcase\c_spac_align_state_vertical
     \spac_align_set_vertical_none
   \or
     \spac_align_set_vertical_lohi
   \or
     \spac_align_set_vertical_low
   \or
     \spac_align_set_vertical_high
   \fi
   \baselineskip
     1\baselineskip
     \ifcase\c_spac_align_state_spread\else
       \s!plus \onepoint
     \fi
   \relax}

% Horizontal

\ifdefined\raggedonelinerstate \else
    \newconditional\raggedonelinerstate  % public
\fi

% \appendtoks
%     \raggedonelinerstate\conditionalfalse
% \to \everyforgetall

\newconstant\raggedstatus     % public
\newconstant\raggedhangstatus % public

\newconstant\c_spac_align_state_horizontal
\newconstant\c_spac_align_state_broad
\newconstant\c_spac_align_state_par_fill

\def\v_spac_align_fill_amount          {\plusone  fil}
\def\v_spac_align_fill_amount_hard     {\plusone fill}
\def\v_spac_align_fill_amount_extreme  {\plustenthousand  filll}
\def\v_spac_align_fill_amount_negative {\minusone fil}
\def\v_spac_align_fill_amount_double   {\plustwo  fil}
\def\v_spac_align_fill_amount_space    {\plustwo  fil} % can be added to xspace if we have a key
\def\v_spac_align_fill_amount_half             {.5fil}
\let\v_spac_align_space_amount        \interwordspace
\def\v_spac_align_space_amount_x          {.5\emwidth}

\newgluespec\s_zero_plus_one_fil  \s_zero_plus_one_fil  = 0pt plus 1fil
\newgluespec\s_zero_plus_one_fill \s_zero_plus_one_fill = 0pt plus 1fill
\newgluespec\s_zero_plus_zero     \s_zero_plus_zero     = 0pt plus 0pt

% \s!plus ... slower than inline

\permanent\protected\def\spac_align_set_horizontal_none % should also relax \updateraggedskips
  {\raggedstatus     \zerocount
   \raggedhangstatus \zerocount
   \c_attr_alignstate\attributeunsetvalue
   \leftskip         \plusone\leftskip
   \rightskip        \plusone\rightskip
   \spaceskip        \zeroskip
   \xspaceskip       \zeroskip
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_one_fil % new
   \raggedonelinerstate\conditionalfalse % now here
   \enforced\aliased\let\updateraggedskips\relax}          % no need for adaption

\permanent\protected\def\spac_align_set_horizontal_left
  {\setraggedness    \spac_align_set_raggedness_left
   \raggedstatus     \plusone
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plusone
   \leftskip         \plusone\leftskip \s!plus\spac_align_set_raggedness_left
   \rightskip        \plusone\rightskip\s!plus\zeropoint
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
 % \parfillleftskip  \zeroskip
 % \parfillrightskip \s_zero_plus_zero
 % needs testing:
   \parfillleftskip  \s_zero_plus_one_fil
   \parfillrightskip \zeroskip
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_center
  {\setraggedness    \spac_align_set_raggedness_middle
   \raggedstatus     \plustwo
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plustwo
   \leftskip         \plusone\leftskip \s!plus\spac_align_set_raggedness_middle
   \rightskip        \plusone\rightskip\s!plus\spac_align_set_raggedness_middle
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_zero
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_right
  {\setraggedness    \spac_align_set_raggedness_right
   \raggedstatus     \plusthree
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plusthree
   \leftskip         \plusone\leftskip \s!plus\zeropoint
   \rightskip        \plusone\rightskip\s!plus\spac_align_set_raggedness_right
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_one_fil
  %\parindent        \parindent
   \relax}

\permanent\protected\def\spac_align_set_horizontal_very_left
  {\raggedstatus     \plusone
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plusone
   \leftskip         \plusone\leftskip \s!plus\v_spac_align_fill_amount
   \rightskip        \plusone\rightskip\s!plus\zeropoint
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
 % \parfillleftskip  \zeroskip
 % \parfillrightskip \s_zero_plus_zero
 % needs testing:
   \parfillleftskip  \s_zero_plus_zero
   \parfillrightskip \zeroskip
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_very_center
  {\raggedstatus     \plustwo
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plustwo
   \leftskip         \plusone\leftskip \s!plus\v_spac_align_fill_amount
   \rightskip        \plusone\rightskip\s!plus\v_spac_align_fill_amount
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_zero
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_very_right
  {\raggedstatus     \plusthree
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plusthree
   \leftskip         \plusone\leftskip \s!plus\zeropoint
   \rightskip        \plusone\rightskip\s!plus\v_spac_align_fill_amount
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_zero
  %\parindent        \parindent
   \relax}

\permanent\protected\def\spac_align_set_horizontal_wide_center
  {\setraggedness    \spac_align_set_raggedness_middle
   \raggedstatus     \plustwo
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plustwo
   \leftskip         \plusone\leftskip \s!plus\v_spac_align_fill_amount_half
   \rightskip        \plusone\rightskip\s!plus\v_spac_align_fill_amount_half
   \spaceskip        \v_spac_align_space_amount
   \xspaceskip       \v_spac_align_space_amount_x
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_zero
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_centered_last_line
  {\raggedstatus     \zerocount
   \raggedhangstatus \zerocount
   \c_attr_alignstate\attributeunsetvalue
   \leftskip         \plusone\leftskip \s!plus\v_spac_align_fill_amount\relax
   \rightskip        \plusone\rightskip\s!plus\v_spac_align_fill_amount_negative\relax
   \spaceskip        \zeroskip
   \xspaceskip       \zeroskip
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \zeropoint\s!plus\v_spac_align_fill_amount_double\relax
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_flushedright_last_line
  {\raggedstatus     \zerocount
   \raggedhangstatus \zerocount
   \c_attr_alignstate\attributeunsetvalue
   \leftskip         \plusone\leftskip \s!plus\v_spac_align_fill_amount\relax
   \rightskip        \plusone\rightskip\s!plus\v_spac_align_fill_amount_negative\relax
   \spaceskip        \zeroskip
   \xspaceskip       \zeroskip
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeropoint\s!plus\v_spac_align_fill_amount_extreme\relax
   \parfillrightskip \zeroskip
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_right_tt % a plain command
  {\tttf % brrr
   \raggedstatus     \plusthree
   \raggedhangstatus \zerocount
   \c_attr_alignstate\plusthree
   \leftskip         \plusone\leftskip \s!plus\zeropoint\relax
   \rightskip        \plusone\rightskip\s!plus\spac_align_set_raggedness_right\relax
   \spaceskip        \zeroskip
   \xspaceskip       \zeroskip
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeroskip
   \parfillleftskip  \zeroskip
   \parfillrightskip \s_zero_plus_zero
  %\parindent        \parindent
   \relax}

\permanent\protected\def\spac_align_set_horizontal_slanted
  {\raggedstatus     \zerocount
   \raggedhangstatus \zerocount
  %\c_attr_alignstate\attributeunsetvalue
   \c_attr_alignstate\plustwo
   \leftskip         \plusone\leftskip \s!plus\spac_align_set_raggedness_middle
   \rightskip        \plusone\rightskip\s!plus\spac_align_set_raggedness_middle
   \spaceskip        \zeroskip
   \xspaceskip       \zeroskip
   \parinitleftskip  \zeroskip
   \parinitrightskip \zeropoint\s!plus\v_spac_align_fill_amount_extreme\relax
   \parfillleftskip  \zeropoint\s!plus\v_spac_align_fill_amount_extreme\relax
   \parfillrightskip \zeroskip
   \parindent        \zeropoint
   \relax}

\permanent\protected\def\spac_align_set_horizontal_extra
  {\xspaceskip\zeropoint\s!plus\v_spac_align_fill_amount_space\relax}

\def\spac_align_flush_horizontal
  {\ifcase\c_spac_align_state_horizontal
     % 0
     \spac_align_set_horizontal_none
   \or
     % 1 center
     \ifcase\c_spac_align_state_broad
       \spac_align_set_horizontal_center
     \or
       \spac_align_set_horizontal_very_center
     \or
       \spac_align_set_horizontal_wide_center
     \fi
   \or
     % 2 flush left
     \ifcase\c_spac_align_state_broad
       \ifcase\c_spac_align_state_tight
         \spac_align_set_horizontal_right
       \else
         \spac_align_set_horizontal_center
         \leftskip1\leftskip
       \fi
     \else
       \spac_align_set_horizontal_very_right
     \fi
   \or
     % 3 flush right
     \ifcase\c_spac_align_state_broad
       \ifcase\c_spac_align_state_tight
         \spac_align_set_horizontal_left
       \else
         \spac_align_set_horizontal_center
         \rightskip1\rightskip
       \fi
     \else
       \spac_align_set_horizontal_very_left
     \fi
   \or
     % 4 inner
     \ifdoublesided
       \signalinnerrealign
     \fi
     \rightorleftpageaction\spac_align_set_horizontal_right\spac_align_set_horizontal_left
   \or
     % 5 outer
     \ifdoublesided
       \signalouterrealign
     \fi
     \rightorleftpageaction\spac_align_set_horizontal_left\spac_align_set_horizontal_right
   \or
     % 6 oneliner
     \ifcase\c_spac_align_state_broad
       \spac_align_set_horizontal_right
     \else
       \spac_align_set_horizontal_very_right
     \fi
    %\parfillleftskip \zeroskip
     \parfillrightskip\zeroskip
   \or
     % 7 centered last line
     \spac_align_set_horizontal_centered_last_line
   \or
     % 8 right aligned last line
     \spac_align_set_horizontal_flushedright_last_line
   \or
     % 9 paragraph
    %\parfillleftskip \zeroskip
     \parfillrightskip\zeroskip
   \or
     % 10 slanted
     \spac_align_set_horizontal_slanted
   \or
     % 11 hangleft
     \spac_align_set_horizontal_hang_left
   \or
     % 12 hangright
     \spac_align_set_horizontal_hang_right
   \or
     % 13 hangleft range, private
     \spac_align_set_horizontal_hang_left_range
   \or
     % 14 hangright range, private
     \spac_align_set_horizontal_hang_right_range
   \fi
   \relax}

% Page spacing:

\newconstant\c_spac_align_state_page

% \mutable\def\bottomalignlimit{\plusthree\lineheight}

\newconstant\bottomraggednessmode % 0=ragged 1=normal/align 2=baseline 3=depth 4=weird-line

\permanent\protected\def\raggedbottom
  {\bottomraggednessmode\zerocount
   \settopskip}

\permanent\protected\def\alignbottom
  {\bottomraggednessmode\plusone
   \settopskip}

\permanent\protected\def\baselinebottom
  {\bottomraggednessmode\plustwo
   \settopskip}

\permanent\protected\def\depthlinebottom
  {\bottomraggednessmode\plusthree
   \settopskip}

\aliased\let\normalbottom\alignbottom % downward compatible

\permanent\protected\def\setbottomalignmode#1%
  {\bottomraggednessmode#1%
   \settopskip}

\def\spac_align_flush_page
  {\ifcase\c_spac_align_state_page
     % keep state
   \or
     \raggedbottom
   \or
     \alignbottom
   \or
     \baselinebottom
   \or
     \depthlinebottom
   \fi}

% Directions

\newconstant\c_spac_align_state_direction

\def\spac_align_flush_direction
  {\ifcase\c_spac_align_state_direction
    % keep state
   \or
     \lefttoright
   \or
     \righttoleft
   \fi}

% Trick

%D \starttyping
%D \framed[width=5.5cm,align={flushleft}]       {This is a test sentence to see if it aligns okay.}
%D \framed[width=5.5cm,align={flushright}]      {This is a test sentence to see if it aligns okay.}
%D \framed[width=5.5cm,align={flushleft,tight}] {This is a test sentence to see if it aligns okay.}
%D \framed[width=5.5cm,align={flushright,tight}]{This is a test sentence to see if it aligns okay.}
%D \framed[width=5.5cm,align={flushleft,broad}] {This is a test sentence to see if it aligns okay.}
%D \framed[width=5.5cm,align={flushright,broad}]{This is a test sentence to see if it aligns okay.}
%D \stoptyping

\newconstant\c_spac_align_state_tight

% Interesting is that the non cached version is also pretty efficient
% and as we cache we seldom call that one now so one can debate the
% speedup.

\newtoks\t_spac_align_collected
\newtoks\t_spac_align_applied
\newtoks\t_spac_align_presets

\aliased\let\raggedcommand    \relax
\aliased\let\updateraggedskips\relax

\lettonothing\m_spac_align_asked
%lettonothing\m_spac_align_named

\def\spac_align_add_to_cache
  {\enforced\let\raggedbox\relax % why
   % we inherit hyphenation and tolerance
   \expand\t_spac_align_presets
%    \lastlinefit\zerocount
   \ifcsname\??aligncommand\m_spac_align_named\endcsname
     \lastnamedcs
   \else
     \rawprocesscommacommand[\m_spac_align_asked]\spac_align_collect
   \fi
   \etoks\t_spac_align_collected
     {\raggedonelinerstate\conditionalfalse % bad
      \expand\t_spac_align_collected
      \expand\t_spac_align_applied}%
   \enforced\permanent\protected\edef\raggedcommand    {\expand\t_spac_align_collected}%
   \enforced\permanent\protected\edef\updateraggedskips{\spac_align_flush_horizontal}%
   \enforced\aliased\gletcsname\??alignmentnormalcache\m_spac_align_named\endcsname\raggedcommand
   \enforced\aliased\gletcsname\??alignmentraggedcache\m_spac_align_named\endcsname\updateraggedskips}

\appendtoks
  \spac_align_flush_horizontal
  \spac_align_flush_vertical
  \spac_align_flush_direction
  \spac_align_flush_page
  \spac_align_flush_parfill
\to \t_spac_align_applied

\appendtoks
   \t_spac_align_collected       \emptytoks
   \c_spac_align_state_broad     \zerocount
   \c_spac_align_state_horizontal\zerocount
   \c_spac_align_state_vertical  \zerocount
   \c_spac_align_state_spread    \zerocount
   \c_spac_align_state_direction \zerocount % what is default ?
   \c_spac_align_state_tight     \zerocount
   \c_spac_align_state_page      \zerocount
   \c_spac_align_state_par_fill  \zerocount
\to \t_spac_align_presets

\mutable\def\m_spac_align_named{\m_spac_align_asked}

\def\spac_align_collect#1%
  {\csname\??aligncommand#1\endcsname}

\permanent\protected\tolerant\def\definealign[#1]#*[#2]%
  {\pushmacro\raggedcommand
   \pushmacro\updateraggedskips
   \edef\m_spac_align_named{#1}%
   \edef\m_spac_align_asked{#2}%
   \spac_align_add_to_cache
   \popmacro\updateraggedskips
   \popmacro\raggedcommand}

% The local (key driven) setter:

\permanent\protected\def\spac_align_prepare#1% deferred
  {\edef\m_spac_align_asked{#1}%
   \enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
   \ifrelax\raggedcommand
     \spac_align_add_to_cache
   \else
     \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
   \fi}

\aliased\let\dosetraggedcommand\spac_align_prepare % sort of public

% The regular align setter:

\permanent\tolerant\protected\def\setupalign[#1]% immediate
  {\edef\m_spac_align_asked{#1}%
   \enforced\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
   \ifrelax\raggedcommand
     \spac_align_add_to_cache
   \else
     \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
   \fi
   \raggedcommand}

\permanent\protected\def\usealignparameter#1% faster local variant
  {\edef\m_spac_align_asked{#1\c!align}%
   \ifempty\m_spac_align_asked\else
     \spac_align_use_indeed
   \fi}

\permanent\protected\def\dousealignparameter#1% faster local variant
  {\edef\m_spac_align_asked{#1}%
   \ifempty\m_spac_align_asked\else
     \spac_align_use_indeed
   \fi}

\def\spac_align_use_indeed
  {\enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
   \ifrelax\raggedcommand
     \spac_align_add_to_cache
   \else
     \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
   \fi
   \raggedcommand}

% experiment

\protected\def\spac_align_use_later#1%
  {\begingroup
   \edef\m_spac_align_asked{#1}%
   \enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
   \ifrelax\raggedcommand
     \spac_align_add_to_cache
   \fi
   \endgroup}

\protected\def\spac_align_use_now#1%
  {\csname\??alignmentnormalcache#1\endcsname}

% Maybe we need something different in columns.

\permanent\protected\def\installalign#1#2% beware: commands must be unexpandable!
  {\ifcsname\??aligncommand#1\endcsname \else  % : so maybe we should use \etoksapp
     \defcsname\??aligncommand#1\endcsname{\toksapp\t_spac_align_collected{#2}}%
   \fi}

% The keywords:

\letcsname\??aligncommand\empty            \endcsname\empty
\defcsname\??aligncommand\v!broad          \endcsname{\c_spac_align_state_broad     \plusone  }
\defcsname\??aligncommand\v!wide           \endcsname{\c_spac_align_state_broad     \plustwo  }

\defcsname\??aligncommand\v!bottom         \endcsname{\c_spac_align_state_page      \plusone  }
\defcsname\??aligncommand\v!height         \endcsname{\c_spac_align_state_page      \plustwo  }
\defcsname\??aligncommand\v!depth          \endcsname{\c_spac_align_state_page      \plusfour }
\defcsname\??aligncommand\v!line           \endcsname{\c_spac_align_state_page      \plusthree
                                                      % this will become another keyword (undocumented anyway)
                                                      \toksapp\t_spac_align_collected{\raggedonelinerstate\conditionaltrue}}

\defcsname\??aligncommand\v!high           \endcsname{\c_spac_align_state_vertical  \plusthree}
\defcsname\??aligncommand\v!low            \endcsname{\c_spac_align_state_vertical  \plustwo  }
\defcsname\??aligncommand\v!lohi           \endcsname{\c_spac_align_state_vertical  \plusone  }

\defcsname\??aligncommand\v!spread         \endcsname{\c_spac_align_state_spread    \plusone  }

\defcsname\??aligncommand\v!flushright     \endcsname{\c_spac_align_state_horizontal\plusthree}
\defcsname\??aligncommand\v!flushleft      \endcsname{\c_spac_align_state_horizontal\plustwo  }
\defcsname\??aligncommand\v!middle         \endcsname{\c_spac_align_state_horizontal\plusone  }
\defcsname\??aligncommand\v!no             \endcsname{\c_spac_align_state_horizontal\plustwo  }
\defcsname\??aligncommand\v!yes            \endcsname{\c_spac_align_state_horizontal\zerocount}
\defcsname\??aligncommand\v!width          \endcsname{\c_spac_align_state_horizontal\zerocount}
\defcsname\??aligncommand\v!normal         \endcsname{\c_spac_align_state_horizontal\zerocount}
\defcsname\??aligncommand\v!reset          \endcsname{\c_spac_align_state_page      \zerocount
                                                      \c_spac_align_state_horizontal\zerocount}
\defcsname\??aligncommand\v!inner          \endcsname{\c_spac_align_state_horizontal\plusfour }
\defcsname\??aligncommand\v!outer          \endcsname{\c_spac_align_state_horizontal\plusfive }
\defcsname\??aligncommand\v!flushinner     \endcsname{\c_spac_align_state_horizontal\plusfour }
\defcsname\??aligncommand\v!flushouter     \endcsname{\c_spac_align_state_horizontal\plusfive }
\defcsname\??aligncommand\v!right          \endcsname{\c_spac_align_state_horizontal\plustwo  }
\defcsname\??aligncommand\v!left           \endcsname{\c_spac_align_state_horizontal\plusthree}
\defcsname\??aligncommand\v!center         \endcsname{\c_spac_align_state_horizontal\plusone
                                                      \c_spac_align_state_broad     \plustwo  }
\defcsname\??aligncommand\v!disable        \endcsname{\c_spac_align_state_horizontal\plussix  }
\defcsname\??aligncommand\v!last           \endcsname{\c_spac_align_state_horizontal\plusseven}
\defcsname\??aligncommand\v!end            \endcsname{\c_spac_align_state_horizontal\pluseight}
\defcsname\??aligncommand\v!paragraph      \endcsname{\c_spac_align_state_horizontal\plusnine}

\defcsname\??aligncommand\v!lefttoright    \endcsname{\c_spac_align_state_direction \plusone  }
\defcsname\??aligncommand\v!righttoleft    \endcsname{\c_spac_align_state_direction \plustwo  }
\defcsname\??aligncommand               l2r\endcsname{\c_spac_align_state_direction \plusone  }
\defcsname\??aligncommand               r2l\endcsname{\c_spac_align_state_direction \plustwo  }

\defcsname\??aligncommand\v!tight          \endcsname{\c_spac_align_state_tight     \plusone}

\defcsname\??aligncommand\v!slanted        \endcsname{\c_spac_align_state_horizontal\plusten
                                                      \c_spac_align_state_broad     \plustwo  }


\defcsname\??aligncommand\v!table          \endcsname{\c_spac_align_state_vertical  \plusthree
                                                      \c_spac_align_state_broad     \plusone
                                                      \c_spac_align_state_horizontal\plustwo  }

\defcsname\??aligncommand\v!lesshyphenation\endcsname{\toksapp\t_spac_align_collected{\lesshyphens}}
\defcsname\??aligncommand\v!morehyphenation\endcsname{\toksapp\t_spac_align_collected{\morehyphens}}

\defcsname\??aligncommand\v!hanging        \endcsname{\toksapp\t_spac_align_collected{\font_protruding_enable }}
\defcsname\??aligncommand\v!hangingboth    \endcsname{\toksapp\t_spac_align_collected{\font_protruding_enable_b}}
\defcsname\??aligncommand\v!nothanging     \endcsname{\toksapp\t_spac_align_collected{\font_protruding_disable}}
\defcsname\??aligncommand\v!hz             \endcsname{\toksapp\t_spac_align_collected{\font_expansion_enable  }}
\defcsname\??aligncommand\v!fullhz         \endcsname{\toksapp\t_spac_align_collected{\font_expansion_enable_k}}
\defcsname\??aligncommand\v!nohz           \endcsname{\toksapp\t_spac_align_collected{\font_expansion_disable }}
%defcsname\??aligncommand\v!spacing        \endcsname{\toksapp\t_spac_align_collected{\normalspacing\zerocount}} % not yet
%defcsname\??aligncommand\v!nospacing      \endcsname{\toksapp\t_spac_align_collected{\normalspacing\plusone}}   % not yet
\defcsname\??aligncommand\v!hyphenated     \endcsname{\toksapp\t_spac_align_collected{\dohyphens}}
\defcsname\??aligncommand\v!nothyphenated  \endcsname{\toksapp\t_spac_align_collected{\nohyphens}}
\defcsname\??aligncommand\v!collapsed      \endcsname{\toksapp\t_spac_align_collected{\dohyphencollapsing}}
\defcsname\??aligncommand\v!notcollapsed   \endcsname{\toksapp\t_spac_align_collected{\nohyphencollapsing}}
\defcsname\??aligncommand\v!explicit       \endcsname{\toksapp\t_spac_align_collected{\nohyphens\doexplicithyphens}}

\defcsname\??aligncommand\v!tolerant       \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_tolerant}}
\defcsname\??aligncommand\v!verytolerant   \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_very_tolerant}}
\defcsname\??aligncommand\v!stretch        \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_stretch}}
\defcsname\??aligncommand\v!extremestretch \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_extreme_stretch}}

\defcsname\??aligncommand          \v!final\endcsname{\c_spac_align_state_par_fill\plusone}
\defcsname\??aligncommand        1*\v!final\endcsname{\c_spac_align_state_par_fill\plusone}
\defcsname\??aligncommand        2*\v!final\endcsname{\c_spac_align_state_par_fill\plustwo}      % hardcoded multiplier
\defcsname\??aligncommand        3*\v!final\endcsname{\c_spac_align_state_par_fill\plusthree}
\defcsname\??aligncommand        4*\v!final\endcsname{\c_spac_align_state_par_fill\plusfour}

% a one shot (only usefull in e.g. framed, also needs tolerance and stretch)

\defcsname\??aligncommand          \v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plusone}}
\defcsname\??aligncommand        1*\v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plusone}}
\defcsname\??aligncommand        2*\v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plustwo}}

% for verbatim overflow

\defcsname\??aligncommand\v!hangleft      \endcsname{\c_spac_align_state_horizontal\pluseleven}
\defcsname\??aligncommand\v!hangright     \endcsname{\c_spac_align_state_horizontal\plustwelve}

\permanent\protected\def\spac_align_set_horizontal_hang_right
  {\spac_align_set_horizontal_very_left
   \parinitrightskip\s_zero_plus_one_fill
   \raggedhangstatus \plusone
   \relax}

\permanent\protected\def\spac_align_set_horizontal_hang_left
  {\spac_align_set_horizontal_very_right
   \parinitleftskip \s_zero_plus_one_fill
   \raggedhangstatus \plustwo
   \relax}

% idem but better: experimental !

\defcsname\??aligncommand\v!hangleft  range\endcsname{\c_spac_align_state_horizontal\plusthirteen}
\defcsname\??aligncommand\v!hangright range\endcsname{\c_spac_align_state_horizontal\plusfourteen}

\permanent\protected\def\spac_align_set_horizontal_hang_right_range % special purpose
  {\spac_align_set_horizontal_very_left
  %\rightskip        \plusone\rightskip\s!plus\spac_align_set_raggedness_left
   \rightskip        \plusone\rightskip\s!plus.5\hsize % if we change this then we need a *_right_range_tt variant
   \parinitrightskip \s_zero_plus_one_fill
   \parfillleftskip  \s_zero_plus_zero
   \raggedhangstatus \plusthree
   \relax}

\permanent\protected\def\spac_align_set_horizontal_hang_left_range % special purpose
  {\spac_align_set_horizontal_very_right
  %\leftskip        \plusone\leftskip \s!plus\spac_align_set_raggedness_left %
   \leftskip        \plusone\leftskip \s!plus.5\hsize % if we change this then we need a *_right_range_tt variant
   \parinitleftskip \s_zero_plus_one_fill
   \raggedhangstatus\plusfour
   \relax}

\newconditional\c_spac_align_hang_done

\permanent\protected\def\starthangrange
  {\c_spac_align_hang_done\conditionalfalse}

\permanent\protected\def\stophangrange
  {\c_spac_align_hang_done\conditionalfalse
   \removeunwantedspaces
  %\localbreakpar
   \par}

\permanent\protected\def\starthangrangestep
  {\ifconditional\c_spac_align_hang_done
     \removeunwantedspaces
   % \hfillneg
   % \strut
   % \parbreak
     \localbreakpar
   \fi
   \ifnum\raggedhangstatus=\plusthree
     \ifhmode
       \strut
       \penalty\plustenthousand
       \hskip-\leftskip
       \penalty\plustenthousand
     \else
       \spac_align_set_horizontal_hang_right_range
       \dontleavehmode
     \fi
   \orelse\ifnum\raggedhangstatus=\plusfour
     \ifhmode
       \strut
       \hfill
     \else
       \spac_align_set_horizontal_hang_left_range
       \dontleavehmode
     \fi
   \fi
  %\luaboundary 123 456
   \ignorespaces}

\permanent\protected\def\stophangrangestep
  {\par\c_spac_align_hang_done\conditionaltrue} % par is needed

\permanent\protected\def\emptyhangrangestep
  {\strut\localbreakpar}

% experiment

\defcsname\??aligncommand\v!flushforward \endcsname{\c_spac_align_state_horizontal\ifconditional\inlinelefttoright\plustwo\else\plusthree\fi}
\defcsname\??aligncommand\v!flushbackward\endcsname{\c_spac_align_state_horizontal\ifconditional\inlinelefttoright\plusthree\else\plustwo\fi}

% experiment

\defcsname\??aligncommand\v!always\endcsname{\toksapp\t_spac_align_collected{\bitwiseflip\hyphenationmode\forcecheckhyphenationcode}}

% experiment

\defcsname\??aligncommand\v!profile\endcsname{\toksapp\t_spac_align_collected{\setmainlineprofile}}

% lastlinefit

\newconstant\c_spac_align_state_fit

\defcsname\??aligncommand\v!fit\endcsname
  {\c_spac_align_state_fit\plusone}

\appendtoks
   \c_spac_align_state_fit\zerocount
\to \t_spac_align_presets

\appendtoks
   \lastlinefit\ifcase\c_spac_align_state_fit\zerocount\else\plusthousand\fi
\to \t_spac_align_applied

\permanent\immutable\integerdef\defaultdoublehyphendemerits\plustenthousand
\permanent\immutable\integerdef\defaultfinalhyphendemerits \plusfivethousand
\permanent\immutable\integerdef\defaultadjdemerits         \plustenthousand
\permanent\immutable\integerdef\defaultorphanpenalty       \plusfifty
\permanent\immutable\integerdef\defaulttoddlerpenalty      \plusfifty
\permanent\immutable\integerdef\defaultlefttwindemerits    \plusseventyfivehundred
\permanent\immutable\integerdef\defaultrighttwindemerits   \plusseventyfivehundred

\permanent\immutable\integerdef\granularadjdemerits        \plusfivethousand % see accumulation in granular

\frozen\doublehyphendemerits \defaultdoublehyphendemerits
\frozen\finalhyphendemerits  \defaultfinalhyphendemerits
\frozen\adjdemerits          \defaultadjdemerits

\frozen\orphanpenalty        \zerocount
\frozen\toddlerpenalty       \zerocount
\frozen\lefttwindemerits     \zerocount
\frozen\righttwindemerits    \zerocount

\permanent\protected\def\noorphans   {\orphanpenalty    \defaultorphanpenalty}
\permanent\protected\def\notoddlers  {\toddlerpenalty   \defaulttoddlerpenalty}
\permanent\protected\def\notwins     {\lefttwindemerits \defaultlefttwindemerits
                                      \righttwindemerits\defaultrighttwindemerits}

\permanent\protected\def\keeporphans {\orphanpenalty    \zerocount}
\permanent\protected\def\keeptoddlers{\toddlerpenalty   \zerocount}
\permanent\protected\def\keeptwins   {\lefttwindemerits \zerocount
                                      \righttwindemerits\zerocount}

% for now, not interfaced (maybe never):

\permanent\def\s!noorphans   {noorphans}
\permanent\def\s!keeporphans {keeporphans}
\permanent\def\s!notwins     {notwins}
\permanent\def\s!keeptwins   {keeptwins}
\permanent\def\s!notoddlers  {notoddlers}
\permanent\def\s!keeptoddlers{keeptoddlers}

\defcsname\??aligncommand\s!noorphans   \endcsname{\toksapp\t_spac_align_collected{\noorphans}}
\defcsname\??aligncommand\s!keeporphans \endcsname{\toksapp\t_spac_align_collected{\keeporphans}}
\defcsname\??aligncommand\s!notwins     \endcsname{\toksapp\t_spac_align_collected{\notwins}}
\defcsname\??aligncommand\s!keeptwins   \endcsname{\toksapp\t_spac_align_collected{\keeptwins}}
\defcsname\??aligncommand\s!notoddlers  \endcsname{\toksapp\t_spac_align_collected{\notoddlers}}
\defcsname\??aligncommand\s!keeptoddlers\endcsname{\toksapp\t_spac_align_collected{\keeptoddlers}}

\newconstant \matchallfitnessclasses \setconstant \matchallfitnessclasses "FF

\appendtoks
    \fitnessclasses \zerocount
\to \t_spac_align_presets

\permanent\protected\def\installaligncommand#1#2%
  {\defcsname\??aligncommand#1\endcsname{\appendtoks#2\to\t_spac_align_applied}}

\aliased\let\stopinstallaligncommand\relax

\permanent\protected\def\startinstallaligncommand[#1]#S#/\stopinstallaligncommand
  {\defcsname\??aligncommand#1\endcsname{\appendtoks#2\to\t_spac_align_applied}}

\permanent \specificationdef \granularfitnessclasses \fitnessclasses \plusnine
    \plusninetynine
    \plusfourtytwo  % .75
    \plustwelve     % .50
    \plustwo        % .25
    \zerocount      % .00
    \plustwo        % .25
    \plustwelve     % .50
    \plusfourtytwo  % .75
    \plusninetynine

% \permanent \specificationdef \defaultadjacentdemerits \adjacentdemerits \minusone \plustenthousand

\permanent \specificationdef \granularadjacentdemerits \adjacentdemerits \pluseight
    \zerocount
    \plustwentyfivehundred
    \plusfivethousand
    \plusseventyfivehundred
    \plustenthousand
    \numexpr\plustenthousand+\plustwentyfivehundred \relax
    \numexpr\plustenthousand+\plusfivethousand      \relax
   %\numexpr\plustenthousand+\plusseventyfivehundred\relax
    \numexpr\plustenthousand+\plustenthousand       \relax

\installaligncommand
  {\v!granular}
  {\granularfitnessclasses
   \granularadjacentdemerits}

% end of definitions

\definehspace [\v!final] [\emspaceamount]

\def\spac_align_flush_parfill
  {\ifcase\c_spac_align_state_par_fill\else
     \spac_align_flush_parfill_indeed{\the\c_spac_align_state_par_fill}%
   \fi}

\protected\def\spac_align_flush_parfill_indeed#1%
  {\parfillrightskip
     #1\directhspaceamount\v!final
   % plus \dimexpr\availablehsize-#1\directhspaceamount\v!final\relax
     plus 1fill
   \relax}

%D For Wolfgang:

\newtoks \t_spac_every_swap_align

\appendtoks
    \defcsname\??aligncommand\v!right\endcsname{\c_spac_align_state_horizontal\plusthree}%
    \defcsname\??aligncommand\v!left \endcsname{\c_spac_align_state_horizontal\plustwo  }%
\to \t_spac_every_swap_align

\permanent\protected\def\enablereversealignment
  {\expand\t_spac_every_swap_align
   \t_spac_every_swap_align\emptytoks}

% Visible commands:

\aliased\let\notragged           \spac_align_set_horizontal_none
\aliased\let\raggedleft          \spac_align_set_horizontal_left
\aliased\let\raggedcenter        \spac_align_set_horizontal_center
\aliased\let\raggedright         \spac_align_set_horizontal_right
\aliased\let\veryraggedleft      \spac_align_set_horizontal_very_left
\aliased\let\veryraggedcenter    \spac_align_set_horizontal_very_center
\aliased\let\veryraggedright     \spac_align_set_horizontal_very_right
\aliased\let\raggedwidecenter    \spac_align_set_horizontal_wide_center
\aliased\let\centeredlastline    \spac_align_set_horizontal_centered_last_line
\aliased\let\flushedrightlastline\spac_align_set_horizontal_flushedright_last_line
\aliased\let\ttraggedright       \spac_align_set_horizontal_right_tt            % a plain command
\aliased\let\forgetragged        \spac_align_set_horizontal_none
\aliased\let\raggedslanted       \spac_align_set_horizontal_slanted

\appendtoks
    \spac_align_set_horizontal_none
\to \everyforgetall

% Box commands.

\permanent\protected\def\ibox
  {\vbox\bgroup
     \forgetall
     \enforced\let\\\endgraf
     \ifdoublesided\signalinnerrealign\fi
     \doifelserightpage\spac_align_set_horizontal_right\spac_align_set_horizontal_left
     \let\next}

\permanent\protected\def\obox
  {\vbox\bgroup
     \forgetall
     \enforced\let\\\endgraf
     \ifdoublesided\signalouterrealign\fi
     \doifelserightpage\spac_align_set_horizontal_left\spac_align_set_horizontal_right
     \let\next}

\let\raggedbox\relax

\permanent\protected\def\dosetraggedvbox#1% can be more keys .. how about caching here (but seldom used)
  {\enforced\let\raggedbox\vbox
   \processcommacommand[#1]\spac_align_set_ragged_vbox}

\permanent\protected\def\dosetraggedhbox#1% can be more keys .. how about caching here (but seldom used)
  {\enforced\let\raggedbox\hbox
   \processcommacommand[#1]\spac_align_set_ragged_hbox}

\def\spac_align_set_ragged_vbox#1%
  {\ifcsname\??alignvertical#1\endcsname
     \lastnamedcs
     \quitcommalist
   \fi}

\def\spac_align_set_ragged_hbox#1%
  {\ifcsname\??alignhorizontal#1\endcsname
     \lastnamedcs
     \quitcommalist
   \fi}

\permanent\protected\def\spac_align_vertical_left      {\lbox}
\permanent\protected\def\spac_align_vertical_right     {\rbox}
\permanent\protected\def\spac_align_vertical_middle    {\cbox}
\permanent\protected\def\spac_align_vertical_inner     {\ibox}
\permanent\protected\def\spac_align_vertical_outer     {\obox}
\permanent\protected\def\spac_align_vertical_flushleft {\rbox}
\permanent\protected\def\spac_align_vertical_flushright{\lbox}
\permanent\protected\def\spac_align_vertical_center    {\cbox}
\permanent\protected\def\spac_align_vertical_no        {\vbox\bgroup\spac_align_set_horizontal_right\let\next=}

\permanent\protected\def\spac_align_horizontal_left      {\doalignedline\v!left}
\permanent\protected\def\spac_align_horizontal_right     {\doalignedline\v!right}
\permanent\protected\def\spac_align_horizontal_middle    {\doalignedline\v!middle}
\permanent\protected\def\spac_align_horizontal_inner     {\doalignedline\v!inner}
\permanent\protected\def\spac_align_horizontal_outer     {\doalignedline\v!outer}
\permanent\protected\def\spac_align_horizontal_flushleft {\doalignedline\v!right}
\permanent\protected\def\spac_align_horizontal_flushright{\doalignedline\v!left}
\permanent\protected\def\spac_align_horizontal_center    {\doalignedline\v!middle}

\defcsname\??alignvertical  \v!left      \endcsname{\enforced\let\raggedbox\spac_align_vertical_left}
\defcsname\??alignvertical  \v!right     \endcsname{\enforced\let\raggedbox\spac_align_vertical_right}
\defcsname\??alignvertical  \v!middle    \endcsname{\enforced\let\raggedbox\spac_align_vertical_middle}
\defcsname\??alignvertical  \v!inner     \endcsname{\enforced\let\raggedbox\spac_align_vertical_inner}
\defcsname\??alignvertical  \v!outer     \endcsname{\enforced\let\raggedbox\spac_align_vertical_outer}
\defcsname\??alignvertical  \v!flushleft \endcsname{\enforced\let\raggedbox\spac_align_vertical_flushleft }
\defcsname\??alignvertical  \v!flushright\endcsname{\enforced\let\raggedbox\spac_align_vertical_flushright}
\defcsname\??alignvertical  \v!center    \endcsname{\enforced\let\raggedbox\spac_align_vertical_center}
\defcsname\??alignvertical  \v!no        \endcsname{\enforced\let\raggedbox\spac_align_vertical_no}

\defcsname\??alignhorizontal\v!left      \endcsname{\enforced\let\raggedbox\spac_align_horizontal_left}
\defcsname\??alignhorizontal\v!right     \endcsname{\enforced\let\raggedbox\spac_align_horizontal_right}
\defcsname\??alignhorizontal\v!middle    \endcsname{\enforced\let\raggedbox\spac_align_horizontal_middle}
\defcsname\??alignhorizontal\v!inner     \endcsname{\enforced\let\raggedbox\spac_align_horizontal_inner}
\defcsname\??alignhorizontal\v!outer     \endcsname{\enforced\let\raggedbox\spac_align_horizontal_outer}
\defcsname\??alignhorizontal\v!flushleft \endcsname{\enforced\let\raggedbox\spac_align_horizontal_flushleft}
\defcsname\??alignhorizontal\v!flushright\endcsname{\enforced\let\raggedbox\spac_align_horizontal_flushright}
\defcsname\??alignhorizontal\v!center    \endcsname{\enforced\let\raggedbox\spac_align_horizontal_center}

% The next one can be in use so we keep it around but one should be aware of
% possible interference. But it will be removed at some point!

\permanent\protected\def\setraggedskips#1#2#3#4#5#6#7% never change this name (todo: inline this one .. less tracingall)
  {\enforced\permanent\protected\def\updateraggedskips{\dosetraggedskips{#1}{#2}{#3}{#4}{#5}{#6}{#7}}%
   \updateraggedskips}

\permanent\protected\def\dosetraggedskips#1#2#3#4#5#6#7%
  {\raggedstatus                 #1\relax
   \leftskip   1\leftskip \s!plus#2\relax
   \rightskip  1\rightskip\s!plus#3\relax
   \spaceskip                    #4\relax
   \xspaceskip                   #5\relax
   \parfillskip \zeropoint\s!plus#6\relax
   \parindent                    #7\relax
   \c_attr_alignstate\ifcase\raggedstatus\attributeunsetvalue\else\raggedstatus\fi}

% older (context) names:

\aliased\let\spaceamount  \interwordspace
\aliased\let\emspaceamount\emwidth

% tracing:

\def\spac_show_par_data#1%
  {\ifrelax#1\else
     \ifzero#1\else
       \hbox{\string#1: \the#1}\endgraf
     \fi
     \expandafter\spac_show_par_data
   \fi}

\permanent\protected\def\showpardata
  {\expanded{\dontleavehmode\inleftmargin{\vsmash{\infofont\framed[\c!align=\v!right]{%
     \hbox{font: \fontname\font}\endgraf
     \spac_show_par_data
       \interwordspace \interwordstretch \interwordshrink \emwidth \exheight \extraspace
       \hsize     \vsize
       \leftskip  \rightskip
       \spaceskip \xspaceskip
       \parindent \parfillleftskip \parinitrightskip \parinitleftskip \parfillrightskip
       \hyphenpenalty \exhyphenpenalty \automatichyphenpenalty \explicithyphenpenalty
     % \displaywidowpenalty
       \widowpenalty \clubpenalty \brokenpenalty
       \doublehyphendemerits \finalhyphendemerits \adjdemerits \orphanpenalty
       \toddlerpenalty \lefttwindemerits \righttwindemerits
     % \linebreakstretchcriterion \linebreakshrinkcriterion
   \relax}}}}}

\permanent\protected\def\startshowpardata
  {\begingroup
   \showstruts
   \tracingparagraphs\maxdimen
   \enforced\let\startshowpardata\begingroup
   \toksapp\everypar{\showpardata}}

\permanent\protected\def\stopshowpardata
  {\endgraf
   \endgroup}

% Structure:

\protected\def\startalignment
  {\par
   \begingroup
   \setupalign}

\protected\def\stopalignment
  {\par
   \endgroup}

\setnewconstant\alignstrutmode\plusone

% see later for the real definition, which in the simple case is:

\newtoks \everyleftofalignedline
\newtoks \everyrightofalignedline

\permanent\protected\def\shiftalignedline#1#2#3#4% left, right, inner, outer
  {\rightorleftpageaction
     {\everyleftofalignedline {\hskip{#1+#3}}%
      \everyrightofalignedline{\hskip{#2+#4}}}
     {\everyleftofalignedline {\hskip{#1+#4}}%
      \everyrightofalignedline{\hskip{#2+#3}}}}

\permanent\protected\def\doalignline#1#2% \\ == newline
  {\noindentation  % was \noindent
   \dontleavehmode % added in marrakesch at TUG 2006\begingroup
   \begingroup
   \setlocalhsize % new
   \enforced\permanent\protected\def\\{\egroup\par\doalignline{#1}{#2}\bgroup}%
   \dowithnextbox
     {\hbox to \localhsize
        {\ifcase\alignstrutmode\or\strut\fi
         \expand\everyleftofalignedline
         #1\unhbox\nextbox#2\relax
         \expand\everyrightofalignedline}%
      \endgroup}
     \hbox}

% plain commands

\ifdefined\line       \else \permanent\protected\def\line        {\hbox to\hsize}    \fi
\ifdefined\leftline   \else \permanent\protected\def\leftline  #1{\line{#1\hss}}     \fi
\ifdefined\rightline  \else \permanent\protected\def\rightline #1{\line{\hss#1}}     \fi
\ifdefined\centerline \else \permanent\protected\def\centerline#1{\line{\hss#1\hss}} \fi

% direct commands

\installcorenamespace{alignwrapper}

\protected\defcsname\??alignwrapper\v!left  \endcsname{\doalignline\relax \hss  }
\protected\defcsname\??alignwrapper\v!middle\endcsname{\doalignline\hss   \hss  }
\protected\defcsname\??alignwrapper\v!right \endcsname{\doalignline\hss   \relax}
\protected\defcsname\??alignwrapper\v!max   \endcsname{\doalignline\relax \relax}

\def\spac_align_wrapper_middle{\doalignline\hss\hss}

\def\spac_align_wrapper_handle#1%
  {\ifcsname\??alignwrapper#1\endcsname\expandafter\lastnamedcs\else\expandafter\spac_align_wrapper_middle\fi}

\permanent\tolerant\protected\def\startlinealignment[#1]%
  {\spac_align_wrapper_handle{#1}%
   \bgroup\ignorespaces}

\protected\def\spac_align_wrapper_stop
  {\removeunwantedspaces\egroup}

\permanent\let\stoplinealignment\spac_align_wrapper_stop

\permanent\protected\def\startleftaligned  {\startlinealignment[\v!left  ]}  \permanent\let\stopleftaligned  \spac_align_wrapper_stop
\permanent\protected\def\startmiddlealigned{\startlinealignment[\v!middle]}  \permanent\let\stopmiddlealigned\spac_align_wrapper_stop
\permanent\protected\def\startrightaligned {\startlinealignment[\v!right ]}  \permanent\let\stoprightaligned \spac_align_wrapper_stop
\permanent\protected\def\startmaxaligned   {\startlinealignment[\v!max   ]}  \permanent\let\stopmaxaligned   \spac_align_wrapper_stop

\aliased\let\startmidaligned   \startmiddlealigned  \aliased\let\stopmidaligned   \stopmiddlealigned
\aliased\let\startcenteraligned\startmiddlealigned  \aliased\let\stopcenteraligned\stopmiddlealigned

\permanent\protected\def\leftaligned  {\spac_align_wrapper_handle\v!left  }
\permanent\protected\def\middlealigned{\spac_align_wrapper_handle\v!middle}
\permanent\protected\def\rightaligned {\spac_align_wrapper_handle\v!right }
\permanent\protected\def\maxaligned   {\spac_align_wrapper_handle\v!max   }

\aliased\let\midaligned   \middlealigned
\aliased\let\centeraligned\middlealigned

\installcorenamespace{alignline}

% Here left and flushleft as well as right and flushright are the same, as it's
% not used with % the aligh key.

\letcsname\??alignline\v!left      \endcsname\leftaligned
\letcsname\??alignline\v!right     \endcsname\rightaligned
\letcsname\??alignline\v!middle    \endcsname\midaligned
\letcsname\??alignline\v!flushleft \endcsname\leftaligned  % beware, makes no sense in locations / we will have a special locations handler
\letcsname\??alignline\v!flushright\endcsname\rightaligned % beware, makes no sense in locations / we will have a special locations handler
\letcsname\??alignline\v!center    \endcsname\midaligned
\letcsname\??alignline\v!max       \endcsname\maxaligned

%D Experimental (will be redone when floats are redone as it's real messy
%D now). It can also be made faster (if needed).

\permanent\protected\def\doxalignline#1#2#3#4#5#6%
  {\noindentation  % was \noindent
   \dontleavehmode % added in marrakesch at TUG 2006\begingroup
   \begingroup
   \setlocalhsize
   \enforced\permanent\protected\def\\{\egroup\par\doxalignline#1#2#3#4#5#6\bgroup}% inefficient
   \dowithnextbox
     {\hbox to \localhsize
        {#1\hskip\ifdone#2\else#3\fi#4%
         \hbox to \localhsize
           {\expand\everyleftofalignedline
            \ifcase\alignstrutmode\or\strut\fi
            \ifdone#5\unhbox\nextbox#6\else#6\unhbox\nextbox#5\fi
            \expand\everyrightofalignedline}%
         \hss}%
        \endgroup}
     \hbox}

\permanent\protected\def\doxcheckline % used for floats so multipass anyway
  {\signalrightpage\doifelserightpage\donetrue\donefalse}

\defcsname\??alignline\v!inner      \endcsname{\doxalignline\doxcheckline++\zeropoint       \relax\hss  }
\defcsname\??alignline\v!outer      \endcsname{\doxalignline\doxcheckline++\zeropoint       \hss  \relax}
\defcsname\??alignline\v!innermargin\endcsname{\doxalignline\doxcheckline-+\innermargintotal\relax\hss  }
\defcsname\??alignline\v!outermargin\endcsname{\doxalignline\doxcheckline+-\outermargintotal\hss  \relax}
\defcsname\??alignline\v!inneredge  \endcsname{\doxalignline\doxcheckline-+\inneredgetotal  \relax\hss  }
\defcsname\??alignline\v!outeredge  \endcsname{\doxalignline\doxcheckline+-\outeredgetotal  \hss  \relax}
\defcsname\??alignline\v!backspace  \endcsname{\doxalignline\doxcheckline-+\backspace       \relax\hss  }
\defcsname\??alignline\v!cutspace   \endcsname{\doxalignline\doxcheckline+-\cutspace        \hss  \relax}

\defcsname\??alignline\v!leftmargin \endcsname{\doxalignline\donefalse   --\leftmargintotal \hss  \relax}
\defcsname\??alignline\v!rightmargin\endcsname{\doxalignline\donefalse   ++\rightmargintotal\relax\hss  }
\defcsname\??alignline\v!leftedge   \endcsname{\doxalignline\donefalse   --\leftedgetotal   \hss  \relax}
\defcsname\??alignline\v!rightedge  \endcsname{\doxalignline\donefalse   ++\rightedgetotal  \relax\hss  }

\permanent\protected\def\doalignedline#1{\begincsname\??alignline#1\endcsname}

\permanent\protected\def\alignedline#1#2% setting default
  {\ifcsname\??alignline#1\endcsname
     \expandafter\lastnamedcs
   \else
     \csname\??alignline#2\expandafter\endcsname
   \fi}

% beware: \wordright{whatever\kern-\rightskip} should work!
% so, no funny boxing here

%D We need to pick up a box (downward compatible in case \type {\bgroup} is used
%D (test suite index example).

\permanent\tolerant\protected\def\wordright[#1]%
  {\dowithnextbox
     {\expanded{\spac_word_right_indeed{#1}{\hpack{\thebox\nextbox}}}}
     \hbox}

\protected\def\spac_word_right_indeed#1#2%
  {\registerparwrapper
     {\v!word:\v!right}
     {\begingroup
      \frozen\parfillrightskip\zeroskip  % frozen ?
      \endgroup}
     {\doifelseparwrapper{\v!word:\v!right}{\unregisterparwrapper{\v!word:\v!right}}\donothing
      \removeunwantedspaces
      \ifcstok{#1}\v!right
        \kern-\rightskip
      \orelse\ifempty{#1}%
        % no kern
      \else
        \kern-#1%
      \fi
      \hfill
      \strut
      \kern\emwidth\strut\kern-\emwidth
      \nobreak
      \hfilll
      \quad
      #2}}

% \dorecurse{5}{something} \wordright{--someone} \endgraf
% \dorecurse{6}{something} \wordright{--someone} \endgraf
% \dorecurse{7}{something} \wordright{--someone} \endgraf
%
% \dorecurse{5}{something} \wordright{--someone else entirely} \endgraf
% \dorecurse{6}{something} \wordright{--someone else entirely} \endgraf
% \dorecurse{7}{something} \wordright{--someone else entirely} \endgraf
%
% \wordright[\rightskip]{whatever}

% Trick posted by WS on mailing list, generalized a bit. The bottom text only shows
% op when there is one line space available. We could add some extra space if needed.

% v\protected\def\bottomword
%   {\par
%    \dowithnextbox
%      {\leaders\box\nextbox\vfil\page}
%      \vbox to \lineheight}
%
% \permanent\protected\def\bottomword
%   {\par
%    \groupedcommand
%      {\leaders
%       \vbox to \lineheight\bgroup}
%      {\egroup
%       \vfil
%       \page}}

% \simplealignedbox{2cm}{right}{x}

\installcorenamespace{alignsimple}
\installcorenamespace{alignsimplereverse}

% todo: also handle \bgroup ... \egroup

\protected\def\spac_align_simple_left  #1{{#1\hss}}
\protected\def\spac_align_simple_right #1{{\hss#1}}
\protected\def\spac_align_simple_middle#1{{\hss#1\hss}}

\letcsname\??alignsimple       \v!right     \endcsname\spac_align_simple_left
\letcsname\??alignsimple       \v!outer     \endcsname\spac_align_simple_left   % not managed! see linenumbers
\letcsname\??alignsimple       \v!flushleft \endcsname\spac_align_simple_left
\letcsname\??alignsimple       \v!left      \endcsname\spac_align_simple_right
\letcsname\??alignsimple       \v!inner     \endcsname\spac_align_simple_right  % not managed! see linenumbers
\letcsname\??alignsimple       \v!flushright\endcsname\spac_align_simple_right
\letcsname\??alignsimple       \v!middle    \endcsname\spac_align_simple_middle

\letcsname\??alignsimplereverse\v!right     \endcsname\spac_align_simple_right
\letcsname\??alignsimplereverse\v!outer     \endcsname\spac_align_simple_right % not managed! see linenumbers
\letcsname\??alignsimplereverse\v!flushleft \endcsname\spac_align_simple_right
\letcsname\??alignsimplereverse\v!left      \endcsname\spac_align_simple_left
\letcsname\??alignsimplereverse\v!inner     \endcsname\spac_align_simple_left  % not managed! see linenumbers
\letcsname\??alignsimplereverse\v!flushright\endcsname\spac_align_simple_left
\letcsname\??alignsimplereverse\v!middle    \endcsname\spac_align_simple_middle

\protected\def\spac_align_simple#1%
  {\begincsname\??alignsimple#1\endcsname}

\permanent\protected\def\simplealignedbox#1#2%
  {\hbox \ifdim#1>\zeropoint to #1
     \ifcsname\??alignsimple#2\endcsname
       \doubleexpandafter\lastnamedcs
     \else
       \doubleexpandafter\spac_align_simple_left
     \fi
   \fi}

\permanent\protected\def\simplealignedspreadbox#1#2%
  {\hbox \ifdim#1>\zeropoint spread #1
     \ifcsname\??alignsimple#2\endcsname
       \doubleexpandafter\lastnamedcs
     \else
       \doubleexpandafter\spac_align_simple_left
     \fi
   \fi}

\permanent\protected\def\simplealignedboxplus#1#2#3%
  {\hbox #3 \ifdim#1>\zeropoint to #1
     \ifcsname\??alignsimple#2\endcsname
       \doubleexpandafter\lastnamedcs
     \else
       \doubleexpandafter\spac_align_simple_left
     \fi
   \fi}

\newconditional\alignsimplelefttoright \alignsimplelefttoright\conditionaltrue

\permanent\protected\def\simplereversealignedbox#1#2%
  {\hbox \ifdim#1>\zeropoint to #1
     \ifcsname\??alignsimplereverse#2\endcsname
       \doubleexpandafter\lastnamedcs
     \else
       \doubleexpandafter\spac_align_simple_left
     \fi
   \fi}

\permanent\protected\def\simplereversealignedboxplus#1#2#3%
  {\hbox #3 \ifdim#1>\zeropoint to #1
     \ifcsname\??alignsimplereverse#2\endcsname
       \doubleexpandafter\lastnamedcs
     \else
       \doubleexpandafter\spac_align_simple_left
     \fi
   \fi}

% Some obsolete (old) helpers:

\permanent\tolerant\protected\def\definehbox[#1]#*[#2]%
  {\ifarguments\else\frozen\instance\protected\defcsname hbox#1\endcsname##1{\hbox to #2{\begstrut##1\endstrut\hss}}\fi}

%D Some direction related helpers:

\installcorenamespace {reverse}

\defcsname\??reverse\v!normal \endcsname{\ifconditional\inlinelefttoright\else\s!reverse\fi}
\defcsname\??reverse\v!reverse\endcsname{\ifconditional\inlinelefttoright     \s!reverse\fi}

\permanent\def\usedirectionparameterreverse#1{\begincsname\??reverse#1\c!direction\endcsname}

%D Now official:

\permanent\protected\def\spaceorpar{\endgraf\ifhmode\space\fi}

%D Kind of documenting:
%D
%D \starttyping
%D \disabletrackers[builders.hpack.quality] % enabled by default
%D \enabletrackers[builders.hpack.collect]
%D \enabletrackers[builders.hpack.overflow]
%D
%D \starttext
%D     {\hsize 4cm \input tufte \par} \page
%D     {\hsize 8cm \input tufte \par} \page
%D \stoptext
%D \stoptyping

\protect \endinput