% \iffalse meta-comment
%% =============================================================================
%%
%% string-diagrams 0.2.1 (2023/06/13)
%%
%% Copyright (C) 2023 by Paolo Brasolin <paolo.brasolin@gmail.com>
%% SPDX-License-Identifier: LPPL-1.3c
%%
%% =============================================================================
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3c
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   https://www.latex-project.org/lppl.txt
%% and version 1.3c or later is part of all distributions of LaTeX
%% version 2008 or later.
%%
%% This work has the LPPL maintenance status `author-maintained'.
%%
%% The Current Maintainer of this work is Paolo Brasolin.
%%
%% This work consists of the files  README.md,
%%                                  string-diagrams.dtx,
%% and the derived files            string-diagrams.ins,
%%                                  string-diagrams.sty, and
%%                                  string-diagrams.pdf.
%%
%% =============================================================================
%%
%
% %%%=[ INSTALL ]===============================================================
%
%<*internal>
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX\else
  \expandafter\begingroup
\fi
%</internal>
%
%<*install>
\input l3docstrip.tex
\keepsilent
\askforoverwritefalse

% NOTE: to avoid redundancy we use `%%'-lines instead of pre/postambles
\preamble
\endpreamble
\nopostamble

\usedir{tex/latex/string-diagrams}
\generate{\file{\jobname.sty}{\from{\jobname.dtx}{package}}}

%</install>
%<*internal>

\usedir{source/latex/string-diagrams}
\generate{\file{\jobname.ins}{\from{\jobname.dtx}{install}}}

%</internal>
%
%<*internal>
\ifx\fmtname\nameofplainTeX
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%
%<install>\endbatchfile
%
% %%%=[ DRIVER ]================================================================
%
%<*driver>
\documentclass[a4paper,full]{l3doc}
\usepackage{parskip}

\NewDocumentCommand{\TikZ}{}{Ti\emph{k}Z}

\EnableCrossrefs
\CodelineIndex
\RecordChanges
\usepackage{string-diagrams}
\usetikzlibrary{calc}
\usepackage{snapshot}

\usepackage{tcolorbox}
\tcbuselibrary{listings, skins}

\lstset{
  language=[LaTeX]TeX,
  basicstyle=\MacroFont,
  columns=flexible,
  ^^A keywordstyle=\color{red},
  ^^A morekeywords={},
  texcsstyle=*\color{violet},
  moretexcs={node,wires},
  breaklines=true,
}

\tcbset{example/.style={
  listing engine=listings,
  verbatim ignore percent=true,
  listing side text,
  size=minimal,
  skin=bicolor,
  colback=black!5!white,
  colbacklower=white,
  sidebyside,
  lefthand ratio=0.62,
  listing options={
    xleftmargin=-1.6em, % poor man's gobble=4
  },
}}

