Server = function(argv) {
  this.argv_ = argv;

  this.nextId_ = 1;
  this.clientMap_ = {};

  var uri = 'ws://' + os.hostname() + ':' + argv['ws-port'];

  this.httpServer_ = http.createServer();
  this.httpServer_.listen(Number(argv['http-port']));
  this.httpServer_.on('request', function(request, response) {
    // We only have the one HTTP file right now.
    var filePath = path.join(__dirname, 'controller.html');
    var content = fs.readFileSync(filePath, 'utf8');
    content = content.replace('%%CONTROL_URI%%', uri);
    contentBuffer = new Buffer(content);
    response.writeHead(200, {
      'Content-Type': 'text/html',
      'Content-Length': contentBuffer.length
    });
    response.end(contentBuffer);
  });

  this.wsServer_ = new ws.Server({
    port: Number(argv['ws-port'])
  });
  this.wsServer_.on('connection', (function(socket) {
    // Setup a periodic ping.
    var pingIntervalId = setInterval(function() {
      socket.ping(undefined, undefined, true);
    }, 1000);
    socket.on('close', function() {
      clearInterval(pingIntervalId);
    });

    // Wait until the hello packet to create the client object.
    socket.once('message', (function(message) {
      var packet = JSON.parse(message);
      if (!packet || packet['command'] != 'hello') {
        console.log('[WS] Bad hello packet:');
        console.log(packet);
        socket.terminate();
        return;
      }

      var client = null;
      switch (packet['client_type']) {
        case 'controller':
          client = new ControlClient(this.nextId_++, this, socket, packet);
          break;
        case 'tracer':
          client = new TraceClient(this.nextId_++, this, socket, packet);
          break;
        default:
          console.log('[WS] Bad client type: ' + packet['client_type']);
          socket.terminate();
          break;
      }
      this.clientMap_[client.id] = client;

      socket.on('error', (function(error) {
        console.log('[WS] Socket error: ' + error);
      }).bind(this));
      socket.on('close', (function(code) {
        console.log('[WS] Client disconnected: ' + code);
        delete this.clientMap_[client.id];
        client.dispose();
      }).bind(this));
    }).bind(this));
  }).bind(this));
  this.wsServer_.on('error', function(error) {
    console.log('[WS] Server Error: ' + error);
  });
}
@fileoverview Remote controller server.
Provides a server for remotely controlling WTF instrumented pages.

@author benvanik@google.com (Ben Vanik)