%
% CoDi: Commutative Diagrams for TeX
% Copyright (c) 2015-2024 Paolo Brasolin <[email protected]>
% SPDX-License-Identifier: MIT
%
% This file is part of CoDi 1.1.2, released on 2024/04/22 under MIT license.
%

% βέλος • (vélos)
%   1. arrow
%   2. dart
%   3. shaft

% Velos is a parsing mechanism for arrow chains.

\usetikzlibrary{commutative-diagrams.koinos}

%==[ TikZ/pgf layer ]===========================================================

\pgfkeys{
 % NOTE: there might be some way to work w/ quotes library, but it's safer and
 %   easier to just have a custom handler mounted in the maximally local scope
 %   and avoid any conflict a priori.
 /velos/install quote handler/.style={
   /handlers/first char syntax=true,
   /handlers/first char syntax/the character "/.initial=\kDVelosQuoteHandler,
   % NOTE: next line is required by recent versions of ConTeXt
   /handlers/first char syntax/the character U+0022 'quotation mark'/.initial=\kDVelosQuoteHandler,
   % node quotes mean={node contents={#1}},
   % edge quotes mean={node contents={#1}}
 },
 /velos/every path/.style={
   /velos/install quote handler
 }
}

\def\kDVelosQuoteHandler#1%
 {\pgfkeysalso{/tikz/node contents/.expand once={\kDVelosUnquote#1}}}

\def\kDVelosUnquote"#1"{#1}

%==[ global options ]===========================================================

\newtoks\kDVelosTemp

\newtoks\kDVelosGlobalLabelOptions
\newtoks\kDVelosGlobalEdgeOptions

\newif\ifkDVelosGOAfterColon

\def\kDVelosFetchGlobalOptionsThen#1{%
 \kDVelosGOAfterColonfalse
 \kDVelosGlobalLabelOptions={}%
 \kDVelosGlobalEdgeOptions={}%
 \kDVelosGOMaybeFetchThen{\kDVelosGOThinkThen{#1}}}

\def\kDVelosGOMaybeFetchThen#1{%
 \kDVelosTemp={}%
 \kDIfNextHardCh[%
   {\kDVelosGOFetchThen{#1}}%
   {#1}}

\def\kDVelosGOFetchThen#1[#2]{%
 \kDVelosTemp={#2}%
 #1}

\def\kDVelosGOThinkThen#1{%
 \def\kDVelosGOLoop{\kDVelosGOThinkThen{#1}}%
 \def\kDVelosGOBreak{#1}%
 \kDIfNextHardCh:%
   {%
     \kDVelosGOAfterColontrue
     \kDVelosGlobalLabelOptions\expandafter{\the\kDVelosTemp}%
     \expandafter\kDVelosGOMaybeFetchThen\expandafter\kDVelosGOLoop\kDGobbleHardTok
   }{%
     \ifkDVelosGOAfterColon
       \kDVelosGlobalEdgeOptions\expandafter{\the\kDVelosTemp}\else
       \kDVelosGlobalLabelOptions\expandafter{\the\kDVelosTemp}\fi
     \kDVelosGOBreak
   }}

%==[ first edge ]===============================================================

\def\kDVelosDoFirstEdgeThen#1%
 {\kDVelosFetchFirstSourceThen
 {\kDVelosFetchFirstEdgeThen
 {\kDVelosDrawFetchedEdgeThen
 {#1}}}}

\newtoks\kDVelosSource
\newtoks\kDVelosCurFstSrc
\newtoks\kDVelosPrvFstSrc

\def\kDVelosFetchFirstSourceThen#1%
 {\kDIfNextHardCh(%
   {\kDVelosFFSBracketsThen{#1}}%
   {\kDVelosFFSTillOverThen{#1}}}

\def\kDVelosFFSBracketsThen#1(#2){%
 \kDVelosSource={#2}%
 \kDVelosCurFstSrc={#2}%
 #1}

\def\kDVelosFFSTillOverThen#1#2 {% <- mind the space!
 \kDVelosSource={#2}%
 \kDVelosCurFstSrc={#2}%
 #1}

\def\kDVelosFetchFirstEdgeThen#1%
 {\kDVelosFetchEdgeThen
 {\kDVelosFetchTargetThen
 {#1}}}

%==[ chained edges ]============================================================

\newtoks\kDVelosEdge

\def\kDVelosFetchEdgeThen#1{%
 \kDVelosEdge={}%
 \kDVelosFEThen{\kDVelosFEThinkThen{#1}}}

\def\kDVelosFEThen#1%
 {\kDIfNextHardCh[%
   {\kDVelosFEKeysListThen{#1}}%
   {\kDIfNextHardCh"%
     {\kDVelosFEEnquotedThen{#1}}%
     {\kDVelosFETillOverThen{#1}}}}

\newif\ifkDVelosTempIsKeysList

\def\kDVelosFEEnquotedThen#1"#2"{%
 \kDVelosTempIsKeysListfalse
 \kDVelosTemp={#2}%
 #1}

\def\kDVelosFEKeysListThen#1[#2]{%
 \kDVelosTempIsKeysListtrue
 \kDVelosTemp={#2}%
 #1}

\def\kDVelosFETillOverThen#1{%
 \kDVelosTempIsKeysListfalse
 \kDVelosTemp={}%
 \kDVelosFETOThen{#1}}

\def\kDVelosFETOThen#1%
 {\kDIfNextSoftCh\kDBlankSpace
   {#1}%
   {\kDIfNextHardCh:%
     {#1}
     {\kDVelosFEAppendThen{\kDVelosFETOThen{#1}}}}}

\def\kDVelosFEAppendThen#1#2{%
 \kDVelosTemp=\expandafter{\the\kDVelosTemp#2}%
 #1}

\def\kDVelosFEEdgePrepend#1{%
 \edef\Act{\noexpand\kDVelosEdge={#1,\the\kDVelosEdge}}%
 \Act}

\def\kDVelosFEEdgePrependNode#1%
 {\kDVelosFEEdgePrepend{edge node={node[every edge quotes][/velos/install quote handler,#1]}}}

\def\kDVelosFEThinkThen#1{%
 \def\kDVelosFELoop{\kDVelosFEThinkThen{#1}}%
 \def\kDVelosFEBreak{#1}%
 \kDIfNextHardCh[%
   {%
     \kDVelosFEEdgePrependNode{\the\kDVelosTemp}%
     \kDVelosFEThen\kDVelosFELoop
   }{%
     \kDIfNextHardCh:%
       {%
         \ifkDVelosTempIsKeysList
           \kDVelosFEEdgePrependNode{\the\kDVelosTemp}\else
           \kDVelosFEEdgePrependNode{"\the\kDVelosTemp"}\fi
         \expandafter\kDVelosFEThen\expandafter\kDVelosFELoop\kDGobbleHardTok
       }{%
         \kDVelosFEEdgePrepend{\the\kDVelosTemp}%
         \kDVelosFEBreak
       }%
   }}

%==[ target ]===================================================================

\newtoks\kDVelosTarget
\newtoks\kDVelosCurLstTar
\newtoks\kDVelosPrvLstTar

\def\kDVelosFetchTargetThen#1%
 {\kDIfNextHardCh(%
   {\kDVelosFTBracketsThen{#1}}%
   {\kDVelosFTTillOverThen{#1}}}

\def\kDVelosFTBracketsThen#1(#2){%
 \kDVelosTarget={#2}%
 \kDVelosCurLstTar={#2}%
 #1}

\def\kDVelosFTTillOverThen#1{%
 \kDVelosTarget={}%
 \kDVelosFTTOThen{#1}}

\def\kDVelosFTTOThen#1{%
 \def\kDVelosFTTOLoop%
   {\kDVelosFTTOThen{#1}}%
 \def\kDVelosFTTOExit{%
   \kDVelosCurLstTar\expandafter{\the\kDVelosTarget}%
   #1}%
 \kDIfNextSoftCh\kDBlankSpace
   {\kDVelosFTTOExit}%
   {\kDIfNextHardCh;%
     {\kDVelosFTTOExit}%
     {\kDVelosFTAppendThen\kDVelosFTTOLoop}}}

\def\kDVelosFTAppendThen#1#2{%
 \kDVelosTarget=\expandafter{\the\kDVelosTarget#2}%
 #1}

%==[ rendering/dereferencing ]==================================================

\def\kDVelosAlias{*}
\def\kDVelosSaila{+}

\newtoks\kDVelosDerefSrc
\newtoks\kDVelosDerefTar

\def\kDVelosDrawFetchedEdgeThen#1{
 \edef\cS{\the\kDVelosSource}%
 \kDVelosDerefSrc\expandafter{\the\kDVelosSource}%
 \ifx\cS\kDVelosAlias\kDVelosDerefSrc\expandafter{\the\kDVelosPrvFstSrc}\fi
 \ifx\cS\kDVelosSaila\kDVelosDerefSrc\expandafter{\the\kDVelosPrvLstTar}\fi
 \edef\cT{\the\kDVelosTarget}%
 \kDVelosDerefTar\expandafter{\the\kDVelosTarget}%
 \ifx\cT\kDVelosAlias\kDVelosDerefTar\expandafter{\the\kDVelosPrvLstTar}\fi
 \ifx\cT\kDVelosSaila\kDVelosDerefTar\expandafter{\the\kDVelosPrvFstSrc}\fi
 \edef\Act{%
   \noexpand\path
     [/velos/every path]%
     [%
       /tikz/every edge/.append style=%
         {\the\kDVelosGlobalEdgeOptions},%
       /tikz/every edge quotes/.append style=%
         {auto,\the\kDVelosGlobalLabelOptions},%
     ]%
     (\the\kDVelosDerefSrc)%
     edge[\the\kDVelosEdge]%
     (\the\kDVelosDerefTar);}%
 \Act%
 #1}

%==[ high level ]===============================================================

\def\kDVelos%
 {\kDVelosFetchGlobalOptionsThen
 {\kDVelosDoFirstEdgeThen
  \kDVelosMaybeChainEdge}}

\def\kDVelosMaybeChainEdge{%
 \kDIfNextHardCh;%
   {%
   \edef\cS{\the\kDVelosCurFstSrc}%
   \ifx\cS\kDVelosAlias
     \kDVelosCurFstSrc\expandafter{\the\kDVelosPrvFstSrc}%
   \else\ifx\cS\kDVelosSaila
     \kDVelosCurFstSrc\expandafter{\the\kDVelosPrvLstTar}%
   \fi\fi
   \edef\cT{\the\kDVelosCurLstTar}%
   \ifx\cT\kDVelosAlias
     \kDVelosCurLstTar\expandafter{\the\kDVelosPrvLstTar}%
   \else\ifx\cT\kDVelosSaila
     \kDVelosCurLstTar\expandafter{\the\kDVelosPrvFstSrc}%
   \fi\fi
   \kDVelosPrvFstSrc\expandafter{\the\kDVelosCurFstSrc}%
   \kDVelosPrvLstTar\expandafter{\the\kDVelosCurLstTar}%
   \kDGobbleHardTok
   }{%
     \kDVelosSource\expandafter{\the\kDVelosTarget}
     \kDVelosFetchEdgeThen
       {\kDVelosFetchTargetThen
       {\kDVelosDrawFetchedEdgeThen
        \kDVelosMaybeChainEdge}}%
   }}