var EventEmitter = require('events').EventEmitter;
var util = require('util');
var WebSocket = require('ws');
var _ = require('underscore');

/**
 * Create a Client that connects to a remote server over a
 * websocket.
 * @constructor
 * @augments EventEmitter
 * @property {Boolean} connected The WebSocket connection state.
 */
function Client() {
  this.connected = false;
  this.ws = null;

  this.nextId = 0;
  this.requests = {};
}

util.inherits(Client, EventEmitter);

/**
 * Open the connection on the given url and protocols, closing any previous
 * connection. Upon a successful connection, the 'open' event will fire.
 * @param {string} url The server URL.
 * @param {(string|Array.<string>)=} protocols The protocols to use.
 */
Client.prototype.open = function(url, protocols) {
  this.close();

  var ws = this.ws = new WebSocket(url, protocols);
  var self = this;

  ws.onopen = function() {
    self.emit('open');
  };

  ws.onclose = function(event) {
    self.ws = null;
    _.each(self.requests, function(callback) {
      callback(new Error('WebSocket closed'));
    });
    self.emit('close', event.code, event.reason);
  };

  ws.onmessage = function(event) {
    try {
      var message = JSON.parse(event.data);
      if (message.event != null) {
        self.emit('event', message.event, message);
      } else {
        var callback = self.requests[message.id];
        var error = message.error && new Error(message.error.message);
        if (callback) callback(error, message.result);
      }
    } catch (err) {
      self.emit('error', err);
    }
  };

  ws.onerror = function(event) {
    self.emit('error', event.errno);
    if (self.ws) self.ws.close();
  };
};

/**
 * Close the connection, if it exists. This will cancel any pending requests
 * and fire the 'close' event. The connection will not try to reconnect.
 */
Client.prototype.close = function() {
  if (this.ws) this.ws.close();
};

/**
 * Asynchronosly send a JSON-RPC request.
 * @param {string} method The method name.
 * @param {(Array|Object)} args The method arguments.
 * @param {function(Error, *)} callback The asynchronous callback.
 */
Client.prototype.request = function(method, args, callback) {
  try {
    if (!this.ws) throw new Error('WebSocket not connected');
    var requestId = this.nextId++;
    this.requests[requestId] = callback;
    this.ws.send(JSON.stringify({
      jsonrpc: '2.0',
      id: requestId,
      method: method,
      params: args
    }));
  } catch (err) {
    delete this.requests[requestId];
    callback(err, null);
  }
};

/** @module client */
module.exports = Client;

@@ -2201,26 +2201,16 @@
 back) %7B%0A
-  try %7B%0A  
   if (!t
@@ -2261,18 +2261,16 @@
 cted');%0A
-  
   var re
@@ -2294,18 +2294,16 @@
 xtId++;%0A
-  
   this.r
@@ -2333,18 +2333,16 @@
 llback;%0A
-  
   this.w
@@ -2365,18 +2365,16 @@
 ngify(%7B%0A
-  
     json
@@ -2389,18 +2389,16 @@
 0',%0A    
-  
 id: requ
@@ -2408,18 +2408,16 @@
 Id,%0A    
- 
- 
 method: 
@@ -2428,18 +2428,16 @@
 od,%0A    
-  
 params: 
@@ -2447,99 +2447,13 @@
 s%0A  
- 
- 
 %7D));%0A
-  %7D catch (err) %7B%0A    delete this.requests%5BrequestId%5D;%0A    callback(err, null);%0A  %7D%0A
 %7D;%0A%0A

Throw error if websocket not connected