%%%% Notes:
%
% 1. need to make sure to use lengths, not pgfmathmacros, for length parameters

\usepackage{tikz}
\usetikzlibrary{shapes.misc,shadings, decorations.pathmorphing, calc}
\usepgfmodule{oo}

\makeatletter

% 3 arguments: the first is an optional list of keys to modify attributes,
% the second is the label (also name of the subkey in box diagrams)
\newcommand\boxdiagram [2][]{
  \pgfkeys{/box diagrams/.cd, make box = #2, #2/.cd, #1, /.cd}
  \edef \n@w{\noexpand \pgfoonew \csname#2\endcsname=new box
    diagram(#2)}
  \n@w
}

% putting everything inside group means keys aren't changed upon returning
\newcommand\drawBd [2][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw()
  }
}

\newcommand\drawNoOutput [2][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw box()
   \n@w.draw input(-1)
   \n@w.draw input two(1)
  }
}

\newcommand\drawNoInput [2][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw box()
   \n@w.draw output(0)
  }
}

\newcommand\drawBox [2][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw box()
  }
}


% 3 arguments: #1 is optional list of key-value pairs, #2 is box, #3 is between -1
% and 1
\newcommand\drawInput[3][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw input(#3)
  }
}

% 3 arguments: #1 is optional list of key-value pairs, #2 is box, #3 is between -1
% and 1
\newcommand\drawInputTwo[3][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw input two(#3)
  }
}

\newcommand\drawOutput[3][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw output(#3)
  }
}

% 2 arguments: #1 is optional list of key-value pairs, #2 is box
\newcommand\drawHLine[2][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.draw hline()
  }
}

 % 3 arguments: #1 is box, #2
 % is coordinate,  and #3 is relative horizontal position
