%D \module
%D   [      file=t-xdesc,
%D        version=2001.11.09,
%D          title=\CONTEXT\ Style File,
%D       subtitle=Extended Descriptions,
%D         author=Giuseppe Bilotta,
%D           date=\currentdate,
%D      copyright={Giuseppe Bilotta}]

%M \usemodule[xdesc]

%D The need for extended descriptions arose while compiling an
%D \RPG\ manual. I needed a special way to treat descriptions
%D of, say, spells, or monsters. In such a situation, the
%D \quotation{object} (spell, monster) has lots of attributes,
%D plus a \quotation{standard} description. This information, once
%D collected, can be typeset in various ways (the expert reader
%D will see \XML\ shadow on this), so we could talk about a
%D \quotation{template} to be filled with the information.
%D
%D The final intent of this module is to allow the user to type
%D something like
%D \startTEX
%D \defineextendeddescription[spell][spells]%
%D                           [class,level,range,save,name]
%D \stopTEX
%D to define a new description set with attributes \attr{class},
%D \attr{level}, \attr{range}, \attr{save}. All descriptions have
%D one default slot, namely \slot{content}. It holds the description
%D of the object.
%D
%D The actual descriptions would then have to be input in the form
%D \startTEX
%D \spell[detmagic][class=Cleric,level=1,range={$10''$},save=None
%D                  name={Detect Magic}]
%D       {This spell allows the cleric to detect the presence of a
%D        magical or enchanted object, which will glow when within
%D        range.}
%D \stopTEX
%D
%D Note that the previous \TeX\ code will not actually typeset
%D anything. But the whole info will be stored. Before an actual
%D spell can be typeset, one needs to define a template to be
%D filled by the info. Such a template will be defined with a
%D command like
%D \startTEX
%D \starttemplate[spell][long]
%D {\bf\em \getslot{name}} \par
%D {\bf Class:} \getslot{class} \par
%D {\bf Level:} \getslot{level} \par
%D {\bf Range:} \getslot{range} \par
%D {\bf Saving Throw:} \getslot{save} \par
%D \blank
%D \getslot{content}
%D \blank
%D \stoptemplate
%D \stopTEX
%D
%D Testing with templates revealed that it is possible to have
%D \quotation{conditional templates}, coded with
%D \tex{doifelse{\getslot{tagname}}{content}{true branch}{false branch}}.
%D this will execute the {\tt true branch} if the slot \slot{tagname}
%D has content {\tt content}; otherwise the {\tt false
%D branch} is executed.
%D 
%D To use a template, the user selects it with:
%D \startTEX
%D \selecttemplate[spell][long]
%D \stopTEX
%D and can then typesets the objects with a command as simple as:
%D \startTEX
%D \getspell[detmagic]
%D \stopTEX
%D
%D (Note to myself: there has been a change in the names of the
%D functions to comply with \CONTEXT\ use of \tex{get...} instead
%D of \tex{use...})
%D
%D (Second note to myself: a previous version of the template
%D definition abused the < and > keys to tag slots. I decided to
%D give this up in favour of those who use active angle brackets
%D for other purposes.)
%D
%D (Thirdt note to myself: older implementation had two default
%D slots (name and desc, the latter being the current content).
%D I firstly thought about using three of them (longcontent and
%D shortcontent instead of the single desc), but then decided
%D there was no reason to force it to others. This allows the
%D extended description structure to be used for epigraphs
%D as~well.)
%D
%D I'm currently working on lists of described items,
%D with commands like:
%D \startTEX
%D \completelistofspells[criterium={class=Cleric},
%D                       sortby=level,template=tname]
%D \stopTEX
%D which will list all the clerical spells, sorted by level, using
%D template {\tt tname}.
%D
%D Let's now see the code. We start with a small definition
%D needed because the \CONTEXT\ command
%D \tex{dosetvalue} keeps checking for \tex{k!} prefixed
%D variables, which is awfull since there is no \tex{dogetvalue}
%D to act the reverse when getting the values. I wonder why Hans
%D removed the \tex{dogetvalue} function~\dots\ anyway, when the
%D module is fully tested I will adapt it to the \CONTEXT\
%D multilingual interface, and this hack will be unnecessary.

\unprotect

\def\xdescsetvalue#1#2{\@EA\def\csname #1#2\endcsname}
\def\xdescgetparameters{\dogetparameters\xdescsetvalue}

