text: "use strict";
  
  const EventEmitter = require('events').EventEmitter;
  
  const TYPES = require('./data-type').typeByName;
  
  const RequestError = require('./errors').RequestError; // TODO: Figure out how to type the `rows` parameter here.
  
  
  class Request extends EventEmitter {
    // TODO: This should be `Connection`, not `any`.
    constructor(sqlTextOrProcedure, callback) {
      super();
      this.sqlTextOrProcedure = sqlTextOrProcedure;
      this.parameters = [];
      this.parametersByName = {};
      this.originalParameters = [];
      this.preparing = false;
      this.handle = undefined;
      this.canceled = false;
      this.paused = false;
      this.error = undefined;
      this.connection = undefined;
      this.timeout = undefined;
      this.userCallback = callback;
  
      this.callback = function (err) {
        if (this.preparing) {
          this.preparing = false;
  
          if (err) {
            this.emit('error', err);
          } else {
            this.emit('prepared');
          }
        } else {
          this.userCallback.apply(this, arguments);
          this.emit('requestCompleted');
        }
      };
    } // TODO: `type` must be a valid TDS value type
  
  
    addParameter(name, type, value, options) {
      if (options == null) {
        options = {};
      }
  
      const parameter = {
        type: type,
        name: name,
        value: value,
        output: options.output || (options.output = false),
        length: options.length,
        precision: options.precision,
        scale: options.scale
      };
      this.parameters.push(parameter);
      this.parametersByName[name] = parameter;
    } // TODO: `type` must be a valid TDS value type
  
  
    addOutputParameter(name, type, value, options) {
      if (options == null) {
        options = {};
      }
  
      options.output = true;
      this.addParameter(name, type, value, options);
    }
  
    makeParamsParameter(parameters) {
      let paramsParameter = '';
  
      for (let i = 0, len = parameters.length; i < len; i++) {
        const parameter = parameters[i];
  
        if (paramsParameter.length > 0) {
          paramsParameter += ', ';
        }
  
        paramsParameter += '@' + parameter.name + ' ';
        paramsParameter += parameter.type.declaration(parameter);
  
        if (parameter.output) {
          paramsParameter += ' OUTPUT';
        }
      }
  
      return paramsParameter;
    }
  
    transformIntoExecuteSqlRpc() {
      if (this.validateParameters()) {
        return;
      }
  
      this.originalParameters = this.parameters;
      this.parameters = [];
      this.addParameter('statement', TYPES.NVarChar, this.sqlTextOrProcedure);
  
      if (this.originalParameters.length) {
        this.addParameter('params', TYPES.NVarChar, this.makeParamsParameter(this.originalParameters));
      }
  
      for (let i = 0, len = this.originalParameters.length; i < len; i++) {
        const parameter = this.originalParameters[i];
        this.parameters.push(parameter);
      }
  
      this.sqlTextOrProcedure = 'sp_executesql';
    }
  
    transformIntoPrepareRpc() {
      this.originalParameters = this.parameters;
      this.parameters = [];
      this.addOutputParameter('handle', TYPES.Int);
      this.addParameter('params', TYPES.NVarChar, this.makeParamsParameter(this.originalParameters));
      this.addParameter('stmt', TYPES.NVarChar, this.sqlTextOrProcedure);
      this.sqlTextOrProcedure = 'sp_prepare';
      this.preparing = true;
      this.on('returnValue', (name, value) => {
        if (name === 'handle') {
          this.handle = value;
        } else {
          this.error = RequestError(`Tedious > Unexpected output parameter ${name} from sp_prepare`);
        }
      });
    }
  
    transformIntoUnprepareRpc() {
      this.parameters = [];
      this.addParameter('handle', TYPES.Int, this.handle);
      this.sqlTextOrProcedure = 'sp_unprepare';
    }
  
    transformIntoExecuteRpc(parameters) {
      this.parameters = [];
      this.addParameter('handle', TYPES.Int, this.handle);
  
      for (let i = 0, len = this.originalParameters.length; i < len; i++) {
        const parameter = this.originalParameters[i];
        parameter.value = parameters[parameter.name];
        this.parameters.push(parameter);
      }
  
      if (this.validateParameters()) {
        return;
      }
  
      this.sqlTextOrProcedure = 'sp_execute';
    }
  
    validateParameters() {
      for (let i = 0, len = this.parameters.length; i < len; i++) {
        const parameter = this.parameters[i];
        const value = parameter.type.validate(parameter.value);
  
        if (value instanceof TypeError) {
          return this.error = new RequestError('Validation failed for parameter \'' + parameter.name + '\'. ' + value.message, 'EPARAM');
        }
  
        parameter.value = value;
      }
  
      return null;
    } // Temporarily suspends the flow of data from the database.
    // No more 'row' events will be emitted until resume() is called.
  
  
    pause() {
      if (this.paused) {
        return;
      }
  
      this.paused = true;
  
      if (this.connection) {
        this.connection.pauseRequest(this);
      }
    } // Resumes the flow of data from the database.
  
  
    resume() {
      if (!this.paused) {
        return;
      }
  
      this.paused = false;
  
      if (this.connection) {
        this.connection.resumeRequest(this);
      }
    }
  
    cancel() {
      if (this.canceled) {
        return;
      }
  
      this.canceled = true;
      this.emit('cancel');
    }
  
    setTimeout(timeout) {
      this.timeout = timeout;
    }
  
  }
  
  module.exports = Request;