\newcommand\coordTopInput[3]{
  {\pgfkeys{/box diagrams/#1/.cd,}
   \def \n@w{\csname#1\endcsname} 
   \n@w.coord top input(#2, #3)
  }
}

\newcommand\coordTopBox[3]{
  {\pgfkeys{/box diagrams/#1/.cd,}
   \def \n@w{\csname#1\endcsname} 
   \n@w.coord top box(#2, #3)
  }
}

\newcommand\coordBottomBox[3]{
  {\pgfkeys{/box diagrams/#1/.cd,}
   \def \n@w{\csname#1\endcsname} 
   \n@w.coord bottom box(#2, #3)
  }
}

\newcommand\coordBottomOutput[4][]{
  {\pgfkeys{/box diagrams/#2/.cd, #1}
   \def \n@w{\csname#2\endcsname} 
   \n@w.coord bottom output(#3, #4)
  }
}

\newcommand\drawComma[1]{
  \def \n@w{\csname#1\endcsname} 
   \n@w.draw comma()}

\newcommand\drawPeriod[1]{
  \def \n@w{\csname#1\endcsname} 
  \n@w.draw period()}


\pgfkeys{
  /box diagrams/.cd,
  make box/.style = {
    % global attributes
    #1/label/.initial = #1,
    #1/label color/.initial = black,
    #1/coord/.initial = {(0,0)},
    #1/width/.initial = .5cm,
    #1/box height/.initial = .5cm,
    #1/box fill color/.initial = black!20,
    % box line style attributes
    #1/box line color/.initial = black,
    #1/box line dec/.initial = {},
    #1/box line dec seg length/.initial = .2cm,
    #1/box line dec amp/.initial = 2,
    #1/box line dash pattern/.initial = thick,
    #1/box line width/.initial = .3mm,
    % input attributes
    #1/input height/.initial = .75cm,
    #1/input color/.initial = black,
    #1/input dec/.initial = {},
    #1/input dec amp/.initial = 2,
    #1/input dec seg length/.initial = .2cm,
    #1/input dash pattern/.initial ={},
    #1/input line width/.initial = .3mm,
    #1/input label/.initial = {},
    #1/input label position/.initial = right,
    % input two attributes
    #1/input two height/.initial = .75cm,
    #1/input two color/.initial = black,
    #1/input two dec/.initial = {},
    #1/input two dec amp/.initial = 2,
    #1/input two dec seg length/.initial = .2cm,
    #1/input two dash pattern/.initial ={},
    #1/input two line width/.initial = .3mm,
    #1/input two label/.initial = {},
    #1/input two label position/.initial = right,
    % output style attributes
    #1/output height/.initial = .5cm,
    #1/output color/.initial = black,
    #1/output dec/.initial = {},
    #1/output dec seg length/.initial = .2cm,
    #1/output dec amp/.initial = 2,
    #1/output dash pattern/.initial = thick,
    #1/output line width/.initial = .3mm,
    #1/output label/.initial = {},
    #1/output label position/.initial = right,
    % hline
    #1/hline left/.initial = -.6,
    #1/hline right/.initial = .6,
    #1/hline vert/.initial = .5,
    #1/hline color/.initial = gray,
    #1/hline dec/.initial = {},
    #1/hline dec amp/.initial = 2,
    #1/hline dec seg length/.initial = .2cm,
    #1/hline dash pattern/.initial = thin,
    #1/hline label/.initial = {},
    #1/hline label position/.initial = above,
},}

\pgfooclass{box diagram}{
  \attribute label;
  \attribute coord;
  % size
  \attribute width;
  \attribute input height;
  \attribute input two height;
  \attribute output height;
  \attribute box height;
  % colors
  \attribute input color;
    \attribute input two color;
  \attribute output color;
    \attribute box line color;
    \attribute box fill color;
    \attribute label color;
    \attribute dec color;
    % input style
    \attribute input dec;
     \attribute input dec amp;
     \attribute input dec seg length;
       \attribute input dash pattern;
       \attribute input line width;
       \attribute input label;
        \attribute input label position;
           % input two style
    \attribute input two dec;
     \attribute input two dec amp;
     \attribute input two dec seg length;
       \attribute input two dash pattern;
       \attribute input two line width;
       \attribute input two label;
        \attribute input two label position;
       % output style
       \attribute output dec;
       \attribute output dec amp;
        \attribute output dec seg length;
  \attribute output dash pattern;
  \attribute output label;
    \attribute output label position;
  \attribute output line width;
   % hline
    \attribute hline left;
    \attribute hline right;
    \attribute hline vert;
    \attribute hline color;
    \attribute hline dec;
    \attribute hline dec amp;
    \attribute hline dec seg length;
    \attribute hline dash pattern;
    \attribute hline label;
    \attribute hline label position;
         % box line style
       \attribute box line dec;
       \attribute box line dec amp;
        \attribute box line dec seg length;
  \attribute box line dash pattern;
  \attribute box line width;
  % initializer - argument is list of attributes
  \method init(#1){ 
    \pgfoothis.set attributes to key values(#1)
  }

  \method set attributes to key values(#1){
    \pgfooset{coord}{\pgfkeysvalueof{/box diagrams/#1/coord}}
    \pgfooset{label}{\pgfkeysvalueof{/box
        diagrams/#1/label}}
    % size
  \pgfooset{width}{\pgfkeysvalueof{/box
        diagrams/#1/width}}
    \pgfooset{input height}{\pgfkeysvalueof{/box
        diagrams/#1/input height}}
        \pgfooset{input two height}{\pgfkeysvalueof{/box
        diagrams/#1/input two height}}
        \pgfooset{box height}{\pgfkeysvalueof{/box
        diagrams/#1/box height}}
    \pgfooset{output height}{\pgfkeysvalueof{/box
        diagrams/#1/output height}}
    % color
         \pgfooset{input color}{\pgfkeysvalueof{/box
             diagrams/#1/input color}}
         \pgfooset{input two color}{\pgfkeysvalueof{/box
             diagrams/#1/input two color}}
         \pgfooset{output color}{\pgfkeysvalueof{/box
        diagrams/#1/output color}}
 \pgfooset{dec color}{\pgfkeysvalueof{/box
     diagrams/#1/dec color}}
         \pgfooset{box line color}{\pgfkeysvalueof{/box
        diagrams/#1/box line color}}
        \pgfooset{box fill color}{\pgfkeysvalueof{/box
            diagrams/#1/box fill color}}
                \pgfooset{label color}{\pgfkeysvalueof{/box
        diagrams/#1/label color}}
   % input style
    \pgfooset{input dec}{\pgfkeysvalueof{/box
        diagrams/#1/input dec}}
     \pgfooset{input dec amp}{\pgfkeysvalueof{/box diagrams/#1/input dec amp}} 
   \pgfooset{input dec seg length}{\pgfkeysvalueof{/box diagrams/#1/input dec seg length}}
    \pgfooset{input dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/input dash pattern}}
    \pgfooset{input line width}{\pgfkeysvalueof{/box
        diagrams/#1/input line width}}
    \pgfooset{input label}{\pgfkeysvalueof{/box
        diagrams/#1/input label}}
    \pgfooset{input label position}{\pgfkeysvalueof{/box
        diagrams/#1/input label position}}
       % input two style
    \pgfooset{input two dec}{\pgfkeysvalueof{/box
        diagrams/#1/input two dec}}
     \pgfooset{input two dec amp}{\pgfkeysvalueof{/box diagrams/#1/input two dec amp}} 
   \pgfooset{input two dec seg length}{\pgfkeysvalueof{/box diagrams/#1/input two dec seg length}}
    \pgfooset{input two dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/input two dash pattern}}
    \pgfooset{input two line width}{\pgfkeysvalueof{/box
        diagrams/#1/input two line width}}
    \pgfooset{input two label}{\pgfkeysvalueof{/box
        diagrams/#1/input two label}}
     \pgfooset{input two label position}{\pgfkeysvalueof{/box
        diagrams/#1/input two label position}}
    % output style
        \pgfooset{output dec}{\pgfkeysvalueof{/box
        diagrams/#1/output dec}}
     \pgfooset{output dec amp}{\pgfkeysvalueof{/box diagrams/#1/output dec amp}} 
   \pgfooset{output dec seg length}{\pgfkeysvalueof{/box diagrams/#1/output dec seg length}}
    \pgfooset{output dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/output dash pattern}}
    \pgfooset{output line width}{\pgfkeysvalueof{/box
        diagrams/#1/output line width}}
    \pgfooset{output label}{\pgfkeysvalueof{/box
        diagrams/#1/output label}}
        \pgfooset{output label position}{\pgfkeysvalueof{/box
        diagrams/#1/output label position}}
        % box line style
        \pgfooset{box line dec}{\pgfkeysvalueof{/box
        diagrams/#1/box line dec}}
     \pgfooset{box line dec amp}{\pgfkeysvalueof{/box diagrams/#1/box line dec amp}} 
   \pgfooset{box line dec seg length}{\pgfkeysvalueof{/box diagrams/#1/box line dec seg length}}
    \pgfooset{box line dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/box line dash pattern}}
    \pgfooset{box line width}{\pgfkeysvalueof{/box
        diagrams/#1/box line width}}
    \pgfooset{box line dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/box line dash pattern}}
    % hline
       \pgfooset{hline left}{\pgfkeysvalueof{/box
        diagrams/#1/hline left}}
    \pgfooset{hline right}{\pgfkeysvalueof{/box
        diagrams/#1/hline right}}
    \pgfooset{hline vert}{\pgfkeysvalueof{/box
        diagrams/#1/hline vert}}
    \pgfooset{hline color}{\pgfkeysvalueof{/box
        diagrams/#1/hline color}}
    \pgfooset{hline dec}{\pgfkeysvalueof{/box
        diagrams/#1/hline dec}}
     \pgfooset{hline dec amp}{\pgfkeysvalueof{/box
         diagrams/#1/hline dec amp}}
     \pgfooset{hline dec seg length}{\pgfkeysvalueof{/box
         diagrams/#1/hline dec seg length}}
    \pgfooset{hline dash pattern}{\pgfkeysvalueof{/box
        diagrams/#1/hline dash pattern}}
    \pgfooset{hline label}{\pgfkeysvalueof{/box
        diagrams/#1/hline label}}
        \pgfooset{hline label position}{\pgfkeysvalueof{/box
        diagrams/#1/hline label position}}
}
  
  \method get(#1,#2) {\pgfooget{#1}{#2}}
  \method set(#1,#2) {\pgfooset{#1}{#2}}
  \method let(#1,#2) {\pgfoolet{#1}#2}

  % two inputs: #1 is the name of the new coordinate, #2 (usually
  % between -1 and 1) is the relative position at the top of the diagram,
  % with -1 upper left corner, 1 upper right corner
  \method coord top input(#1,#2){
    \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \node[shape=coordinate] (#1) at
    ($(st@rt)+(#2*\pgfoovalueof{width},
    \pgfoovalueof{box height} + \pgfoovalueof{output height} + \pgfoovalueof{input height})$){};}

    % two inputs: #1 is the name of the new coordinate, #2 (usually
  % between -1 and 1) is the relative position at the top of the box,
  % with -1 upper left corner, 1 upper right corner
  \method coord top box(#1,#2){
     \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \node[shape=coordinate] (#1) at
    ($(st@rt)+(#2*\pgfoovalueof{width},
    \pgfoovalueof{box height} + \pgfoovalueof{output height})$){};}

  % two inputs: #1 is the name of the new coordinate, #2 (usually
  % between -1 and 1) is the relative position at the bottom of the box,
  % with -1 lower left corner, 1 lower right corner
  \method coord bottom box(#1,#2){
     \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \node[shape=coordinate] (#1) at
    ($(st@rt)+(#2*\pgfoovalueof{width},\pgfoovalueof{output height})$){};}
  
  % two inputs: #1 is the name of the new coordinate, #2 (usually
  % between -1 and 1) is the relative position at the bottom of the diagram,
  % with -1 lower left corner, 1 lower right corner
  \method coord bottom output(#1,#2){
     \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \node[shape=coordinate] (#1) at
    ($(st@rt)+(#2*\pgfoovalueof{width},0)$){};}
  
    \method draw() { 
    \pgfoothis.draw box();
    \pgfoothis.draw input(-1);
      \pgfoothis.draw input two(1);
\pgfoothis.draw output(0);
  }

  
\method draw box() {
\node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \begin{scope}[\pgfoovalueof{box line dash pattern}, \pgfoovalueof{box line color}, text = \pgfoovalueof{label color}]     
    \ifdim\pgfoovalueof{box height}=0cm {} 
    \else
    \node (t@st)[minimum width = 2*\pgfoovalueof{width}, minimum height =
    \pgfoovalueof{box height}, fill=\pgfoovalueof{box fill
      color}, draw, decorate, decoration = {\pgfoovalueof{box line
        dec}, segment length = \pgfoovalueof{box line dec seg length},
     amplitude =
 \pgfoovalueof{box line dec amp}}] at ($(st@rt) + (0,
    \pgfoovalueof{output height} + \pgfoovalueof{box height}/2)$)
    {\small{$\pgfoovalueof{label}$}};
    \fi
  \end{scope}
}
 

%  the input is expected to be between -1 and 1; relative position of input
  \method draw input(#1) {
    \begin{scope}[\pgfoovalueof{input dash pattern},color=\pgfoovalueof{input color}]
      \pgfoothis.coord top box(cor, #1);
    \draw[decorate, line width = \pgfoovalueof{input line width},
    decoration = {\pgfoovalueof{input dec}, amplitude =
      \pgfoovalueof{input dec amp}, segment length =
      \pgfoovalueof{input dec seg length}}] (cor) -- ($(cor) +
    (0,\pgfoovalueof{input height})$)  node[midway,
    \pgfoovalueof{input label position}]{\tiny{$\pgfoovalueof{input label}$}};
    \end{scope}
  }

%  the input is expected to be between -1 and 1; relative position of input two
  \method draw input two(#1) {
    \begin{scope}[\pgfoovalueof{input two dash pattern},color=\pgfoovalueof{input two color}]
      \pgfoothis.coord top box(cor, #1);
    \draw[decorate, line width = \pgfoovalueof{input two line width},
    decoration = {\pgfoovalueof{input two dec}, amplitude =
      \pgfoovalueof{input two dec amp}, segment length =
      \pgfoovalueof{input two dec seg length}}] (cor) -- ($(cor) +
    (0,\pgfoovalueof{input two height})$)  node[midway,
    \pgfoovalueof{input two label position}]{\tiny{$\pgfoovalueof{input two label}$}};
    \end{scope}
  }
  
   % 1 arg: number between -1 and 1 that gives relative position of output to be drawn
   \method draw output(#1){
    }
  
\method draw output(#1){
\begin{scope}[\pgfoovalueof{output dash pattern},color=\pgfoovalueof{output color}]
      \pgfoothis.coord bottom output(cor, #1);
      \draw[decorate, line width = \pgfoovalueof{output line width},
      decoration = {\pgfoovalueof{output dec}, amplitude =
        \pgfoovalueof{output dec amp}, segment length =
        \pgfoovalueof{output dec seg length}}] (cor) -- ++(0,
      \pgfoovalueof{output height}) node[midway, \pgfoovalueof{output
        label position}]{\tiny{$\pgfoovalueof{output label}$}};
      \end{scope}
    }

 \method draw hline(){
   \begin{scope}[\pgfoovalueof{hline dash pattern},]
    \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
    \draw[color=\pgfoovalueof{hline color}, decorate, decoration =
    {\pgfoovalueof{hline dec}, amplitude = \pgfoovalueof{hline dec
        amp}, segment length = \pgfoovalueof{hline dec seg length}}]
    ($(st@rt)+(\pgfoovalueof{hline left}*\pgfoovalueof{width}, \pgfoovalueof{box height} + \pgfoovalueof{output
      height} + \pgfoovalueof{hline vert}*\pgfoovalueof{input height})$) --
    ($(st@rt)+(\pgfoovalueof{hline right}*\pgfoovalueof{width}, \pgfoovalueof{box height} + \pgfoovalueof{output
      height}+ \pgfoovalueof{hline vert}*\pgfoovalueof{input
      height})$) node[midway, \pgfoovalueof{hline label position}]{\tiny{$\pgfoovalueof{hline label}$}};
   \end{scope}
 }

\method draw period(){
  \def\smallxshift{.1cm}
  \def\smallyshift{.05cm}
      \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
      \node at ($(st@rt) +
      (\pgfoovalueof{width}+\smallxshift,\smallyshift)$) {.};}

    \method draw comma(){
      \def\smallxshift{.1cm}
      \def\smallyshift{.05cm}
      \node[shape=coordinate] (st@rt) at \pgfoovalueof{coord} {};
      \node at ($(st@rt) + (\pgfoovalueof{width}+\smallxshift,\smallyshift)$) {,};}
}

\makeatother
%%% Local Variables:
%%% mode: latex
%%% End:

% Known issues:
% 1) can't use same name for two different box diagrams in same picture?
% 2) coordinate at top still uses pgfoo interface
% 3) can't use numbers in box names