%
% CoDi: Commutative Diagrams for TeX
% Copyright (c) 2015-2024 Paolo Brasolin <paolo.brasolin@gmail.com>
% 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}}%
    }}
