\ProvidesPackage{charextracts}

% This package provides the magic parts of extraction from character sheets.
% It should only be used by character.sty, and it's highly unlikely that
% the Czar wants to fool around with it.



% No .tex file should ever define \WriteGameExtractsAlongPath.
% It is set on the command line when extraction is supposed to happen,
% e.g. from perl in Charsheets/DVI:
%% latex "\def\WriteGameExtractsAlongPath{../Extracts} \input ../foobar"
% Whether it is defined sets whether to do extraction:
%
\newif\if@extracting
\expandafter\ifx\csname WriteGameExtractsAlongPath\endcsname\relax
  \@extractingfalse
\else
  \@extractingtrue
  \newcommand\extract@prefix{\WriteGameExtractsAlongPath/\jobname^}
\fi


% generic counter to use here
\newcommand\e@n{}   % check name not taken
\newcount\e@n
% specific counter for multiplicities
\newcommand\en@multiplicity{}  % check name not taken
\newcount\en@multiplicity


% \if's always want to be defined so rest of this will *parse*
\newif\if@tex@extract@  % tex-type as opposed to list-type

%
% Now we'll have the other things that need to always be defined,
% and then the things that are only needed \if@extracting.
%

% Czar defines new tex-type extractable field in character.sty w \tex@extract
%
\newcommand\tex@extract[2]{
  % do necessary initialization for this extractable
  \expandafter\newcommand\csname extractable-#1\endcsname{%
    \if@extracting
      \@tex@extract@true
      \extract@open{#1}{#2}%
    \fi
  }
  \expandafter\let\csname suite-#1\endcsname\empty
}
%
% Czar defines new list-type extractable field in character.sty w \list@extract
%
\newcommand\list@extract[1]{
  % do necessary initialization for this extractable
  \expandafter\newcommand\csname extractable-#1\endcsname{%
    \if@extracting
      \@tex@extract@false
      \immediate\write\extract@list{EXTRACTABLE #1}%
    \fi
  }
  \expandafter\let\csname suite-#1\endcsname\empty
}


