# Source: Overload.js

    "use strict";

    /**
     * Allows a method to accept overloaded calls with different parameters controlling
     * which passed overload function is called.
     * @param {String=} name A name to provide this overload to help identify
     * it if any errors occur during the resolving phase of the overload. This
     * is purely for debug purposes and serves no functional purpose.
     * @param {Object} def The overload definition.
     * @returns {Function}
     * @constructor
     */
    var Overload = function (name, def) {
    	if (!def) {
    		def = name;
    		name = undefined;
    	}

    	if (def) {
    		var self = this,
    			index,
    			count,
    			tmpDef,
    			defNewKey,
    			sigIndex,
    			signatures;

    		if (!(def instanceof Array)) {
    			tmpDef = {};

    			// Def is an object, make sure all prop names are devoid of spaces
    			for (index in def) {
    				if (def.hasOwnProperty(index)) {
    					defNewKey = index.replace(/ /g, '');

    					// Check if the definition array has a * string in it
    					if (defNewKey.indexOf('*') === -1) {
    						// No * found
    						tmpDef[defNewKey] = def[index];
    					} else {
    						// A * was found, generate the different signatures that this
    						// definition could represent
    						signatures = this.generateSignaturePermutations(defNewKey);

    						for (sigIndex = 0; sigIndex < signatures.length; sigIndex++) {
    							if (!tmpDef[signatures[sigIndex]]) {
    								tmpDef[signatures[sigIndex]] = def[index];
    							}
    						}
    					}
    				}
    			}

    			def = tmpDef;
    		}

    		return function () {
    			var arr = [],
    				lookup,
    				type,
    				overloadName;

    			// Check if we are being passed a key/function object or an array of functions
    			if (def instanceof Array) {
    				// We were passed an array of functions
    				count = def.length;
    				for (index = 0; index < count; index++) {
    					if (def[index].length === arguments.length) {
    						return self.callExtend(this, '$main', def, def[index], arguments);
    					}
    				}
    			} else {
    				// Generate lookup key from arguments
    				// Copy arguments to an array
    				for (index = 0; index < arguments.length; index++) {
    					type = typeof arguments[index];

    					// Handle detecting arrays
    					if (type === 'object' && arguments[index] instanceof Array) {
    						type = 'array';
    					}

    					// Handle been presented with a single undefined argument
    					if (arguments.length === 1 && type === 'undefined') {
    						break;
    					}

    					// Add the type to the argument types array
    					arr.push(type);
    				}

    				lookup = arr.join(',');

    				// Check for an exact lookup match
    				if (def[lookup]) {
    					return self.callExtend(this, '$main', def, def[lookup], arguments);
    				} else {
    					for (index = arr.length; index >= 0; index--) {
    						// Get the closest match
    						lookup = arr.slice(0, index).join(',');

    						if (def[lookup + ',...']) {
    							// Matched against arguments + "any other"
    							return self.callExtend(this, '$main', def, def[lookup + ',...'], arguments);
    						}
    					}
    				}
    			}

    			overloadName = name !== undefined ? name : typeof this.name === 'function' ? this.name() : 'Unknown';

    			console.log('Overload Definition:', def);
    			throw('ForerunnerDB.Overload "' + overloadName + '": Overloaded method does not have a matching signature "' + lookup + '" for the passed arguments: ' + this.jStringify(arr));
    		};
    	}

    	return function () {};
    };

    /**
     * Generates an array of all the different definition signatures that can be
     * created from the passed string with a catch-all wildcard *. E.g. it will
     * convert the signature: string,*,string to all potentials:
     * string,string,string
     * string,number,string
     * string,object,string,
     * string,function,string,
     * string,undefined,string
     *
     * @param {String} str Signature string with a wildcard in it.
     * @returns {Array} An array of signature strings that are generated.
     */
    Overload.prototype.generateSignaturePermutations = function (str) {
    	var signatures = [],
    		newSignature,
    		types = ['array', 'string', 'object', 'number', 'function', 'undefined'],
    		index;

    	if (str.indexOf('*') > -1) {
    		// There is at least one "any" type, break out into multiple keys
    		// We could do this at query time with regular expressions but
    		// would be significantly slower
    		for (index = 0; index < types.length; index++) {
    			newSignature = str.replace('*', types[index]);
    			signatures = signatures.concat(this.generateSignaturePermutations(newSignature));
    		}
    	} else {
    		signatures.push(str);
    	}

    	return signatures;
    };

    Overload.prototype.callExtend = function (context, prop, propContext, func, args) {
    	var tmp,
    		ret;

    	if (context && propContext[prop]) {
    		tmp = context[prop];

    		context[prop] = propContext[prop];
    		ret = func.apply(context, args);
    		context[prop] = tmp;

    		return ret;
    	} else {
    		return func.apply(context, args);
    	}
    };

    module.exports = Overload;