\begin{document}
  \DocInput{\jobname.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
%
% \fi
%
% \changes{0.1.0}{2023/05/31}{initial version}
% \changes{0.2.0}{2023/06/12}{make box ports configurable}
%
% \GetFileInfo{\jobname.sty}
%
% \title{^^A
%   The \pkg{\jobname} package^^A
%   \thanks{Thanks!}\\^^A
%   \fileinfo^^A
% }
%
% \author{^^A
%   Paolo Brasolin\\^^A
%   \texttt{\href{mailto:paolo.brasolin@gmail.com}{paolo.brasolin@gmail.com}}^^A
% }
%
% \date{\fileversion~(\filedate)}
%
% \maketitle
%
% ^^A=[ DOCUMENTATION ]=========================================================
%
% \begin{documentation}
%
% \begin{tcolorbox}[
%   colback=red!5!white,
%   colframe=red,
%   sharp corners,
%   boxrule=1pt,
% ]
% Please note this is the \href{https://semver.org/#spec-item-4}{major version zero}, meant for initial development: \emph{anything MAY change at any time}.
% The upside is that this is the best time to \href{https://github.com/paolobrasolin/string-diagrams#contributing}{contribute}!
% Of course you can also just keep the \texttt{sty} along with your code and not care at all.
% \end{tcolorbox}
%
%
% \section{Documentation}
%
% \begin{function}[added=2023-05-31,updated=2023-06-12]{/pgf/box}
%
%   To draw boxes, you use this style on a node.
%
%   \begin{tcblisting}{example}
%     \begin{tikzpicture}
%       \node[box] {A};
%     \end{tikzpicture}
%   \end{tcblisting}
%
%   You can draw multiple boxes using any of your standard \TikZ\ positioning techniques.
%   Don't forget to label the nodes so you can easily reference them.
%
%   \begin{tcblisting}{example}
%     \begin{tikzpicture}
%       \node[box] (A) at (0,0) {A};
%       \node[box, right of=A] (B) {B};
%       \node[box] (C) at ($(B)+(2cm,1em)$) {C};
%     \end{tikzpicture}
%   \end{tcblisting}
% \end{function}
%
% \begin{function}[added=2023-06-12]{
%   /pgf/box ports north,
%   /pgf/box ports east,
%   /pgf/box ports south,
%   /pgf/box ports west,
% }
%   \begin{syntax}
%     /pgf/box ports north=\meta{integer}
%     /pgf/box ports east=\meta{integer}
%     /pgf/box ports south=\meta{integer}
%     /pgf/box ports west=\meta{integer}
%   \end{syntax}
%
%   You can open up any number of ports on any side of a box using the appropriate key.
%   Then, you can refer to the opened ports by their index.
%
%   \begin{tcblisting}{example,lefthand ratio=0.8}
%     \begin{tikzpicture}[
%       marker/.style={circle, fill, inner sep=1pt, text=white},
%     ]
%
%     \node[
%       box,
%       box ports north=3,
%       box ports east=3,
%       box ports south=3,
%       box ports west=3,
%       minimum width=6em,
%       minimum height=6em,
%     ] (A) {A};
%
%     \foreach \side in {north,east,south,west}
%       \foreach \index in {1,...,3}
%         \node[marker] at (A.\side.\index) {\index};
%
%     \end{tikzpicture}
%   \end{tcblisting}
%
% \end{function}
%
% \begin{function}[added=2023-06-12]{
%   /pgf/box ports,
% }
%   \begin{syntax}
%     /pgf/box ports=\meta{integer}/\meta{integer}/\meta{integer}/\meta{integer}
%   \end{syntax}
%
%   The \texttt{box ports} key is a shortcut to set the number of ports on all sides at once.
%
%   \begin{tcblisting}{example,lefthand ratio=0.8}
%     \begin{tikzpicture}[
%       marker/.style={circle, fill, inner sep=1pt},
%     ]
%
%     \node[box, box ports=1/2/3/4] (A) {A};
%
%     \foreach \side/\n in {north/1,east/2,south/3,west/4}
%       \foreach \index in {1,...,\n}
%         \node[marker] at (A.\side.\index) {};
%
%     \end{tikzpicture}
%   \end{tcblisting}
%
%   The same value can also be passed to the \texttt{box} key itself.
%
% \end{function}
%
% \begin{function}[added=2023-05-31,updated=2023-06-13]{\wires}
%   \begin{syntax}
%     \cs{wires}\oarg{\TikZ\ keys}\marg{connectivity}\marg{loose ends}
%   \end{syntax}
%
%   To connect boxes, you can use the \cmd\wires\ macro.
%   The first argument is \TikZ\ styling for the wires; the second argument is a nested dicionary specifying the connectivity; the third argument is a list of the loose ends to draw.
%   \texttt{box}es have the following anchors: \texttt{west}, \texttt{west.0}, \texttt{west.1},  \texttt{east}, \texttt{east.0}, and \texttt{east.1}.
%
%   \begin{tcblisting}{example}
%     \begin{tikzpicture}[scale=0.6]
%       \node[box=0/2/0/1] (A) at (-2, 0) {A};
%       \node[box=0/1/0/2] (B) at (+2, 0) {B};
%       \node[box=0/1/0/1] (C) at ( 0,+1) {C};
%       \node[box=0/1/0/1] (D) at ( 0,-1) {D};
%       \wires{
%         A = { east.1 = C.west, east.2 = D.west },
%         C = { east = B.west.1 },
%         D = { east = B.west.2 },
%       }{ A.west, B.east }
%     \end{tikzpicture}
%   \end{tcblisting}
%
% \end{function}
%
% \begin{function}[added=2023-05-31]{/pgf/dot}
%
%   To split and join wires, you can use \texttt{dot}s and their anchors \texttt{north}, \texttt{east}, \texttt{south}, and \texttt{west}.
%   Remember to have fun with styling wires.
%
%   \begin{tcblisting}{example}
%     \begin{tikzpicture}
%       \node[box=0/1/0/2] (A) at ( 0,+1) {A};
%       \node[box=0/2/0/1] (B) at ( 0,-1) {B};
%       \node[dot] (x) at (+1, 0) {};
%       \node[dot] (y) at (-1, 0) {};
%       \wires[looseness=1.5, dashed]{
%         A = { east = x.north },
%         B = { east.1 = x.south },
%         y = { north = A.west.2, south = B.west },
%       }{
%         A.west.1, B.east.2, x.east, y.west
%       }
%     \end{tikzpicture}
%   \end{tcblisting}
%
% \end{function}
%
% That's it. This is the package, for now.
%
% \end{documentation}
%
% ^^A=[ PACKAGE ]===============================================================
%
% \begin{implementation}
%
% \section{Implementation}
%
% Open the \pkg{DocStrip} guards and set the internal namespace prefix (as per \LaTeX3 \pkg{DocStrip} convention).
%    \begin{macrocode}
%<*package>
%<@@=stridi>
%    \end{macrocode}
%
% Load the essential support (\pkg{expl3}) \enquote{up-front}.
%
%    \begin{macrocode}
\RequirePackage{expl3}[2023/05/11]
\RequirePackage{tikz}[2023/01/15]
%    \end{macrocode}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage
  {string-diagrams}
  {2023/06/13}
  {0.2.1}
  {Draw string diagrams using TikZ}
%    \end{macrocode}
%
%  \begin{macro}{
%    /pgf/box ports north,
%    /pgf/box ports east,
%    /pgf/box ports south,
%    /pgf/box ports west,
%    /pgf/box ports,
%  }
%
%    Define high level keys to configure the number of ports on each side.
%    \begin{macrocode}
\pgfkeys{
  /pgf/box~ports~north/.initial=1,
  /pgf/box~ports~east/.initial=1,
  /pgf/box~ports~south/.initial=1,
  /pgf/box~ports~west/.initial=1,
  /pgf/box~ports/.style~args={#1/#2/#3/#4}{
    /pgf/box~ports~north=#1,
    /pgf/box~ports~east=#2,
    /pgf/box~ports~south=#3,
    /pgf/box~ports~west=#4,
  },
}
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\@@_intersect_hv_lines_through:NN}
%    Calculates the intersection of two lines parallel to axes passing through given points on the plane.
%    \begin{arguments}
%      \item Point through which the vertical line passes
%      \item Point through which the horizontal line passes
%    \end{arguments}
%    \begin{macrocode}
\cs_new:Nn \@@_intersect_hv_lines_through:NN {
  \pgfextractx { \pgf@xa } { #1 }
  \pgfextracty { \pgf@ya } { #2 }
  \pgfpoint { \pgf@xa } { \pgf@ya }
}
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_subdivide_segment:nNNNNN}
%   Defines macros numbering equally spaced points on a segment.
%   \begin{arguments}
%     \item Base namespace
%     \item Points count
%     \item Point containing the x coordinate of the starting point
%     \item Point containing the y coordinate of the starting point
%     \item Point containing the x coordinate of the ending point
%     \item Point containing the y coordinate of the ending point
%   \end{arguments}
%    \begin{macrocode}
\cs_new:Nn \@@_subdivide_segment:nNNNNN {
  \int_step_inline:nnnn { #2 } { -1 } { 1 } {
    \cs_if_exist:cTF
      { #1.##1 }
      { \prg_break: }
      { \prg_do_nothing: }
    \cs_new_nopar:cpn
      { #1.##1 }
      {
        \pgfmathdivide
          { 2 * ##1 - 1 }
          { 2 * #2 }
        \pgfpointlineattime
          { \pgfmathresult }
          { \@@_intersect_hv_lines_through:NN { #3 } { #4 } }
          { \@@_intersect_hv_lines_through:NN { #5 } { #6 } }
      }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{box}
%   \changes{0.2.0}{2023/06/12}{make ports configurable through \TikZ\ keys}
%
%   Define a rectangular shape with configurable ports.
%    \begin{macrocode}
\pgfdeclareshape{box}{

  % Inherit all the structure of rectangle
  \inheritsavedanchors[from=rectangle]
  \clist_map_inline:nn
    {
      north~west,  north, north~east,
            west, center,       east,
        mid~west,    mid,   mid~east,
       base~west,   base,  base~east,
      south~west,  south, south~east,
    }
    { \inheritanchor[from=rectangle]{#1} }
  \inheritanchorborder[from=rectangle]
  \inheritbackgroundpath[from=rectangle]

  % Dump port counts into saved macros
  \savedmacro\portsnorth
    {\pgfmathtruncatemacro\portsnorth{\pgfkeysvalueof{/pgf/box~ports~north}}}
  \savedmacro\portseast
    {\pgfmathtruncatemacro\portseast{\pgfkeysvalueof{/pgf/box~ports~east}}}
  \savedmacro\portssouth
    {\pgfmathtruncatemacro\portssouth{\pgfkeysvalueof{/pgf/box~ports~south}}}
  \savedmacro\portswest
    {\pgfmathtruncatemacro\portswest{\pgfkeysvalueof{/pgf/box~ports~west}}}

  % Add ports definitions to shape definition
  \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@box\endcsname{
    \@@_subdivide_segment:nNNNNN { pgf@anchor@box@north } { \portsnorth }
      { \southwest } { \northeast } { \northeast } { \northeast }
    \@@_subdivide_segment:nNNNNN { pgf@anchor@box@east } { \portseast }
      { \northeast } { \northeast } { \northeast } { \southwest }
    \@@_subdivide_segment:nNNNNN { pgf@anchor@box@south } { \portssouth }
      { \southwest } { \southwest } { \northeast } { \southwest }
    \@@_subdivide_segment:nNNNNN { pgf@anchor@box@west } { \portswest }
      { \southwest } { \northeast } { \southwest } { \southwest }
  }

}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{/pgf/box}
%   \changes{0.2.0}{2023/06/12}{acts as a shortcut for setting port counts}
%
%   Define style to draw boxes.
%    \begin{macrocode}
\ExplSyntaxOff
\tikzset{
  box/.default={0/0/0/0},
  box/.style args={#1}{
    shape=box,
    draw,
    inner sep=.5em,
    minimum width=2em,
    minimum height=2em,
    execute at begin node=$,
    execute at end node=$,
    /pgf/box ports=#1,
  },
}
\ExplSyntaxOn
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{/pgf/dot}
%
%   Define style to draw dots.
%    \begin{macrocode}
\ExplSyntaxOff
\tikzset{
  dot/.style={
    shape=circle,
    fill,
    inner sep=0,
    minimum width=0.4em,
  },
}
\ExplSyntaxOn
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_draw_bound_wires:nn}
%   Draws bound wires.
%   \begin{arguments}
%     \item \TikZ\ keys
%     \item dictionary of port labels
%   \end{arguments}
%    \begin{macrocode}
\cs_new:Nn \@@_draw_bound_wires:nn {
  \prop_set_from_keyval:Nn \l_tmpa_prop { #2 }
  \prop_map_inline:Nn \l_tmpa_prop
  {
    \prop_set_from_keyval:Nn \l_tmpb_prop { ##2 }
    \prop_map_inline:Nn \l_tmpb_prop
    {
      \regex_match_case:nn
      {
        { \. north } { \tl_gset:Nn \g_tmpa_tl { 90 } }
        { \. south } { \tl_gset:Nn \g_tmpa_tl { -90 } }
        { \. west } { \tl_gset:Nn \g_tmpa_tl { 180 } }
        { \. east } { \tl_gset:Nn \g_tmpa_tl { 0 } }
      } { ####2 }
      \regex_match_case:nn
      {
        { north } { \tl_gset:Nn \g_tmpb_tl { 90 } }
        { south } { \tl_gset:Nn \g_tmpb_tl { -90 } }
        { west } { \tl_gset:Nn \g_tmpb_tl { 180 } }
        { east } { \tl_gset:Nn \g_tmpb_tl { 0 } }
      } { ####1 }
      \draw [
        out={\tl_use:N \g_tmpb_tl},
        in={\tl_use:N \g_tmpa_tl},
        #1,
      ] (##1.####1) to (####2);
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_draw_loose_wires:nn}
%   Draws loose wires.
%   \begin{arguments}
%     \item \TikZ\ keys
%     \item list of port labels
%   \end{arguments}
%    \begin{macrocode}
\cs_new:Nn \@@_draw_loose_wires:nn {
  \clist_set:Nn \l_tmpa_clist { #2 }
  \clist_map_inline:Nn \l_tmpa_clist {
    \regex_match_case:nn
    {
      { \. north } { \draw[#1] (##1) -- +( 0,+1); } % TODO: cleaner solution?
      { \. south }
        {
          \draw[out=-90, in=0,#1] (##1)
            to ($(\pgf@picminx, \pgf@y)$);
        } % TODO: not sure why this works
      { \. west  } { \draw[#1] (##1) -- +(-1, 0); }
      { \. east  } { \draw[#1] (##1) -- +(+1, 0); }
    } { ##1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\wires}
%   \changes{0.2.1}{2023/06/13}{now correctly handles optional style parameter}
%   Define our main actor.
%    \begin{macrocode}
\NewDocumentCommand{\wires}{ O{} m m }
{
  \@@_draw_bound_wires:nn { #1 } { #2 }
  \@@_draw_loose_wires:nn { #1 } { #3 }
}
%    \end{macrocode}
% \end{macro}
%
% Close the \pkg{DocStrip} guards and call it a day.
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
