text: "use strict";
  
  var Options = require("./options"),
      $Refs = require("./refs"),
      parse = require("./parse"),
      normalizeArgs = require("./normalize-args"),
      resolveExternal = require("./resolve-external"),
      bundle = require("./bundle"),
      dereference = require("./dereference"),
      url = require("./util/url"),
      maybe = require("call-me-maybe"),
      ono = require("ono");
  
  module.exports = $RefParser;
  module.exports.YAML = require("./util/yaml");
  
  /**
   * This class parses a JSON schema, builds a map of its JSON references and their resolved values,
   * and provides methods for traversing, manipulating, and dereferencing those references.
   *
   * @constructor
   */
  function $RefParser () {
    /**
     * The parsed (and possibly dereferenced) JSON schema object
     *
     * @type {object}
     * @readonly
     */
    this.schema = null;
  
    /**
     * The resolved JSON references
     *
     * @type {$Refs}
     * @readonly
     */
    this.$refs = new $Refs();
  }
  
  /**
   * Parses the given JSON schema.
   * This method does not resolve any JSON references.
   * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed
   * @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object.
   * @returns {Promise} - The returned promise resolves with the parsed JSON schema object.
   */
  $RefParser.parse = function (path, schema, options, callback) {
    var Class = this; // eslint-disable-line consistent-this
    var instance = new Class();
    return instance.parse.apply(instance, arguments);
  };
  
  /**
   * Parses the given JSON schema.
   * This method does not resolve any JSON references.
   * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed
   * @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object.
   * @returns {Promise} - The returned promise resolves with the parsed JSON schema object.
   */
  $RefParser.prototype.parse = function (path, schema, options, callback) {
    var args = normalizeArgs(arguments);
    var promise;
  
    if (!args.path && !args.schema) {
      var err = ono("Expected a file path, URL, or object. Got %s", args.path || args.schema);
      return maybe(args.callback, Promise.reject(err));
    }
  
    // Reset everything
    this.schema = null;
    this.$refs = new $Refs();
  
    // If the path is a filesystem path, then convert it to a URL.
    // NOTE: According to the JSON Reference spec, these should already be URLs,
    // but, in practice, many people use local filesystem paths instead.
    // So we're being generous here and doing the conversion automatically.
    // This is not intended to be a 100% bulletproof solution.
    // If it doesn't work for your use-case, then use a URL instead.
    var pathType = "http";
    if (url.isFileSystemPath(args.path)) {
      args.path = url.fromFileSystemPath(args.path);
      pathType = "file";
    }
  
    // Resolve the absolute path of the schema
    args.path = url.resolve(url.cwd(), args.path);
  
    if (args.schema && typeof args.schema === "object") {
      // A schema object was passed-in.
      // So immediately add a new $Ref with the schema object as its value
      var $ref = this.$refs._add(args.path);
      $ref.value = args.schema;
      $ref.pathType = pathType;
      promise = Promise.resolve(args.schema);
    }
    else {
      // Parse the schema file/url
      promise = parse(args.path, this.$refs, args.options);
    }
  
    var me = this;
    return promise
      .then(function (result) {
        if (!result || typeof result !== "object" || Buffer.isBuffer(result)) {
          throw ono.syntax('"%s" is not a valid JSON Schema', me.$refs._root$Ref.path || result);
        }
        else {
          me.schema = result;
          return maybe(args.callback, Promise.resolve(me.schema));
        }
      })
      .catch(function (e) {
        return maybe(args.callback, Promise.reject(e));
      });
  };
  
  /**
   * Parses the given JSON schema and resolves any JSON references, including references in
   * externally-referenced files.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved
   * @param {function} [callback]
   * - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references
   *
   * @returns {Promise}
   * The returned promise resolves with a {@link $Refs} object containing the resolved JSON references
   */
  $RefParser.resolve = function (path, schema, options, callback) {
    var Class = this; // eslint-disable-line consistent-this
    var instance = new Class();
    return instance.resolve.apply(instance, arguments);
  };
  
  /**
   * Parses the given JSON schema and resolves any JSON references, including references in
   * externally-referenced files.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved
   * @param {function} [callback]
   * - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references
   *
   * @returns {Promise}
   * The returned promise resolves with a {@link $Refs} object containing the resolved JSON references
   */
  $RefParser.prototype.resolve = function (path, schema, options, callback) {
    var me = this;
    var args = normalizeArgs(arguments);
  
    return this.parse(args.path, args.schema, args.options)
      .then(function () {
        return resolveExternal(me, args.options);
      })
      .then(function () {
        return maybe(args.callback, Promise.resolve(me.$refs));
      })
      .catch(function (err) {
        return maybe(args.callback, Promise.reject(err));
      });
  };
  
  /**
   * Parses the given JSON schema, resolves any JSON references, and bundles all external references
   * into the main JSON schema. This produces a JSON schema that only has *internal* references,
   * not any *external* references.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
   * @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object
   * @returns {Promise} - The returned promise resolves with the bundled JSON schema object.
   */
  $RefParser.bundle = function (path, schema, options, callback) {
    var Class = this; // eslint-disable-line consistent-this
    var instance = new Class();
    return instance.bundle.apply(instance, arguments);
  };
  
  /**
   * Parses the given JSON schema, resolves any JSON references, and bundles all external references
   * into the main JSON schema. This produces a JSON schema that only has *internal* references,
   * not any *external* references.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
   * @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object
   * @returns {Promise} - The returned promise resolves with the bundled JSON schema object.
   */
  $RefParser.prototype.bundle = function (path, schema, options, callback) {
    var me = this;
    var args = normalizeArgs(arguments);
  
    return this.resolve(args.path, args.schema, args.options)
      .then(function () {
        bundle(me, args.options);
        return maybe(args.callback, Promise.resolve(me.schema));
      })
      .catch(function (err) {
        return maybe(args.callback, Promise.reject(err));
      });
  };
  
  /**
   * Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema.
   * That is, all JSON references are replaced with their resolved values.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
   * @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object
   * @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object.
   */
  $RefParser.dereference = function (path, schema, options, callback) {
    var Class = this; // eslint-disable-line consistent-this
    var instance = new Class();
    return instance.dereference.apply(instance, arguments);
  };
  
  /**
   * Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema.
   * That is, all JSON references are replaced with their resolved values.
   *
   * @param {string} [path] - The file path or URL of the JSON schema
   * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
   * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
   * @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object
   * @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object.
   */
  $RefParser.prototype.dereference = function (path, schema, options, callback) {
    var me = this;
    var args = normalizeArgs(arguments);
  
    return this.resolve(args.path, args.schema, args.options)
      .then(function () {
        dereference(me, args.options);
        return maybe(args.callback, Promise.resolve(me.schema));
      })
      .catch(function (err) {
        return maybe(args.callback, Promise.reject(err));
      });
  };
  