text: "main";'use strict';
  
  var copy = require('../connection/utils').copy,
    retrieveBSON = require('../connection/utils').retrieveBSON,
    KillCursor = require('../connection/commands').KillCursor,
    GetMore = require('../connection/commands').GetMore,
    Query = require('../connection/commands').Query,
    f = require('util').format,
    MongoError = require('../error').MongoError,
    getReadPreference = require('./shared').getReadPreference;
  
  var BSON = retrieveBSON(),
    Long = BSON.Long;
  
  var WireProtocol = function() {};
  
  //
  // Execute a write operation
  var executeWrite = function(pool, bson, type, opsField, ns, ops, options, callback) {
    if (ops.length === 0) throw new MongoError('insert must contain at least one document');
    if (typeof options === 'function') {
      callback = options;
      options = {};
      options = options || {};
    }
  
    // Split the ns up to get db and collection
    var p = ns.split('.');
    var d = p.shift();
    // Options
    var ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
    var writeConcern = options.writeConcern;
  
    // return skeleton
    var writeCommand = {};
    writeCommand[type] = p.join('.');
    writeCommand[opsField] = ops;
    writeCommand.ordered = ordered;
  
    // Did we specify a write concern
    if (writeConcern && Object.keys(writeConcern).length > 0) {
      writeCommand.writeConcern = writeConcern;
    }
  
    // Do we have bypassDocumentValidation set, then enable it on the write command
    if (options.bypassDocumentValidation === true) {
      writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
    }
  
    // Options object
    var opts = { command: true };
    if (typeof options.session !== 'undefined') opts.session = options.session;
    var queryOptions = { checkKeys: false, numberToSkip: 0, numberToReturn: 1 };
    if (type === 'insert') queryOptions.checkKeys = true;
    if (typeof options.checkKeys === 'boolean') queryOptions.checkKeys = options.checkKeys;
    // Ensure we support serialization of functions
    if (options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
    // Do not serialize the undefined fields
    if (options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
  
    try {
      // Create write command
      var cmd = new Query(bson, f('%s.$cmd', d), writeCommand, queryOptions);
      // Execute command
      pool.write(cmd, opts, callback);
    } catch (err) {
      callback(err);
    }
  };
  
  //
  // Needs to support legacy mass insert as well as ordered/unordered legacy
  // emulation
  //
  WireProtocol.prototype.insert = function(pool, ns, bson, ops, options, callback) {
    executeWrite(pool, bson, 'insert', 'documents', ns, ops, options, callback);
  };
  
  WireProtocol.prototype.update = function(pool, ns, bson, ops, options, callback) {
    executeWrite(pool, bson, 'update', 'updates', ns, ops, options, callback);
  };
  
  WireProtocol.prototype.remove = function(pool, ns, bson, ops, options, callback) {
    executeWrite(pool, bson, 'delete', 'deletes', ns, ops, options, callback);
  };
  
  WireProtocol.prototype.killCursor = function(bson, ns, cursorState, pool, callback) {
    var cursorId = cursorState.cursorId;
    // Create a kill cursor command
    var killCursor = new KillCursor(bson, ns, [cursorId]);
  
    // Build killCursor options
    const options = {
      immediateRelease: true,
      noResponse: true
    };
  
    if (typeof cursorState.session === 'object') {
      options.session = cursorState.session;
    }
  
    // Execute the kill cursor command
    if (pool && pool.isConnected()) {
      try {
        pool.write(killCursor, options, callback);
      } catch (err) {
        if (typeof callback === 'function') {
          callback(err, null);
        } else {
          _f3e‍.g.console.warn(err);
        }
      }
    }
  };
  
  WireProtocol.prototype.getMore = function(
    bson,
    ns,
    cursorState,
    batchSize,
    raw,
    connection,
    options,
    callback
  ) {
    // Create getMore command
    var getMore = new GetMore(bson, ns, cursorState.cursorId, { numberToReturn: batchSize });
  
    // Query callback
    var queryCallback = function(err, result) {
      if (err) return callback(err);
      // Get the raw message
      var r = result.message;
  
      // If we have a timed out query or a cursor that was killed
      if ((r.responseFlags & (1 << 0)) !== 0) {
        return callback(new MongoError('cursor does not exist, was killed or timed out'), null);
      }
  
      // Ensure we have a Long valie cursor id
      var cursorId = typeof r.cursorId === 'number' ? Long.fromNumber(r.cursorId) : r.cursorId;
  
      // Set all the values
      cursorState.documents = r.documents;
      cursorState.cursorId = cursorId;
  
      // Return
      callback(null, null, r.connection);
    };
  
    // Contains any query options
    var queryOptions = {};
  
    // If we have a raw query decorate the function
    if (raw) {
      queryOptions.raw = raw;
    }
  
    // Check if we need to promote longs
    if (typeof cursorState.promoteLongs === 'boolean') {
      queryOptions.promoteLongs = cursorState.promoteLongs;
    }
  
    if (typeof cursorState.promoteValues === 'boolean') {
      queryOptions.promoteValues = cursorState.promoteValues;
    }
  
    if (typeof cursorState.promoteBuffers === 'boolean') {
      queryOptions.promoteBuffers = cursorState.promoteBuffers;
    }
  
    if (typeof cursorState.session === 'object') {
      queryOptions.session = cursorState.session;
    }
  
    // Write out the getMore command
    connection.write(getMore, queryOptions, queryCallback);
  };
  
  WireProtocol.prototype.command = function(bson, ns, cmd, cursorState, topology, options) {
    // Establish type of command
    if (cmd.find) {
      return setupClassicFind(bson, ns, cmd, cursorState, topology, options);
    } else if (cursorState.cursorId != null) {
      return;
    } else if (cmd) {
      return setupCommand(bson, ns, cmd, cursorState, topology, options);
    } else {
      throw new MongoError(f('command %s does not return a cursor', JSON.stringify(cmd)));
    }
  };
  
  //
  // Execute a find command
  var setupClassicFind = function(bson, ns, cmd, cursorState, topology, options) {
    // Ensure we have at least some options
    options = options || {};
    // Get the readPreference
    var readPreference = getReadPreference(cmd, options);
    // Set the optional batchSize
    cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
    var numberToReturn = 0;
  
    // Unpack the limit and batchSize values
    if (cursorState.limit === 0) {
      numberToReturn = cursorState.batchSize;
    } else if (
      cursorState.limit < 0 ||
      cursorState.limit < cursorState.batchSize ||
      (cursorState.limit > 0 && cursorState.batchSize === 0)
    ) {
      numberToReturn = cursorState.limit;
    } else {
      numberToReturn = cursorState.batchSize;
    }
  
    var numberToSkip = cursorState.skip || 0;
    // Build actual find command
    var findCmd = {};
  
    // We have a Mongos topology, check if we need to add a readPreference
    if (topology.type === 'mongos' && readPreference) {
      findCmd['$readPreference'] = readPreference.toJSON();
    }
  
    // Add special modifiers to the query
    if (cmd.sort) findCmd['$orderby'] = cmd.sort;
    if (cmd.hint) findCmd['$hint'] = cmd.hint;
    if (cmd.snapshot) findCmd['$snapshot'] = cmd.snapshot;
    if (typeof cmd.returnKey !== 'undefined') findCmd['$returnKey'] = cmd.returnKey;
    if (cmd.maxScan) findCmd['$maxScan'] = cmd.maxScan;
    if (cmd.min) findCmd['$min'] = cmd.min;
    if (cmd.max) findCmd['$max'] = cmd.max;
    if (typeof cmd.showDiskLoc !== 'undefined') findCmd['$showDiskLoc'] = cmd.showDiskLoc;
    if (cmd.comment) findCmd['$comment'] = cmd.comment;
    if (cmd.maxTimeMS) findCmd['$maxTimeMS'] = cmd.maxTimeMS;
  
    if (cmd.explain) {
      // nToReturn must be 0 (match all) or negative (match N and close cursor)
      // nToReturn > 0 will give explain results equivalent to limit(0)
      numberToReturn = -Math.abs(cmd.limit || 0);
      findCmd['$explain'] = true;
    }
  
    // Add the query
    findCmd['$query'] = cmd.query;
  
    // Throw on majority readConcern passed in
    if (cmd.readConcern && cmd.readConcern.level !== 'local') {
      throw new MongoError(
        f('server find command does not support a readConcern level of %s', cmd.readConcern.level)
      );
    }
  
    // Remove readConcern, ensure no failing commands
    if (cmd.readConcern) {
      cmd = copy(cmd);
      delete cmd['readConcern'];
    }
  
    // Serialize functions
    var serializeFunctions =
      typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
    var ignoreUndefined =
      typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  
    // Build Query object
    var query = new Query(bson, ns, findCmd, {
      numberToSkip: numberToSkip,
      numberToReturn: numberToReturn,
      pre32Limit: typeof cmd.limit !== 'undefined' ? cmd.limit : undefined,
      checkKeys: false,
      returnFieldSelector: cmd.fields,
      serializeFunctions: serializeFunctions,
      ignoreUndefined: ignoreUndefined
    });
  
    // Set query flags
    query.slaveOk = readPreference.slaveOk();
  
    // Set up the option bits for wire protocol
    if (typeof cmd.tailable === 'boolean') {
      query.tailable = cmd.tailable;
    }
  
    if (typeof cmd.oplogReplay === 'boolean') {
      query.oplogReplay = cmd.oplogReplay;
    }
  
    if (typeof cmd.noCursorTimeout === 'boolean') {
      query.noCursorTimeout = cmd.noCursorTimeout;
    }
  
    if (typeof cmd.awaitData === 'boolean') {
      query.awaitData = cmd.awaitData;
    }
  
    if (typeof cmd.partial === 'boolean') {
      query.partial = cmd.partial;
    }
  
    // Return the query
    return query;
  };
  
  //
  // Set up a command cursor
  var setupCommand = function(bson, ns, cmd, cursorState, topology, options) {
    // Set empty options object
    options = options || {};
    // Get the readPreference
    var readPreference = getReadPreference(cmd, options);
  
    // Final query
    var finalCmd = {};
    for (var name in cmd) {
      finalCmd[name] = cmd[name];
    }
  
    // Build command namespace
    var parts = ns.split(/\./);
  
    // Serialize functions
    var serializeFunctions =
      typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  
    var ignoreUndefined =
      typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  
    // Throw on majority readConcern passed in
    if (cmd.readConcern && cmd.readConcern.level !== 'local') {
      throw new MongoError(
        f(
          'server %s command does not support a readConcern level of %s',
          JSON.stringify(cmd),
          cmd.readConcern.level
        )
      );
    }
  
    // Remove readConcern, ensure no failing commands
    if (cmd.readConcern) delete cmd['readConcern'];
  
    // We have a Mongos topology, check if we need to add a readPreference
    if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
      finalCmd = {
        $query: finalCmd,
        $readPreference: readPreference.toJSON()
      };
    }
  
    // Build Query object
    var query = new Query(bson, f('%s.$cmd', parts.shift()), finalCmd, {
      numberToSkip: 0,
      numberToReturn: -1,
      checkKeys: false,
      serializeFunctions: serializeFunctions,
      ignoreUndefined: ignoreUndefined
    });
  
    // Set query flags
    query.slaveOk = readPreference.slaveOk();
  
    // Return the query
    return query;
  };
  
  module.exports = WireProtocol;
  