#charset "us-ascii"

/* 
 *  Copyright (c) 2009 by Kevin Forchione. All rights reserved.
 *   
 *  This file is part of the TADS 3 Inherited Multi-Method 
 *  library extension
 *
 *  imm.t
 *
 *  Implements an inherited multi-method mechanism.
 */

#include <tads.h>


/* 
 *   Add a base function lookup to the multi-method registry.
 */
modify _multiMethodRegistry
{
    /* table of registered base functions */
    baseFuncTab_ = static new LookupTable(128, 256)
}
        
/*
 *   Modify the function to store information in the base function Lookup
 */
modify _multiMethodRegister(baseFunc, func, params)
{
    _baseMethodRegister(baseFunc, func, params);
    
    replaced(baseFunc, func, params);
}

/*
 *  This function stores base function and params as values in base function
 *  lookup, using func as the key.
 */
_baseMethodRegister(baseFunc, func, params)
{
    /* if there's no hash entry for the function yet, add one */
    local tab = _multiMethodRegistry.baseFuncTab_;

    /* set the entry as the base function for this multi-method */
    tab[func] = [baseFunc, params];
}

/*
 *   An analog to "inherited()" this function will attempt to find the 
 *   appropriate multi-method and execute it. 
 *
 *   Each parameter value requiring a type should be composed of a 2-element 
 *   list, the 1st element designating the type, the 2nd element the value 
 *   to be passed to the retrieved multi-method.
 *
 *  For instance, to invoke the following multi-method:
 *
 *      functionName(Type1 value1, value2, Type3, value3) {}
 *
 *  You would code the call as following from the calling multi-method:
 *
 *      functionName(TypeX value1, TypeY value2, TypeZ value3)
 *      {
 *           inheritedMultiMethod([Type1, value1], value2, [Type3, value3]);
 *      }
 */
inheritedMultiMethod([params])
{
    local func, result, mmFunc, typeList = [], valueList = [];
    
    /* get the invoked function */
    func        = t3GetStackTrace(2).func_;
    
    /* retrieve the associated entry from the base function lookup */
    result      = _multiMethodRegistry.baseFuncTab_[func];
    
    if (result)
    {
        /* 
         *   Build a "types" list and "values" list from the function's 
         *   parameters
         */
        params.forEach(new function(elm)
        {
            switch(dataType(elm))
            {
                case TypeList:
                    typeList    += elm[1];
                    valueList   += elm[2];
                    break;
                
                default:
                    typeList    += elm;
                    valueList   += elm;
            }
        });
        
        /* 
         *   Retrieve the multi-method for the base function and type list
         */
        mmFunc = getMultiMethodPointer(result[1], typeList...);
        
        /* if we have a multi-method, invoke it */
        if (mmFunc)
            return mmFunc(valueList...);
    }
    
    /* we don't have a multi-method, return nil */
    return nil;
}
