text: var httpsys = require('./httpsys_native')
      , events = require('events')
      , util = require('util')
      , ServerRequest = require('./ServerRequest')
      , ServerResponse = require('./ServerResponse')
      , Socket = require('./Socket');
  
  function Server() {
      events.EventEmitter.call(this);
  }
  
  util.inherits(Server, events.EventEmitter);
  
  Server.prototype.listen = function (port, hostname, callback) {
      if (this._server) 
          throw new Error('The server is already listening. Call close before calling listen again.');
  
      if (!port || isNaN(+port) && typeof port !== 'string')
          throw new Error('Port must be specified as a positive integer or a full URL specification string.');
  
      if (typeof port === 'string' && typeof hostname === 'string')
          throw new Error('If port specifies a full URL, hostname cannot be specified.');
  
      if (typeof hostname === 'function') {
          callback = hostname;
          hostname = '*';
      }
      else if (typeof hostname === 'undefined') {
          hostname = '*';
      }
  
      if (typeof callback === 'function') {
          this.on('listening', callback);
      }
  
      var options = {
          url: typeof port === 'string' ? port : (this._scheme + hostname + ':' + port + '/')
      };
  
      try {
          this._nativeServer = httpsys.httpsys_listen(options);
          this._nativeServer.serverId = httpsys.serverId++;
          httpsys.servers[this._nativeServer.serverId] = this;
      }
      catch (e) {
          throw new Error('Error initializing the HTTP.SYS server. System error ' + e + '.');
      }
  
      var self = this;
      process.nextTick(function () {
          self.emit('listening');
      });
  
      return this;
  };
  
  Server.prototype.close = function (callback) {
      if (!this._closed) {
          try {
              httpsys.httpsys_stop_listen(this._nativeServer);
          }
          catch (e) {
              throw new Error('Error closing the HTTP.SYS listener. System error ' + e + '.');
          }
  
          if (typeof callback === 'function') {
              this.on('close', function () { 
                  callback(); 
              });
          }
  
          this._closed = true;
      }
  };
  
  Server.prototype._on_server_closed = function () {
      delete httpsys.servers[this._nativeServer.serverId];
      delete this._nativeServer;            
      this.emit('close');
  };
  
  Server._dispatch = function (args) {
      if (!args.eventType || !httpsys.nativeEvents[args.eventType])
          throw new Error('Unrecognized eventType: ' + args.eventType);
  
      var server = args.server || httpsys.servers[args.uv_httpsys_server.serverId];
      if (server) {                        
          return server[httpsys.nativeEvents[args.eventType]](args);
      }
      else if (args.eventType === 2) {
          // Error initiating a new request for a server that was closed by the application. 
          // This is expected since the HTTP.SYS request queue was closed, and so attempts
          // to initiate new requests are going to fail. Ignore the error. 
  
          // Ignore.
      }
      else {
          // We should never get here.
  
          throw new Error('Server associated with HTTP.SYS request queue ' 
              + args.uv_httpsys_server.serverId + ' does not exist. Unable to dispatch event ' 
              + util.format(args));
      }
  };
  
  Server.prototype._on_error_initializing_request = function(args) {
      // This is a non-recoverable exception. Ignoring this exception would lead to 
      // the server becoming unresponsive due to lack of pending reads. 
  
      throw new Error('Unable to initiate a new asynchronous receive of an HTTP request against HTTP.SYS. '
          + 'System error ' + args.code + '.');
  };
  
  Server.prototype._on_error_new_request = function(args) {
      // The HTTP.SYS operation that was to receive a new HTTP request had failed. This
      // condition is safe to ignore - no JavaScript representation of the request exists yet, 
      // and the failed pending read had already been replaced with a new pending read. 
  
      this.emit('clientError', new Error('HTTP.SYS receive of a new HTTP request has failed. '
          + 'System errror ' + args.code + '.'));
  };
  
  Server.prototype._on_new_request = function(requestContext) {    
      requestContext._reqAsyncPending = false;
      requestContext._resAsyncPending = false;
      requestContext.responseStarted = false;
      requestContext.server = this;
      requestContext.headers = {};
      requestContext.statusCode = 200;
      requestContext.reason = 'OK';
      requestContext.noDelay = true;
      requestContext.socket = new Socket(requestContext);
      requestContext.req = new ServerRequest(requestContext.socket);
      requestContext.asyncPending = function (target, value) {
  
          // For regular HTTP reuests, only one async operation outstanding against HTTP.SYS 
          // per request is allowed. For upgraded HTTP requests, one async operation per each target
          // (req/res) is allowed. 
  
          if (value === undefined) {
              // For regular HTTP requests, _reqAsyncPending === _resAsyncPending at all times.
              // For upgraded HTTP requests they may differ.
              return requestContext['_' + target + 'AsyncPending'];
          }
          else {
              if (requestContext.upgrade) {
                  requestContext['_' + target + 'AsyncPending'] = value;
              }
              else {
                  requestContext._reqAsyncPending = requestContext._resAsyncPending = value;
              }
          }
      };
  
      if (requestContext.req.headers['upgrade']) {
          // This is an upgrade request.
  
          requestContext.upgrade = true;
  
          if (this.listeners('upgrade').length > 0) {
              // The 'upgrade' event has a listener. Emit the event. At this point the request 
              // object is not subscribed to socket's data events: application can only read request 
              // data by subscribing to socket events directly.
  
              this.emit('upgrade', requestContext.req, requestContext.req.socket, new Buffer(0));
              requestContext.asyncPending('req', !requestContext.socket._paused);
          }
          else {
              // The 'upgrade' event is not listened for. Reject the upgrade request. 
  
              // Prevent the native module from reading request entity body after this function returns.
              requestContext.asyncPending('req', false); 
  
              // Send a 400 response and drop the TCP connection
              requestContext.statusCode = 400;
              requestContext.disconnect = true;
              httpsys.httpsys_write_headers(requestContext);
          }
      }
      else {
          // This is a non-upgrade request. Create a response object, and subscribe the request object
          // to the data events generated by the socket in order to re-expose them. 
          
          requestContext.res = new ServerResponse(requestContext.socket);
          requestContext.req._subscribe();
  
          // Generate new request event
  
          this.emit('request', requestContext.req, requestContext.res);
          requestContext.asyncPending('req', !requestContext.socket._paused);
      }
  
      // Reading of the next request chunk should be started if the request is not
      // paused, if the socket was not destroyed, and if the response had not been started yet.
  
      return requestContext.asyncPending('req') 
          && !requestContext.disconnect 
          && !requestContext.responseStarted;
  };
  
  Server.prototype._on_error_initializing_read_request_body = function(args) {
      // The headers of the HTTP request had already been read but initializing reading of the 
      // request body failed. Notify application code and clean up managed resources
      // representing the request. Native resources had already been released at this point.
  
      args.socket._error('req', new Error('Error initializing the reading of the request body. System error '
          + args.code + '.'));
  };
  
  Server.prototype._on_end_request = function(requestContext) {
      requestContext.asyncPending('req', false);
      requestContext.socket._on_end_request();
  };
  
  Server.prototype._on_error_read_request_body = function(args) {
      // Reading of the request body failed. Notify application code and clean up managed resources
      // representing the request. Native resources had already been released at this point.
  
      args.socket._error('req', new Error('Error reading the request body. System error ' + args.code + '.'));
  };
  
  Server.prototype._on_request_body = function(requestContext) {
      requestContext.asyncPending('req', false);
      requestContext.socket._on_request_body(requestContext);
      requestContext.asyncPending('req', !requestContext.socket._paused);
  
      // Reading of the next request chunk should be started if the request is not
      // paused, if the socket was not destroyed, and if the response had not been started yet.
  
      return requestContext.asyncPending('req') 
          && !requestContext.disconnect 
          && !requestContext.responseStarted;
  };
  
  Server.prototype._on_error_writing = function(args) {
      // Sending of the response headers and/or body failed. Notify application code and clean up managed resources
      // representing the request. Native resources had already been released at this point.
  
      args.socket._error('res', new Error('Error sending response data. System error ' + args.code + '.'));
  };
  
  Server.prototype._on_written = function(requestContext) {
      requestContext.asyncPending('res', false);
      requestContext.socket._on_written();
  };
  
  module.exports = Server;
  