\NeedsTeXFormat{LaTeX2e}%
\ProvidesPackage{assoccnt}[2014/08/21 v0.3 -- Associate counters stepping]%
%%%
%% License: LaTeX Project Public License
%% Copyright (2014) Dr. Christian Hupfer 
%% Author: Christian Hupfer christian.hupfer@siebenfelsen.de
%%
%%%%
%%% Some options later on
%%%%

\RequirePackage{etoolbox}[2011/01/03 2.2]%
\RequirePackage{xkeyval}[2012/10/14 v2.6b]%


\def\@@assoccnt@@keys{@@assoccnt@@keyfamily}%



\newcounter{@@assoccnt@@internalcounter}%

\let\@@assoccnt@@standardstepcounter\stepcounter%
\let\@@assoccnt@@standardrefstepcounter\refstepcounter%


% Just for a quick suffix
\newcommand{\@@assoccnt@@associatedcounterslistsuffix}{%
AssociatedCountersList%
}%



%%% Internal macro to generate the name of the list of associated counters
\newcommand{\@@assoccnt@@generatelistname}[1]{%
% #1 Name of the driver counter
@@#1@@\@@assoccnt@@associatedcounterslistsuffix
}%

\newcommand{\@@assoccnt@@drivercounterlist}{%
@@assoccnt@@DriverCountersList%
}%

