%
% 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.
%

% μήτρα • (mítra)
%   1. womb
%   2. matrix
%   3. mold

% Mitra is an alternative parsing mechanism for TiKz matrices of nodes.
% It couples with ozos to ensure the node contents  always pass through the TikZ key.

\usetikzlibrary{matrix}
\usetikzlibrary{commutative-diagrams.koinos}

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

\pgfkeys{
 /mitra/every node/.style={},
 /mitra/every matrix/.style={
   /tikz/matrix,
   /tikz/cells={
     /tikz/anchor=base
   }
 },
 % NOTE: the alias key *cannot* be put along with global node styles
 %   because of execution order.
 /mitra/matrix coordinates alias/.style={
   /tikz/alias=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn
 }
}

%==[ main macro ]===============================================================

\def\kDMitra
 {\kDMitraFetchMatrixThen
 {\kDMitraParseMatrixTableThen
  \kDMitraOutput}}

% General purpose scratch register.
\newtoks\kDMitraTmpTok

%==[ fetching routine ]=========================================================

% I use the general one implemented by koinos.
\let\kDMitraFetchMatrixThen\kDFetchOptAndGrpThen

%==[ parsing routine: /table ]==================================================

\newtoks\kDMitraMatOutTok

\def\kDMitraParseMatrixTableThen#1{%
 \kDMitraMatOutTok={}%
 \expandafter\kDMitraParseTable\the\kDGrpTok\kD
 #1}

\def\kDMitraParseTable
 {\kDMitraParseAllRowsThen\relax}

\def\kDMitraParseAllRowsThen#1{%
 \def\kDExit{#1}%
 \kDIfNextHardCh\kD
   {\expandafter\kDExit\kDGobbleHardTok}%
   {\kDMitraParseOneRowThen{\kDMitraParseAllRowsThen{#1}}}}

%==[ parsing routine: /table/row ]==============================================

\def\kDMitraParseOneRowThen#1%
 {\kDMitraMarkRowEndBefore
 {\kDMitraParseAllColsThen
 {\kDMitraParseRowEndThen
 {#1}}}}

\kDStoreCatcodeOf &
\ifConTeXt\catcode`&=12\else\catcode`&=4\fi
\def\kDMitraMarkRowEndBefore#1#2\\{#1#2&\kD\\}
\kDRestoreCatcodeOf &

\def\kDMitraParseAllColsThen#1{%
 \def\kDExit{#1}%
 \kDIfNextHardCh\kD
   {\expandafter\kDExit\kDGobbleHardTok}%
   {\kDMitraParseOneColThen{\kDMitraParseAllColsThen{#1}}}}

\def\kDMitraParseRowEndThen#1%
 {\kDMitraFetchRowEndThen
 {\kDMitraPrintRowEndThen
 {#1}}}

\def\kDMitraFetchRowEndThen#1\\{\kDMitraMaybeFetchRowOptionsThen{#1}}

\newtoks\kDMitraRowOptTok

\def\kDMitraPrintRowEndThen#1{%
 \edef\kDAct{\noexpand\kDMitraTmpTok={\noexpand\\\the\kDMitraRowOptTok}}%
 \kDAct
 \kDAppend\kDMitraTmpTok\kDMitraMatOutTok
 #1}

\def\kDMitraMaybeFetchRowOptionsThen#1{%
 \kDMitraRowOptTok={}%
 \kDIfNextHardCh[%
   {\kDMitraFetchRowOptionsThen{#1}}%
   {#1}}

\def\kDMitraFetchRowOptionsThen#1[#2]{\kDMitraRowOptTok={[#2]}#1}

%==[ parsing routine: /table/row/column ]=======================================

\def\kDMitraParseOneColThen#1%
 {\kDMitraParseCellThen
 {\kDMitraParseColEndThen
 {#1}}}

\def\kDMitraParseColEndThen#1%
 {\kDMitraMaybeFetchColOptionsThen
 {\kDMitraMaybePrintColEndThen
 {#1}}}

\newtoks\kDMitraColOptTok

\def\kDMitraMaybeFetchColOptionsThen#1{%
 \kDMitraColOptTok={}%
 \kDIfNextHardCh[%
   {\kDMitraFetchColOptionsThen{#1}}%
   {#1}}

\def\kDMitraFetchColOptionsThen#1[#2]{\kDMitraColOptTok={[#2]}#1}

\def\kDMitraMaybePrintColEndThen#1{%
 \kDIfNextHardCh\kD
   {#1}%
   {% NOTE: we use \pgfmatrixnextcell instead of & to avoid catcode juggling
     \edef\kDAct{\noexpand\kDMitraTmpTok={\noexpand\pgfmatrixnextcell\the\kDMitraColOptTok}}%
     \kDAct
     \kDAppend\kDMitraTmpTok\kDMitraMatOutTok
     #1%
   }}

%==[ parsing routine: /table/row/column/cell ]==================================

\def\kDMitraParseCellThen#1%
 {\kDMitraMaybeFetchCellOptionsThen
 {\kDMitraFetchCellContentThen
 {\kDMitraPrintCellThen
 {#1}}}}

\newtoks\kDMitraCelOptTok

\def\kDMitraMaybeFetchCellOptionsThen#1{%
 \kDMitraCelOptTok={}%
 \kDIfNextHardCh|%
   {\kDMitraFetchCellOptionsThen{#1}}
   {#1}}

\def\kDMitraFetchCellOptionsThen#1|#2|{\kDMitraCelOptTok={#2}#1}

\newtoks\kDMitraCelCntTok

\kDStoreCatcodeOf &
\ifConTeXt\catcode`&=12\else\catcode`&=4\fi
\def\kDMitraFetchCellContentThen#1#2&{%
 \kDMitraCelCntTok={#2}%
 \kDTrimLeadingSpace\kDMitraCelCntTok
 \kDTrimTrailingSpace\kDMitraCelCntTok
 #1}
\kDRestoreCatcodeOf &

\def\kDMitraMaybeDoCellThen#1{%
 \kDIfNextHardCh\kD
   {#1}%
   {\kDMitraParseOneColThen{#1}}}

\def\kDMitraPrintCellThen#1{%
 \edef\kDAct{%
   \noexpand\kDMitraTmpTok={%
     \noexpand\node
       [/mitra/every node]
       \the\kDMitraCelOptTok
       [node contents={\the\kDMitraCelCntTok},/mitra/matrix coordinates alias];
     \kDMitraMaybeDumpCell}}%
 \kDAct
 \kDAppend\kDMitraTmpTok\kDMitraMatOutTok
 #1}

%==[ dumping routine ]==========================================================

\def\kDMitraMaybeDumpCell{\noexpand\path\noexpand\pgfextra{%
 \noexpand\kDDump{'\noexpand\the\noexpand\pgfmatrixcurrentrow-\noexpand\the\noexpand\pgfmatrixcurrentcolumn':}%
 \noexpand\kDDump{\space\space options: '\the\kDMitraCelOptTok'}%
 \noexpand\kDDump{\space\space content: '\the\kDMitraCelCntTok'}};}

%==[ output routine ]===========================================================

\def\kDMitraOutput{%
 \edef\kDAct{%
   \noexpand\kDMitraTmpTok={%
     \noexpand\matrix
       [/mitra/every matrix]%
       \the\kDOptTok
       {\the\kDMitraMatOutTok};}}%
 \kDAct
 \the\kDMitraTmpTok}