#include <adv3.h>
#include <en_us.h>

/*
*   easyListing.t - a TADS 3 extension
*   Email: [email protected]
*
*   This extension allows you to print simple lists (or vectors) of objects
*   using the message parameter substitution system. It works much like the
*   Inform 7 list-printing mechanism.
*
*   To print a list with indefinite articles, use "{a/list listName}". To print
*   a list with definite articles, use "{the/list listName}". To print a list
*   with no articles, use "{list listName}". Remember to register the list with
*   the message parameter substitution system first, for instance
*   "gMessageParams(listName);".
*
*   If you want finer-grained control over what your list looks like, you can
*   call "listName.showListWith(whicheverListerYouWantToUse)".
*
*   This extension is open source software licensed under the MIT Licence:
*
*   Copyright (c) 2013 Emily Boegheim
*
*   Permission is hereby granted, free of charge, to any person obtaining a
*   copy of this software and associated documentation files (the "Software"),
*   to deal in the Software without restriction, including without limitation
*   the rights to use, copy, modify, merge, publish, distribute, sublicense,
*   and/or sell copies of the Software, and to permit persons to whom the
*   Software is furnished to do so, subject to the following conditions:
*
*   The above copyright notice and this permission notice shall be included in
*   all copies or substantial portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
*   DEALINGS IN THE SOFTWARE.
*
*   Change history:
*
*   0.3
*   - renamed extension
*   - cleaned up the documentation a bit
*   - added licence information
*
*   0.2
*   - rewrote some of the code to be more respectful of the library code
*
*   0.1
*   - initial version
*/


/* Info on the extension, so it will show up in the list of credits. */
easyListingModuleID: ModuleID
   name = 'Easy Listing'
   byline = 'by Emily Boegheim'
   htmlByline = 'by <a href="mailto:[email protected]">Emily
       Boegheim</a>'
   version = '0.3'
;


/*
*   Adding methods to show and return lists, and to use the correct grammar
*   when referring to a list.
*/
modify Collection
   /* A generic method for showing this list with any given Lister. */
   showListWith(lister) {
       lister.showListAll(self, 0, nil);
   }

   /*
    *   Wrapper methods for particular listers - plainLister (uses
    *   indefinite pronouns), theLister (uses definite pronouns), and
    *   noArticleLister (uses no pronouns). Instead of printing the lists,
    *   these methods catch the output and return it. This is for message
    *   substitution parameters, which require a text value to be returned
    *   rather than printed.
    */
   getAList() {
       return mainOutputStream.captureOutput(new function
                                             {showListWith(SimpleLister);});
   }
   getTheList() {
       return mainOutputStream.captureOutput(new function
                                             {showListWith(theLister);});
   }
   getPlainList() {
       return mainOutputStream.captureOutput(new function
                                             {showListWith(noArticleLister);});
   }

   /*
    *   Decide whether the list is plural or not. If it has more than one
    *   object in it, or if the single object in it is plural, the list is
    *   plural.
    */
   isPlural() {
       return (length > 1 || (length == 1 && self[1].isPlural));
   }

   /*
    *   Methods for generating associated verbs - this is for the message
    *   parameter substitution system. We'll just delegate to Thing.
    */
   verbToBe() { return delegated Thing(); }
   verbWas { return delegated Thing(); }
   verbToHave { return delegated Thing(); }
   verbToDo = delegated Thing()
   verbToGo = delegated Thing()
   verbToCome = delegated Thing()
   verbToLeave = delegated Thing()
   verbToSee = delegated Thing()
   verbToSay = delegated Thing()
   verbMust = delegated Thing()
   verbCan = delegated Thing()
   verbCannot = delegated Thing()
   verbCant = delegated Thing()
   verbWill = delegated Thing()
   verbWont = delegated Thing()
   verbEndingS { return delegated Thing(); }
   verbEndingSD = delegated Thing()
   verbEndingSEd = delegated Thing()
   verbEndingSMessageBuilder_ = delegated Thing()
   verbEndingEs { return delegated Thing(); }
   verbEndingIes { return delegated Thing(); }

   /*
    *   Methods to return the correct pronouns for the list; again, for the
    *   message parameter substitution system.
    */
   itNom { return (isPlural ? 'they' : (length == 1 ? delegated self[1] : 'it')); }
   itObj { return (isPlural ? 'them' : (length == 1 ? delegated self[1] : 'it')); }
   itPossAdj { return (isPlural ? 'their' : (length == 1 ? delegated self[1] : 'its')); }
   itPossNoun { return (isPlural ? 'theirs' : (length == 1 ? delegated self[1] : 'its')); }
   itReflexive
   {
       return (isPlural ? 'themselves' : (length == 1 ? delegated self[1] : 'itself'));
   }
   thatNom { return (isPlural ? 'those' : (length == 1 ? delegated self[1] : 'that')); }
   thatIsContraction
   {
       return delegated Thing();
   }
   thatObj { return (isPlural ? 'those' : (length == 1 ? delegated self[1] : 'that')); }
   itIs { return delegated Thing(); }
   itIsContraction { return delegated Thing(); }

   /*
    *   The pronoun selector. This will only get called when there is a
    *   single item in the list and a method has been delegated to that
    *   item. So we'll delegate this to that single item as well.
    */
   pronounSelector = delegated self[1]

   /* "dummyName", for the "subj" message parameter substition trick. */
   dummyName = ''
;


/* Add new options for choosing which articles to use in a list. */
modify SimpleLister
   definiteArticle = ListerCustomFlag(1)
   noArticle = ListerCustomFlag(2)
;


/* Modify Thing.showListItem to check which article to use. */
modify Thing
   showListItemGen(options, pov, infoTab, stateNameProp)
   {
       local info;
       local st;
       local stName;

       /* get my visual information from the point of view */
       info = infoTab[self];

       /* check which article to use */
       local nameProp;
       if (options & SimpleLister.noArticle)
           nameProp = &name;
       else if (options & SimpleLister.definiteArticle)
           nameProp = &theName;
       else
           nameProp = &listName;

       /* show the item's list name */
       say(withVisualSenseInfo(pov, info, nameProp));

       /*
        *   If we have a list state with a name, show it.  Note that to
        *   obtain the state name, we have to pass a list of the objects
        *   being listed to the stateNameProp method of the state object;
        *   we're the only object we're showing for this particular
        *   display list element, so we simply pass a single-element list
        *   containing 'self'.
        */
       if ((st = getStateWithInfo(info, pov)) != nil
           && (stName = st.(stateNameProp)([self])) != nil)
       {
           /* we have a state with a name - show it */
           gLibMessages.showListState(stName);
       }
   }
;


/* The theLister prints a simple list with definite articles. */
theLister: SimpleLister
   showList(pov, parent, lst, options, indent, infoTab, parentGroup) {
       options = options | definiteArticle;
       inherited(pov, parent, lst, options, indent, infoTab, parentGroup);
   }
;

/* The noArticleLister prints a simple list with no articles. */
noArticleLister: SimpleLister
   showList(pov, parent, lst, options, indent, infoTab, parentGroup) {
       options = options | noArticle;
       inherited(pov, parent, lst, options, indent, infoTab, parentGroup);
   }
;

/* Add the new message parameters to the message builder. */
modify langMessageBuilder
   paramList_ = static inherited() + [
       ['list', &getPlainList, nil, nil, nil],
       ['the/list', &getTheList, nil, nil, nil],
       ['a/list', &getAList, nil, nil, nil]
   ]
;