text: 'use strict';
  
  // Load modules
  
  const Hoek = require('hoek');
  
  
  // Declare internals
  
  const internals = {
      STATUS_CODES: Object.setPrototypeOf({
          '100': 'Continue',
          '101': 'Switching Protocols',
          '102': 'Processing',
          '200': 'OK',
          '201': 'Created',
          '202': 'Accepted',
          '203': 'Non-Authoritative Information',
          '204': 'No Content',
          '205': 'Reset Content',
          '206': 'Partial Content',
          '207': 'Multi-Status',
          '300': 'Multiple Choices',
          '301': 'Moved Permanently',
          '302': 'Moved Temporarily',
          '303': 'See Other',
          '304': 'Not Modified',
          '305': 'Use Proxy',
          '307': 'Temporary Redirect',
          '400': 'Bad Request',
          '401': 'Unauthorized',
          '402': 'Payment Required',
          '403': 'Forbidden',
          '404': 'Not Found',
          '405': 'Method Not Allowed',
          '406': 'Not Acceptable',
          '407': 'Proxy Authentication Required',
          '408': 'Request Time-out',
          '409': 'Conflict',
          '410': 'Gone',
          '411': 'Length Required',
          '412': 'Precondition Failed',
          '413': 'Request Entity Too Large',
          '414': 'Request-URI Too Large',
          '415': 'Unsupported Media Type',
          '416': 'Requested Range Not Satisfiable',
          '417': 'Expectation Failed',
          '418': 'I\'m a teapot',
          '422': 'Unprocessable Entity',
          '423': 'Locked',
          '424': 'Failed Dependency',
          '425': 'Unordered Collection',
          '426': 'Upgrade Required',
          '428': 'Precondition Required',
          '429': 'Too Many Requests',
          '431': 'Request Header Fields Too Large',
          '451': 'Unavailable For Legal Reasons',
          '500': 'Internal Server Error',
          '501': 'Not Implemented',
          '502': 'Bad Gateway',
          '503': 'Service Unavailable',
          '504': 'Gateway Time-out',
          '505': 'HTTP Version Not Supported',
          '506': 'Variant Also Negotiates',
          '507': 'Insufficient Storage',
          '509': 'Bandwidth Limit Exceeded',
          '510': 'Not Extended',
          '511': 'Network Authentication Required'
      }, null)
  };
  
  
  exports.wrap = function (error, statusCode, message) {
  
      Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object');
      return (error.isBoom ? error : internals.initialize(error, statusCode || 500, message));
  };
  
  
  exports.create = function (statusCode, message, data) {
  
      return internals.create(statusCode, message, data, exports.create);
  };
  
  
  internals.create = function (statusCode, message, data, ctor) {
  
      const error = new Error(message ? message : undefined);       // Avoids settings null message
      Error.captureStackTrace(error, ctor);                       // Filter the stack to our external API
      error.data = data || null;
      internals.initialize(error, statusCode);
      return error;
  };
  
  
  internals.initialize = function (error, statusCode, message) {
  
      const numberCode = parseInt(statusCode, 10);
      Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);
  
      error.isBoom = true;
      error.isServer = numberCode >= 500;
  
      if (!error.hasOwnProperty('data')) {
          error.data = null;
      }
  
      error.output = {
          statusCode: numberCode,
          payload: {},
          headers: {}
      };
  
      error.reformat = internals.reformat;
      error.reformat();
  
      if (!message &&
          !error.message) {
  
          message = error.output.payload.error;
      }
  
      if (message) {
          error.message = (message + (error.message ? ': ' + error.message : ''));
      }
  
      return error;
  };
  
  
  internals.reformat = function () {
  
      this.output.payload.statusCode = this.output.statusCode;
      this.output.payload.error = internals.STATUS_CODES[this.output.statusCode] || 'Unknown';
  
      if (this.output.statusCode === 500) {
          this.output.payload.message = 'An internal server error occurred';              // Hide actual error from user
      }
      else if (this.message) {
          this.output.payload.message = this.message;
      }
  };
  
  
  // 4xx Client Errors
  
  exports.badRequest = function (message, data) {
  
      return internals.create(400, message, data, exports.badRequest);
  };
  
  
  exports.unauthorized = function (message, scheme, attributes) {          // Or function (message, wwwAuthenticate[])
  
      const err = internals.create(401, message, undefined, exports.unauthorized);
  
      if (!scheme) {
          return err;
      }
  
      let wwwAuthenticate = '';
  
      if (typeof scheme === 'string') {
  
          // function (message, scheme, attributes)
  
          wwwAuthenticate = scheme;
  
          if (attributes || message) {
              err.output.payload.attributes = {};
          }
  
          if (attributes) {
              const names = Object.keys(attributes);
              for (let i = 0; i < names.length; ++i) {
                  const name = names[i];
                  if (i) {
                      wwwAuthenticate = wwwAuthenticate + ',';
                  }
  
                  let value = attributes[name];
                  if (value === null ||
                      value === undefined) {              // Value can be zero
  
                      value = '';
                  }
                  wwwAuthenticate = wwwAuthenticate + ' ' + name + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';
                  err.output.payload.attributes[name] = value;
              }
          }
  
          if (message) {
              if (attributes) {
                  wwwAuthenticate = wwwAuthenticate + ',';
              }
              wwwAuthenticate = wwwAuthenticate + ' error="' + Hoek.escapeHeaderAttribute(message) + '"';
              err.output.payload.attributes.error = message;
          }
          else {
              err.isMissing = true;
          }
      }
      else {
  
          // function (message, wwwAuthenticate[])
  
          const wwwArray = scheme;
          for (let i = 0; i < wwwArray.length; ++i) {
              if (i) {
                  wwwAuthenticate = wwwAuthenticate + ', ';
              }
  
              wwwAuthenticate = wwwAuthenticate + wwwArray[i];
          }
      }
  
      err.output.headers['WWW-Authenticate'] = wwwAuthenticate;
  
      return err;
  };
  
  
  exports.forbidden = function (message, data) {
  
      return internals.create(403, message, data, exports.forbidden);
  };
  
  
  exports.notFound = function (message, data) {
  
      return internals.create(404, message, data, exports.notFound);
  };
  
  
  exports.methodNotAllowed = function (message, data) {
  
      return internals.create(405, message, data, exports.methodNotAllowed);
  };
  
  
  exports.notAcceptable = function (message, data) {
  
      return internals.create(406, message, data, exports.notAcceptable);
  };
  
  
  exports.proxyAuthRequired = function (message, data) {
  
      return internals.create(407, message, data, exports.proxyAuthRequired);
  };
  
  
  exports.clientTimeout = function (message, data) {
  
      return internals.create(408, message, data, exports.clientTimeout);
  };
  
  
  exports.conflict = function (message, data) {
  
      return internals.create(409, message, data, exports.conflict);
  };
  
  
  exports.resourceGone = function (message, data) {
  
      return internals.create(410, message, data, exports.resourceGone);
  };
  
  
  exports.lengthRequired = function (message, data) {
  
      return internals.create(411, message, data, exports.lengthRequired);
  };
  
  
  exports.preconditionFailed = function (message, data) {
  
      return internals.create(412, message, data, exports.preconditionFailed);
  };
  
  
  exports.entityTooLarge = function (message, data) {
  
      return internals.create(413, message, data, exports.entityTooLarge);
  };
  
  
  exports.uriTooLong = function (message, data) {
  
      return internals.create(414, message, data, exports.uriTooLong);
  };
  
  
  exports.unsupportedMediaType = function (message, data) {
  
      return internals.create(415, message, data, exports.unsupportedMediaType);
  };
  
  
  exports.rangeNotSatisfiable = function (message, data) {
  
      return internals.create(416, message, data, exports.rangeNotSatisfiable);
  };
  
  
  exports.expectationFailed = function (message, data) {
  
      return internals.create(417, message, data, exports.expectationFailed);
  };
  
  
  exports.badData = function (message, data) {
  
      return internals.create(422, message, data, exports.badData);
  };
  
  
  exports.locked = function (message, data) {
  
      return internals.create(423, message, data, exports.locked);
  };
  
  
  exports.preconditionRequired = function (message, data) {
  
      return internals.create(428, message, data, exports.preconditionRequired);
  };
  
  
  exports.tooManyRequests = function (message, data) {
  
      return internals.create(429, message, data, exports.tooManyRequests);
  };
  
  
  exports.illegal = function (message, data) {
  
      return internals.create(451, message, data, exports.illegal);
  };
  
  
  // 5xx Server Errors
  
  exports.internal = function (message, data, statusCode) {
  
      return internals.serverError(message, data, statusCode, exports.internal);
  };
  
  
  internals.serverError = function (message, data, statusCode, ctor) {
  
      let error;
      if (data instanceof Error) {
          error = exports.wrap(data, statusCode, message);
      }
      else {
          error = internals.create(statusCode || 500, message, undefined, ctor);
          error.data = data;
      }
  
      return error;
  };
  
  
  exports.notImplemented = function (message, data) {
  
      return internals.serverError(message, data, 501, exports.notImplemented);
  };
  
  
  exports.badGateway = function (message, data) {
  
      return internals.serverError(message, data, 502, exports.badGateway);
  };
  
  
  exports.serverUnavailable = function (message, data) {
  
      return internals.serverError(message, data, 503, exports.serverUnavailable);
  };
  
  
  exports.gatewayTimeout = function (message, data) {
  
      return internals.serverError(message, data, 504, exports.gatewayTimeout);
  };
  
  
  exports.badImplementation = function (message, data) {
  
      const err = internals.serverError(message, data, 500, exports.badImplementation);
      err.isDeveloperError = true;
      return err;
  };
  