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

/*
 *   easyListing.t - a TADS 3 extension
 *   Email: emily.boegheim@gmail.com
 *
 *   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:emily.boegheim@gmail.com">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]
    ]
;