%D Let's now define the command which will parse the provided
%D attribute list and add these attributes to the attribute
%D list of the description being defined. Note that an '=' is
%D added if not present. This way of processing the list
%D allows the user to provide default values in the
%D definition, without having to add a '=' at the end of each
%D non||defaulted key.

\def\xdescsetattr#1%
 {\doifinstringelse{=}{#1}
      {\addtocommalist{#1}\templist}
      {\addtocommalist{#1=}\templist}
 }

%D We now define the command that creates a description; note the
%D use of \#? between brackes to eat up space and allow clean
%D coding.

\def\defineextendeddescription[#1]#2[#3]#4[#5]%
    {% Parameter #1 is the name of the description,
     % Parameter #3 is the plural,
     % Parameter #5 is the list of the attributes
     % Parameters #2 and #4 are only used to eat spaces. As a side
     % effect they eat all the stuff not included in square
     % brackets, which provides for a way to comment
     % without using \% signs in the code~\dots
     % 
     % Reset the attribute list
     \def\templist{}
     %
     % Fill it with the appropriate values
     \processcommalist[#5]\xdescsetattr
     % and store it
     \@EA\let\csname #1:attrlist\endcsname\templist
     % TODO: when multiple sorting will be available, we will need
     % to process the list in a different way: for each tagkey
     % (e.g. 'author') we will add a sort||related key (e.g.
     % 'sortauthor') to be used instead of author when sorting by
     % author. During assignment, when sort<tag> is not define, we
     % assign to it the value owned by <tag>.
     %
     % The next counter is used when the user does not provide
     % a handle for the object:
     \@EA\newcount\csname !!lastunused#1count\endcsname
     % We now define the start/stop pair. The stop pair allows us
     % to hook some function to the stop action --useful for
     % extended enumerations.
     \setvalue{\e!start#1}%
         {\dotripleempty\startfillslots[#1]}
     \expandafter\newtoks\csname everystop#1\endcsname
     \setvalue{\e!stop#1}%
         {\expandafter\the\csname everystop#1\endcsname}
     % We now call the command that puts the extended description
     % definition in the auxiliary file:
     \writexdescdeftouti{#1}
     % 
     % Let's now define the command which will typeset the
     % described object. By default, it will just print an error.
     % A maybe better idea is to let it print the data in 'raw'
     % form that is 'key=value' pairs, plus signaling with a warning
     % message the 'No template selected' error. Anyway, the real
     % command will be defined by the \tex{selecttemplate} macro.
     % We need the letvalue trick in case !get... is already
     % defined.
     \letvalue{!get#1}\relax
     \setevalue{get#1}%
         {\noexpand\dosingleempty\csname !get#1\endcsname}
     \@EA\def\csname !get#1\endcsname[##1]{No template selected!}
     % Let's now define the commands to list and sort
     % descriptions. This is still alpha code, since TeXUtils
     % itself does not handle this stuff, and I'm working at the
     % same time on the Perl code to handle this.
     \definetwopasslist{desc:#1}%
     \setvalue{placelistof#3}%
         {\dodoubleempty\!placelistofxdescs[#1]}%
     \setvalue{setuplistof#3}%
         {\dodoubleempty\!setuplistofxdescs[#1]}%
     \csname setuplistof#3\endcsname[before=,after=,
                                     sortby=,template=,
                                     selection=,]
     }

%D Here we define the commands common to all xdescs, starting
%D by the one that fills the slot when an xdesc is invoked.
%D When no handle is provided, we create a unique one.

\def\startfillslots[#1][#2][#3]%
  {\gdef\xdescfiller{\realstartfillslots[#1][#2][#3]}%
    \doifempty{#3}{%
      \doifassignmentelse{#2}%
        {\global\advance\csname !!lastunused#1count\endcsname1\relax%
          \xdef\xdescfiller{\noexpand\realstartfillslots[#1][#1\the\csname
          !!lastunused#1count\endcsname][#2]}%
        }{%
        {\doifempty{#2}%
          {\global\advance\csname !!lastunused#1count\endcsname1\relax%
            \xdef\xdescfiller{\noexpand\realstartfillslots[#1][#1\the\csname
            !!lastunused#1count\endcsname][#2]}%
          }%
        }%
      }%
    }%
    \xdescfiller}
       
%D Let's now define the command that will parse the
%D description. The first argument is the description name,
%D the second the (optional) description hook and the third the
%D (optional) list of attributes. The values are
%D stored in the category desc:description~name:hook; for
%D example, the command \tex{spell[stupid]} will store the
%D values in the category desc:spell:stupid.
%D This fills up string space very fast! Find an alternative?

\def\dofillslots#1#2#3{
   \xdescgetparameters[desc:#1:#2:][#3]%
}

\def\realstartfillslots[#1][#2][#3]%
  {% Instead of \getparameters we use
   % \dogetparameters\mysimplesetvalue for the
   % reasons specified when we defined
   % \mysimplesetvalue
   % We need an indirect call to expand the predefined
   % attribute list.
   \expanded{\dofillslots{#1}{#2}{\getvalue{#1:attrlist},#3}}%
   % This one is needed to be able to use \get<obj> without
   % tagging the last object:
   \@EA\def\csname desc:last#1\endcsname{#2}%
   \def\fillingxdesctype{#1}%
   \def\fillingxdeschandle{#2}%
   \grabuntil{\e!stop#1}\stopfillslots
    }%
\long\def\stopfillslots#1{%
   \@EA\long\@EA\def%
     \csname desc:\fillingxdesctype:\fillingxdeschandle:content\endcsname{#1}%
   \expanded{\writexdesctouti{\fillingxdesctype}{\fillingxdeschandle}}%
   \setvalue{desc:\fillingxdesctype:\fillingxdeschandle}{defined}%
   \getvalue{\e!stop\fillingxdesctype}%
   }

%D We now set up some commands to deal with the auxiliary files.
%D First of all, we set a conditional to test whether we actually
%D want to use the aux file or not.

\newif\ifxdesctoaux

\xdesctoauxtrue

%D The following command writes the stuff needed to tell the
%D TeXUtil plugin that an object type is being defined.

%D We define a macro that strips the equal signs from the
%D attribute list:

\def\xdescstripequal#1=#2\endxdescstrip{#1}
\def\xdescdefgetelement#1%
    {\edef\templist{\templist{\xdescstripequal#1\endxdescstrip}}}
\def\preparexdescdeflist#1%
    {\def\templist{}%
     \@EA\@EA\@EA\processcommalist\@EA\@EA\@EA[\csname#1:attrlist\endcsname]\xdescdefgetelement}

\def\writexdescdeftouti#1%
    {\ifxdesctoaux
       \preparexdescdeflist{#1}%
       \immediatewriteplugindata{xdesc}{%
         {define}{#1}{handle}\templist{content}%
       }%
     \fi
    }

%D The following command writes the stuff needed to tell the
%D TeXUtil plugin that an object is being defined.

\def\xdescgetelement#1%
    {\edef\templist%
         {\templist
           {\getvalue{desc:\xdesctype:\xdeschandle:\xdescstripequal#1\endxdescstrip}}%
         }%
    }

\def\preparexdesclist#1#2%
    {\def\xdesctype{#1}%
     \def\xdeschandle{#2}%
     \def\templist{}%
     \@EA\@EA\@EA\processcommalist\@EA\@EA\@EA[\csname#1:attrlist\endcsname]\xdescgetelement}

\newtoks\xdesccontenttoks

\def\writexdesctouti#1#2%
    {\ifxdesctoaux
       \preparexdesclist{#1}{#2}%
       \@EA\convertcommand\csname
       desc:#1:#2:content\endcsname\to\ascii
       \immediatewriteplugindata{xdesc}{%
         {element}{#1}{#2}\templist{\ascii}%
       }%
     \fi
    }

%D These commands are for the lists of xdescs
%D The options to set this up are the following:
%D \starttabulatie[|lB|l|]
%D \FL\NR
%D \NC before    \NC something to do before placement
%D                   of first element \NR
%D \NC after     \NC something to do after placement
%D                   of last element \NR
%D \NC template  \NC the template for each item \NR
%D \NC sortby    \NC comma separated list of sort criteria \NR
%D \NC selection \NC a macro which sets \tex{ifdisplayxdesc} after
%D                   determinining whether an element has to be listed
%D                   or not\NR
%D \LL\NR
%D \stoptabulatie

\newif\ifdisplayxdesc \displayxdesctrue
\newif\ifselectingxdescs \selectingxdescsfalse

\def\xdescbuildlistofcriteria#1%
    {\edef\listofcriteria{\listofcriteria {#1} }}

\newtoks\xdescauxsort

\def\!placelistofxdescs[#1][#2]%
    {\bgroup
     \ifsecondargument
        \!setuplistofxdescs[#1][#2]
     \fi
     %\edef\commalistofcriteria{\getvalue{descplace:#1:criterium}}%
     %\def\listofcriteria{}%
     %\@EA\processcommalist\@EA[\commalistofcriteria]\xdescbuildlistofcriteria%
     \def\listofcriteria{}% This will only be used if we find that
                          % Perl is a better selector
     \expanded{\xdescauxsort{%
       \noexpand\immediatewriteplugindata{xdesc}{%
         {sort}{#1}{\getvalue{descplace:#1:sortby}}\listofcriteria
       }%
     }}%
     \expandafter\doglobal\expandafter\appendtoks\the\xdescauxsort\to\everybye
     % real code goes here. Selection criteria are not implemented
     % yet, but that's a Perl thing.
     \gettwopassdata{desc:#1}%
     \iftwopassdatafound
         \!!placelistofxdescs{#1}\twopassdata
     \fi
     \egroup
    }

\def\!!placelistofxdescs#1#2%
    {\csname descplace:#1:before\endcsname
     \selectingxdescstrue%
     \selecttemplate[#1]%
              [\csname descplace:#1:template\endcsname]%
     \def\xdescplaceitem##1{\csname get#1\endcsname[##1]}%
     \edef\xdesclistofitems{#2}%
     \@EA\processcommalist\@EA[\xdesclistofitems]\xdescplaceitem%
     \selectingxdescsfalse%
     \csname descplace:#1:after\endcsname}


\def\!setuplistofxdescs[#1][#2]%
    {\xdescgetparameters
         [descplace:#1:]
         [#2]
    }

%D This defines the template macros

\long\def\starttemplate[#1][#2]#3\stoptemplate%
    {% Parameter #1 is the name of the application using the
     %              template
     % Parameter #2 is the name of the template itself. Note that
     %              different applications can have a template
     %              with the same name, without running the risk
     %              of conflict.
     % Parameter #3 is the actual template
     \@EA\long\@EA\gdef\csname template:#1:#2\endcsname%
         {\bgroup
          #3
          \egroup}%
    }

\def\selecttemplate[#1][#2]%
    {% Parameter #1 is the name of the application using the
     %              template
     % Parameter #2 is the name of the template itself. Note that
     %              different objects can have a template with
     %              the same name, without running into conflict.
     % The implementation is very simple since we got rid of
     % the angle brackets.
     \@EA\def\csname !get#1\endcsname[##1]%
         {\bgroup%
          \iffirstargument%
              \def\xdeschandle{##1}%
          \else%
              \edef\xdeschandle{\csname desc:last#1\endcsname}
          \fi%
          \edef\getslot####1{\noexpand\getvalue{desc:#1:\xdeschandle:####1}}
          \doifelsevalue{desc:#1:\xdeschandle}{defined}{%
            \ifselectingxdescs
               \csname descplace:#1:selection\endcsname
            \fi
            \ifdisplayxdesc
               \csname template:#1:#2\endcsname
            \fi
            }{#1 ##1 not defined}%
          \egroup%
         }%
    }

%D As an application to extended descriptions we implement
%D extended enumerations: they work more or less like
%D enumerations, except that they accept some optional parameters,
%D and these can be used to manipulate the \prepare<xenum> macro
%D called by the default template. This macro usually changes the
%D commands assigned to right=, left= depending on the content of
%D the keywords.
%D
%D We can also have lists of extended enumerations, and so on and
%D so forth.

\def\definexenum{\dotripleempty\dodefinexenum}

\def\dodefinexenum[#1][#2][#3]{%
  % #1 xenum name
  % #2 xenum plural
  % #3 xenum keywords --these get passed to the xdesc
  \defineextendeddescription[#1][#2][#3]%
  \appendtoks\csname get#1\endcsname\to{\csname everystop#1\endcsname}%
%
  \starttemplate[#1][normal]
    \csname prepare#1\endcsname
%
    \csname startenum#1\endcsname[\xdeschandle]
      \getslot{content}
    \csname stopenum#1\endcsname
%
  \stoptemplate
  \selecttemplate[#1][normal]%
  % And finally we define the relative enumeration,
  % which will take the following parameters, if present
  \defineenumeration[enum#1]
}

%D The plugin interface:

\installplugin{xdesc}{%
    \long\def\syncxdescs##1{}% Gobble argument on non-first passes
}{%
    \long\def\syncxdescs##1{%
    % On first pass, run argument outside of current group
        \long\gdef\runner{##1}%
        \aftergroup\runner
    }%
}

\loadplugindata{xdesc}

%D Closing:

\protect

\endinput