%%
%% Adds a driver counter to an internal administration list
\newcommand{\AddDriverCounter}[2][]{%
% #1 -> options
% #2 -> driver counter name
  \setkeys{\@@assoccnt@@keys}{#1}%
  \ifcsdef{\@@assoccnt@@drivercounterlist}{%
  }{%
    \csgdef{\@@assoccnt@@drivercounterlist}{}%
  }%
  \listcsgadd{\@@assoccnt@@drivercounterlist}{#2}%
}%


%%
%% Removes the complete list of driver counters
\newcommand{\ClearDriverCountersList}{%
  \ifcsdef{\@@assoccnt@@drivercounterlist}%
  {%
    % Clear the possible driver counter entry in the list, the trailing associated counters list 
    % is cleared as well
    \forlistcsloop{\ClearAssociatedCountersList}{\@@assoccnt@@drivercounterlist}%
  }{%
    % Nothing to be done
  }%
}%

\newcommand{\IsDriverCounter}[3]{%
  \ifcsdef{\@@assoccnt@@drivercounterlist}{%
    \ifinlistcs{#1}{\@@assoccnt@@drivercounterlist}{% True branch, it's in the list
      #2%
    }{% False branch, not in the list
      #3% 
    }%
  }{%  List does not exist at all --> expand false branch
    #3%
  }%
}% 

\newcommand{\@@assoccnt@@generateboundtocounterslist}[1]{%
#1@@boundtocounterslist%
}%


\newcommand{\@@assoccnt@@addassociatedcounter}[3][]{%
  \ifcsdef{\@@assoccnt@@generateboundtocounterslist{#3}}{%
    % The counter is already to some other counter, it should not be multiply associated!
  }{%
    \csgdef{\@@assoccnt@@generateboundtocounterslist{#3}}{}%
    \listcsgadd{\@@assoccnt@@generateboundtocounterslist{#3}}{#1}
    \ifcsdef{#2}{%
      \ifinlistcs{#3}{#2}{%
        % Do nothing, since counter is already in the list, prevent multiple list insertion
      }{%
        \listcsgadd{#2}{#3}%
      }%
    }{%
      % Nothing in here
    }%
  }%
}%



\newcommand{\AddAssociatedCounters}[3][]{%
% #1 --> options
% #2 --> driver counter 
% #3 --> CSV list of counters, that should be stepped, if the driver counter is stepped
\setkeys{\@@assoccnt@@keys}{#1}%
  \ifcsdef{\@@assoccnt@@generatelistname{#2}}{%
    %  % Nothing to be done --> List already exists
    \forcsvlist{\@@assoccnt@@addassociatedcounter[#2]{\@@assoccnt@@generatelistname{#2}}}{#3}%
    % Prevent accidental self - association
    \RemoveAssociatedCounter{#2}{#2}% 
  }{%
    \GenericError{}{Error: List for driver counter #2 does not exist -- Usage on line \the\inputlineno}{}{}
  }%
}%

% This command defines a list of counters, that should be stepped also if the driver counter
% is `\stepcounter`ed. 
% A self - association is not possible, as this would lead to inconsistent counting
\newcommand{\DeclareAssociatedCounters}[3][]{%
% #1 --> options
% #2 --> driver counter 
% #3 --> CSV list of other counters, that should be stepped, if the driver counter is stepped
\setkeys{\@@assoccnt@@keys}{#1}%
  \ifcsdef{\@@assoccnt@@generatelistname{#2}}{%
    %  % Nothing to be done --> List already exists
    \GenericWarning{}{Warning: List of associated counters for driver counter #2 already exists}%
  }{%
    \AddDriverCounter{#2}%
    \csgdef{\@@assoccnt@@generatelistname{#2}}{}%   Define some global list 
  }%
  % Now add the counter names from #3 to the list 
  % Note: Currently, it is not checked whether a counter is already added!
  \forcsvlist{\@@assoccnt@@addassociatedcounter[#2]{\@@assoccnt@@generatelistname{#2}}}{#3}%
  % Now remove an accidental self-association
  \RemoveAssociatedCounter{#2}{#2}% 
}%

%\@onlypreamble{\DeclareAssociatedCounters}%


%%



%%% A generic macro, that removes a list entry from the list by
%%% defining a temporary list and omitting the 
\newcommand{\@@assoccnt@@removefromlist}[2]{%
% #1 list name
% #2 element to be removed
  \ifcsdef{#1}{%
    \gdef\@@assoccnt@@mytemplist{}%
    \renewcommand*{\do}[1]{%
      \ifstrequal{##1}{#2}{
        % Later one some success routine etc. ???
      }{%
        \listgadd{\@@assoccnt@@mytemplist}{##1}%
      }%
    }%
    \dolistcsloop{#1}%
    \csxdef{#1}{\@@assoccnt@@mytemplist}%
  }{%
    % The list is not defined at all, can't remove something from something not existing...
  }%
}%



%% Remove a particular counter from the list 
\newcommand{\RemoveAssociatedCounter}[2]{%
% #1 arg: driver counter
% #2 arg: counter name to be removed
  \@@assoccnt@@removefromlist{\@@assoccnt@@generatelistname{#1}}{#2}%
}%

%% Remove a CSV list of counters from the list of associated counters 

\newcommand{\RemoveAssociatedCounters}[2]{%
% #1 arg: driver counter
% #2 arg: CSV list of counters to be removed
  \forcsvlist{\RemoveAssociatedCounter{#1}}{#2}%
}%

%% Remove all associated counters from the list 
\newcommand{\ClearAssociatedCountersList}[1]{%
% #1 arg: driver counter
  \csundef{\@@assoccnt@@generatelistname{#1}}%
  \@@assoccnt@@removefromlist{\@@assoccnt@@drivercounterlist}{#1}%
}%


%% Test if a counter is an associated counter of driver counter 
\newcommand{\IsAssociatedToCounter}[4]{%
% #1 arg: driver counter
% #2 arg: (possibly) associated counter
% #3 arg: Code for execution on true branch
% #4 arg: Code for execution on false branch
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%
    \ifinlistcs{#2}{\@@assoccnt@@generatelistname{#1}}{%
      #3%
    }{%
      #4%
    }%
  }{% List does not exist, so it's not associated
    #4%
  }%
}%


\newcommand{\IsAssociatedCounter}[3]{%
  \ifcsdef{\@@assoccnt@@generateboundtocounterslist{#1}}{% true branch
    \setcounter{@@assoccnt@@internalcounter}{0}%
    \renewcommand*{\do}[1]{%
      \@@assoccnt@@standardstepcounter{@@assoccnt@@internalcounter}%
    }%
    \dolistcsloop{\@@assoccnt@@generateboundtocounterslist{#1}}%
    \ifnumgreater{\number\value{@@assoccnt@@internalcounter}}{0}{%
      #2%
    }{%
      #3%
    }%
  }{%
    #3% 
  }% End of \ifcsdef false branch
}%


\newcommand{\GetDriverCounter}[1]{%
  \setcounter{@@assoccnt@@internalcounter}{0}%
  \renewcommand*{\do}[1]{%
    ##1%
    \ifnumequal{\number\value{@@assoccnt@@internalcounter}}{1}{%
      \listbreak}{%
      \@@assoccnt@@standardstepcounter{@@assoccnt@@internalcounter}%
    }%
  }%
  \dolistcsloop{\@@assoccnt@@generateboundtocounterslist{#1}}%
}%

\newcommand{\@@assoccnt@@showassociatedcounterlist}[1]{%
  \textcolor{red}{\textbf{#1{ }}}%
}%

\newcommand{\@@assoccnt@@showdrivercounterlist}[1]{%
  \textcolor{blue}{\textbf{#1}}
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%
    \forlistcsloop{\@@assoccnt@@showassociatedcounterlist}{\@@assoccnt@@generatelistname{#1}}%
  }{}%
  \par%
}%

\newcommand{\AssociationStatistics}[1][]{%
  \forlistcsloop{\@@assoccnt@@showdrivercounterlist}{\@@assoccnt@@drivercounterlist}%
}%



%%% The stepcounter wrapper for the standard stepcounter command
%%% This is just for convenience, if the command as to be improved/extended later on in future
\newcommand{\@@assoccnt@@stepcounter}[1]{%  
  \@@assoccnt@@standardstepcounter{#1}%
}%

%%% Not needed so far
\newcommand{\@@assoccnt@refstepcounter}[1]{%  
  \@@assoccnt@@standardrefstepcounter{#1}%
}%

%%%% Redefinition of the \stepcounter command 
\renewcommand{\stepcounter}[1]{%
  \@@assoccnt@@standardstepcounter{#1}%   Traditional behaviour, since this is expected!
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%    Check first, whether the list exists at all, 
    \forlistcsloop{\@@assoccnt@@stepcounter}{\@@assoccnt@@generatelistname{#1}}%  March through the list
  }{% 
    %No list -> do nothing at all
  }%
}%





%%%% Redefinition of the \stepcounter command 
\renewcommand{\refstepcounter}[1]{%
  \@@assoccnt@@standardstepcounter{#1}%   Traditional behaviour, since this is expected!
  \protected@edef\@currentlabel%
  {\csname p@#1\endcsname\csname the#1\endcsname}%
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%    Check first, whether the list exists at all, 
    \forlistcsloop{\@@assoccnt@@stepcounter}{\@@assoccnt@@generatelistname{#1}}%  March through the list
  }{% 
    %No list -> do nothing at all
  }%
}%


\endinput%