/**
 * Module dependencies
 */
var util = require('util');
var _ = require('lodash');
var constants = require('./constants');



/**
 * factory
 *
 * @return {Switchback}
 *
 * An anonymous function is used as the base for switchbacks so that
 * they are both dereferenceable AND callable.  This allows functions
 * which accept switchback definitions to maintain compatibility with
 * standard node callback conventions (which are better for many situations).
 *
 * This also means that instantiated switchbacks may be passed interchangably
 * into functions expecting traditional node callbacks, and everything will
 * "just work".
 */

module.exports = function(callbackContext) {

  var _switch = function( /* err, arg1, arg2, ..., argN */ ) {
    var args = Array.prototype.slice.call(arguments);

    // Trigger error handler
    var err = args[0];
    if (err) {
      return _switch.error.apply(callbackContext || this, args);
    }
    return _switch.success.apply(callbackContext || this, args.slice(1));
  };

  // Mark switchback function so it can be identified for tests
  _switch[constants.telltale.key] = constants.telltale.value;

  // Mix in non-enumerable `.inspect()` method
  Object.defineProperty(this, 'inspect', { enumerable: false, writable: true });
  _switch.inspect = function () { return '[Switchback]'; };

  return _switch;
};

/**
 * Module dependencies
 */
var util = require('util');
var _ = require('lodash');
var constants = require('./constants');



/**
 * factory
 *
 * @return {Switchback}
 *
 * An anonymous function is used as the base for switchbacks so that
 * they are both dereferenceable AND callable.  This allows functions
 * which accept switchback definitions to maintain compatibility with
 * standard node callback conventions (which are better for many situations).
 *
 * This also means that instantiated switchbacks may be passed interchangably
 * into functions expecting traditional node callbacks, and everything will
 * "just work".
 */

module.exports = function(callbackContext) {

  var _switch = function( /* err, arg1, arg2, ..., argN */ ) {
    var args = Array.prototype.slice.call(arguments);

    // Trigger error handler
    var err = args[0];
    if (err) {
      // If the error has a `.exit` property that corresponds with the name of an exit,
      // call that exit instead of `error`. This makes it possible for `sb(e)` to work
      // as a type of clean, conventional error negotiation, allowing usage like:
      // ```
      // function (inputs, exits) {
      //   try { doStuff(); }
      //   catch (e) { return exits(e); }
      //   return exits();
      // }
      // ```
      if (_.isObject(err) && _.isString(err.exit) && _.isFunction(_switch[err.exit])) {
        return _switch[err.exit].apply(callbackContext || this, args);
      }
      return _switch.error.apply(callbackContext || this, args);
    }
    return _switch.success.apply(callbackContext || this, args.slice(1));
  };

  // Mark switchback function so it can be identified for tests
  _switch[constants.telltale.key] = constants.telltale.value;

  // Mix in non-enumerable `.inspect()` method
  Object.defineProperty(this, 'inspect', { enumerable: false, writable: true });
  _switch.inspect = function () { return '[Switchback]'; };

  return _switch;
};

Make sb(err) usage use the .exit property to try and negotiate a more appropriate handler.