% When GMs do things in a charsheet that need to be passed along
% to extractables --- e.g. use \newpronoun or \newcommand or something
% to create a \foobar that they stick in a memory packet, item, \name,
% whatever --- they're supposed to do it inside a \frobbies at the top
% of the charsheet, so that it can get passed along to the extractables
% so that the extracted .tex for the mempackets etc doesn't break on it.
% Of course, they don't get passed to list-types, so GMs shouldn't
% be *too* enthusiastic about simultaneously giving list-type macros args
% and doing clever new-macro things in them.
%
\newcommand\frobbies@set{}
\newcommand\frobbies[1]{\add@to@macro\frobbies@set{#1}#1}
\@onlypreamble\frobbies


% Obviously \extract, \secret, \mention always want to be defined.
% If \@extractingfalse they won't actually do all that much, but
% they're a bit convoluted anyway from the combination of having
% an optional argument (multiplicity) and newdef-wise enforcing braces 
% around the mandatory argument (to avoid foot-shooting).
%
%
% Simplest first:  \mention doesn't do *anything* interesting
% beyond handle optional multiplicity and newdef brace behavior.
% To get the error messages to always be in terms of \mention,
% we define it back and forth.
% This first defn is the inner one and won't actually be around much.
\newdef\mention[1]{%
  \ifnum\en@multiplicity=1\relax
    \let\tell@multiplicity\empty
  \else
    \def\tell@multiplicity{(x\the\en@multiplicity)}%
  \fi
  \ifnum\en@multiplicity>0\relax
    % put in braces for grouping, to restrict \em-like changes
    \item \tell@multiplicity {#1}%
  \fi
  \let\mention\outer@mention
}
% Now we stash that away so that we can invoke it when desired.
\let\inner@mention\mention
% And now the value that \mention will usually have, which invokes the inner.
\newcommand\outer@mention[1][1]{%
  \en@multiplicity=#1\relax
  \let\mention\inner@mention
  \mention
}
% Now set \mention to its usual value.
\let\mention\outer@mention
%
%
% Follow that?  \extract is much the same (it even uses \inner@mention
% to do its \item typesetting (harmlessly resetting \mention to its outer),
% but it also actually Does Stuff.  In particular, it extracts things
% (multiplicity) times.  (\cleverlistsheet does so for list-types.)
% Oh --- and \extract[-N] becomes \secret[+N] and vice versa.
% Again, this first is just the inner defn.
\newdef\extract[1]{%
  \if@extracting
    \if@tex@extract@
      \e@n=0\relax
      \loop \ifnum\e@n<\en@multiplicity\relax
        \to@extract{#1}%
        \advance\e@n by 1\relax
      \repeat
    \else
      \let\listsheet\cleverlistsheet
      \def\listextracttype{NORMAL}%
    \fi
  \fi
  \inner@mention{#1}%  % why write the same ``list it'' code again?
  \let\extract\outer@extract
  \let\secret\outer@secret  % in case called as negative secret
}
% Stash that away for invocation by \outer@extract.
\let\inner@extract\extract
% The outer meaning that \extract will usually have.
\newcommand\outer@extract[1][1]{%
  \ifnum#1<0\relax
    \en@multiplicity=-#1\relax
    \let\extract\inner@secret
  \else
    \en@multiplicity=#1\relax
    \let\extract\inner@extract
  \fi
  \extract
}
% And set to the usual.
\let\extract\outer@extract
%
%
% Same song, next verse.  Negative \secret is positive \extract.
\newbox\secretbox
\newdef\secret[1]{%
  \if@extracting
    \if@tex@extract@
      \e@n=0\relax
      \loop \ifnum\e@n<\en@multiplicity\relax
        \add@to@macro\extract@secret{#1^^J}%
        \advance\e@n by 1\relax
      \repeat
    \else
      \let\listsheet\cleverlistsheet
      \def\listextracttype{SECRET}%
    \fi
  \fi
  \ifnum\en@multiplicity>0\relax
    \setbox\secretbox\hbox{#1}%  % evaluate code for global side effects (once)
  \fi
  \let\secret\outer@secret
  \let\extract\outer@extract % in case called as negative extract
}
\let\inner@secret\secret
\newcommand\outer@secret[1][1]{%
  \ifnum#1<0\relax
    \en@multiplicity=-#1\relax
    \let\secret\inner@extract
  \else
    \en@multiplicity=#1\relax
    \let\secret\inner@secret
  \fi
  \secret
}
\let\secret\outer@secret 



% In extractable ``suites,'' sets of extractables (often of different fields)
% common to multiple characters, we want to say ``when you get to the
% Abilities field, give the \Afoo and \Abar; when you get to Items, \Ibaz.''
% \augment{field name}{material} does the storage part, into
% \csname suite-(field name)\endcsname (initialized at field defn time),
% and \suitestuff in an extractable list pulls out all augments, then
% erases them to be idempotent.  \suitestuff is automatically called
% when the extractable env ends, so augments appear there by default.
% It is an error to never list an \augment'd field.
%
\newdef\augment[2]{%
  \expandafter\ifx\csname suite-#1\endcsname\relax
    \@die{can't \string\augment\space undefined field ``#1''}%
  \fi
  \expandafter\ifx\csname EXTRACTED-#1\endcsname\relax\else
    \@die{can't \string\augment\space already-listed field ``#1''}%
  \fi
  \expandafter\ifx\csname suite-#1\endcsname\empty
    \AtEndDocument{%
      \expandafter\ifx\csname suite-#1\endcsname\empty \else 
        \@die{#1 extractable field was augmented but never listed}%
      \fi
    }%
  \fi
  \expandafter\add@to@macro\csname suite-#1\endcsname{#2}%
}
\newdef\suitestuff{%
  \ifx\cur@extract\ThisMacroIsNotDefined
    \@die{can't call \string\suitestuff\space outside extractable field}%
  \fi
  \csname suite-\cur@extract\endcsname  % do all augments for this field
  \expandafter\gdef\csname suite-\cur@extract\endcsname{} % be idempotent
}



% The extractable environment itself, i.e. what happens when GMs
% do \begin{extractable}{Foo Bar} ... \end{extractable}.
% Does some magic itself, mostly safety checks, and invokes other magic.
%
\newcommand\NoBracesAllowedInExtractableName[1]{%
	\def\AnyBraceTest#1FakeName##1{FakeDef}}
\newenvironment{extractable}[1]
{%% do at \begin:
  % first check against nested extractable environments
  \ifx\cur@extract\ThisMacroIsNotDefined
    \def\cur@extract{#1} % automatically local to environment
  \else 
    \@die{cannot nest extractables (``#1'' is in ``\cur@extract'')}
  \fi
  % now check that this extractable hasn't been done already
  \expandafter\ifx\csname EXTRACTED-#1\endcsname\relax
    \expandafter\gdef\csname EXTRACTED-#1\endcsname{}%
  \else
    \@die{cannot give extractable #1 twice}
  \fi
  % check the extractable name has no braces in it
  \NoBracesAllowedInExtractableName{#1}%
  % check that it's a defined extractable, if so do its initialization
  \expandafter\ifx\csname extractable-#1\endcsname\relax
    \@die{no such extractable ``#1'' in character.sty}
  \else
    \csname extractable-#1\endcsname % has been set up to do initialization
  \fi
  % ok, now do the list
  \filbreak % try to keep list header + list items all together
  \noindent \textbf{#1}%
  \begin{tightlist}%
}%
{%% do at \end
  \suitestuff  % dump suite augments if they haven't been dumped already
  \end{tightlist}%
  \if@extracting\if@tex@extract@\extract@close\fi\fi
}


%
% The rest of this we only want when doing magic extraction, so
% if we're not, stop now.
%
\if@extracting \else \endinput \fi
%


% list-type information all goes to one place, the .Lists file, for perl
%
\newwrite\extract@list
\immediate\openout\extract@list=\extract@prefix.Lists

% \extract and \secret will do local redefines of \listsheet so that it DTRT,
% including not doing extraneous extractions from nesting
%
\newcommand\listextracttype{}  % will be set to NORMAL or SECRET
\let\normallistsheet\listsheet
\newdef\cleverlistsheet[2]{%
  \global\let\listsheet\normallistsheet
  % We extract these (multiplicity) times 'cause hey, might as well;
  % the Perl may or may not treat lists as just ``you're there or not.''
  \e@n=0\relax
  \loop \ifnum\e@n<\en@multiplicity  % set up by \extract or \secret
    \immediate\write\extract@list{\cur@extract: \listextracttype\space#1}%
    \advance\e@n by 1\relax
  \repeat
  #2%
}


% tex-type information, plus the \name tex information, goes various places
% we usually send things there literally with \to@extract
%
\newwrite\extract@tex
\newcommand\to@extract[1]{%
  \begingroup
    \tmp@str{#1}%
    \immediate\write\extract@tex{\the\tmp@str}%
  \endgroup
}


% want to turn names of extractable fields like ``Foo Bar Baz'' into FooBarBaz
% call ``\build@file@name Foo Bar Baz @ '' to build up \extract@file that way
%
\def\at@sign{@}%
\def\build@file@name#1 {%
  \def\@tmp{#1}%
  \ifx\@tmp\at@sign
  \else
    \add@to@macro\extract@file{#1}%
    \expandafter\build@file@name
  \fi
}


% called when tex-type extractable starts, in its \csname'd initialization
% open file for tex-type (or name) information
% gets called with args {extractable field}{package-use code}
% start the file with appropriate headers
%
\newcommand\extract@open[2]{%
  \def\extract@file{\extract@prefix}%
  \build@file@name#1 @ % trailing space needful
  \immediate\openout\extract@tex=\extract@file.tex
  \def\extract@secret{^^J\dosecretextracts^^J^^J}%  initialize, will add to it
  \to@extract{\documentclass{game}}%
  \to@extract{#2^^J}%
  \begingroup
    \to@extract{\note{Extracted Frobbies:}}%
    \tmp@str\expandafter{\frobbies@set}%
    \immediate\write\extract@tex{\the\tmp@str^^J}%
  \endgroup
  \to@extract{\begin{document}^^J}%
  \begingroup
    \tmp@str\expandafter{\me}%
    \immediate\write\extract@tex{\string\name{\the\tmp@str}^^J}%
  \endgroup
}

% gets called when tex-type extractable ends (if @extracting)
% spit out the \secret stuff and end the file
%
\def\extract@close{%
  \begingroup
    \tmp@str\expandafter{\extract@secret}%  % has been built up with secrets
    \immediate\write\extract@tex{\the\tmp@str}%
  \endgroup
  \to@extract{\end{document}}%
  \immediate\closeout\extract@tex
}

% Finally, if we're extracting (remember, if we're not we never got down
% to this code at all!) we want to make the name-only file that'll get
% turned into EPS for the char's other sheets to blip in; we treat
% that like a tex-type extractable, field ``.Name'' (dot prevents conflict),
% using the charname.sty package, which sets up for \name to DTRT.
% We also want to be polite and close the .Lists file.
%
\AtEndDocument{%
  \immediate\closeout\extract@list
  \extract@open{.Name}{\usepackage{charname}}%
  \extract@close
}


% We now return you to your regularly scheduled character.sty file.
