'use strict';

// glorious streaming json parser, built specifically for the twitter streaming api
// assumptions:
//   1) ninjas are mammals
//   2) tweets come in chunks of text, surrounded by {}'s, separated by line breaks
//   3) only one tweet per chunk
//
//   p = new parser.instance()
//   p.addListener('object', function...)
//   p.receive(data)
//   p.receive(data)
//   ...

var EventEmitter = require('events').EventEmitter;

var Parser = module.exports = function Parser() {
  // Make sure we call our parents constructor
  EventEmitter.call(this);
  this.buffer = '';
  return this;
};

// The parser emits events!
Parser.prototype = Object.create(EventEmitter.prototype);

Parser.END        = '\r\n';
Parser.END_LENGTH = 2;

Parser.prototype.receive = function receive(buffer) {
  this.buffer += buffer.toString('utf8');
  var index, json;

  // We have END?
  while ((index = this.buffer.indexOf(Parser.END)) > -1) {
    json = this.buffer.slice(0, index);
    this.buffer = this.buffer.slice(index + Parser.END_LENGTH);
    if (json.length > 0) {
      try {
        json = JSON.parse(json);
        switch(json.event){
          case 'follow':
            this.emit('follow', json);
            break;
          case 'unfollow':
            this.emit('unfollow', json);
            break;
          case 'favorite':
            this.emit('favorite', json);
            break;
          case 'unfavorite':
            this.emit('unfavorite', json);
            break;
          case 'block':
            this.emit('block', json);
            break;
          case 'unblock':
            this.emit('unblock', json);
            break;
          case 'list_created':
            this.emit('list_created', json);
            break;
          case 'list_destroyed':
            this.emit('list_destroyed', json);
            break;
          case 'list_updated':
            this.emit('list_updated', json);
            break;
          case 'list_member_added':
            this.emit('list_member_added', json);
            break;
          case 'list_member_removed':
            this.emit('list_member_removed', json);
            break;
          case 'list_user_subscribed':
            this.emit('list_user_subscribed', json);
            break;
          case 'list_user_unsubscribed':
            this.emit('list_user_unsubscribed', json);
            break;
          case 'quoted_tweet':
            this.emit('quoted_tweet', json);
            break;
          case 'user_update':
            this.emit('user_update', json);
            break;
          default:
            this.emit('data', json);
            break;
        }
      }
      catch (error) {
        error.source = json;
        this.emit('error', error);
      }
    }
  }
};

@@ -1115,316 +1115,104 @@
     
-switch(json.event)%7B%0A          case 'follow':%0A            this.emit('follow', json);%0A            break;%0A          case 'unfollow':%0A            this.emit('unfollow', json);%0A            break;%0A          case 'favorite':%0A            this.emit('favorite', json);%0A            break;%0A          case 'unfavorite':%0A  
+// Event message%0A        if (json.event !== undefined) %7B%0A          // First emit specific event%0A
     
@@ -1231,20 +1231,18 @@
 mit(
-'unfavorite'
+json.event
 , js
@@ -1260,126 +1260,36 @@
     
-  break;%0A          case 'block':%0A            this.emit('block', json);%0A            break;%0A          case 'unblock':%0A  
+// Now emit catch-all event%0A
     
@@ -1309,15 +1309,13 @@
 it('
-unblock
+event
 ', j
@@ -1332,246 +1332,82 @@
     
-    break;%0A          case 'list_created':%0A            this.emit('list_created', json);%0A            break;%0A          case 'list_destroyed':%0A            this.emit('list_destroyed', json);%0A            break;%0A          case 'list_updated':%0A  
+%7D%0A        // Delete message%0A        else if (json.delete !== undefined) %7B%0A
     
@@ -1427,20 +1427,14 @@
 it('
-list_upda
+dele
 te
-d
 ', j
@@ -1451,668 +1451,232 @@
     
-    break;%0A          case 'list_member_added':%0A            this.emit('list_member_added', json);%0A            break;%0A          case 'list_member_removed':%0A            this.emit('list_member_removed', json);%0A            break;%0A          case 'list_user_subscribed':%0A            this.emit('list_user_subscribed', json);%0A            break;%0A          case 'list_user_unsubscribed':%0A            this.emit('list_user_unsubscribed', json);%0A            break;%0A          case 'quoted_tweet':%0A            this.emit('quoted_tweet', json);%0A            break;%0A          case 'user_update':%0A            this.emit('user_update', json);%0A            break;%0A          default:%0A  
+%7D%0A        // Friends message (beginning of stream)%0A        else if (json.friends !== undefined %7C%7C json.friends_str !== undefined) %7B%0A          this.emit('friends', json);%0A        %7D%0A        // Any other message%0A        else %7B%0A
     
@@ -1710,27 +1710,8 @@
 n);%0A
-            break;%0A
     

Change stream events to emit specific and general versions of event messages, as well as delete and friends messages