/** var $func = pulp.func */
pulp.Modules.func = '$func';

(function() {
  
  var $p = pulp.base;
  
  /**
   * @class  Static methods that alter Functions
   * @name pulp.func
   * @requires base
   */
  var self = pulp.func = /** @scope pulp.func */{
    /**
     * @function  Return a new function bound to the given object scope
     * @param {Function} fn  The function to bind
     * @param {Object} context  The scope to bind
     * @return {Mixed}
     */
    bind: $p.bind,
  
    /**
     * Create a new version of a function that has one or more arguments preset
     * 
     * @param {Function} fn  The function to alter
     * @param {Mixed} arg1
     * @param {Mixed} [arg2]
     * @param {Mixed} [argN]
     * @return {Function}
     */
    curry: function(fn) {
      if (arguments.length < 2) return fn;
      var args = $p.makeArray(arguments);
      return function() {
        return fn.apply(this, args.concat($p.makeArray(arguments)));
      }
    },
    /**
     * Execute the given function after the given number of seconds have past
     * 
     * @param {Function} fn  The function to execute
     * @param {Number} timout  The number of seconds to wait
     * @return {Number}
     */  
    delay: function(fn, timeout) {
      var args = $p.makeArray(arguments);
      timeout *= 1000;
      return window.setTimeout(function() {
        return fn.apply(fn, args);
      }, timeout);
    },
    /**
     * Execute the given function in 10ms (effectively at the end of the call stack)
     * @param {Function} fn  The function to execute
     * @return {Number}
     */    
    defer: function(fn) {
      return self.delay(fn, 0.01);
    },
    /**
     * @function Call code before and or after a given function
     *
     * @param {Function} method
     * @param {Function} wrapper
     * @return {Function}
     * @example pulp.base.wrapFunction(myFn, function(proceed, arg1, arg2) {
     *   // code before
     *   proceed(); // call wrapped function
     *   // code after
     * }); 
     */  
    wrap: $p.wrapFunction,
    /**
     * @function  Return a function that executes the given function with the first argument set to the calling object or calling object property
     *
     * @param {Function} fn  The function to wrap
     * @param {String} [prop]  The property of the calling object to use instead of the calling object itself
     * @return {Function}
     */  
    methodize: $p.methodize,
    /**
     * Export pulp.func methods to Function.prototype
     * @return {pulp.func}
     */
    exportToPrototype: function() {
      return $p.each(self, function(fn, name) {
        if (typeof fn == 'function' && fn != pulp.func.exportToPrototype) {
          Function.prototype[name] = $p.methodize(fn);
        }
      });
    }    
  };

})();
