text: /**
   * Dependencies
   */
  var Param = require(__dirname + '/Param'); 
  var sax = require('sax');
  var stream = require('stream');
  var util = require('util');
  
  /**
   * Module exports
   */
  module.exports = XmlStreamParser;
  
  var config = {
  
    /**
     * Default tags we need to handle. They are used instead of camelCased parameters 
     * to optimize additional processing during xmlStream parsing.
     * @var {object}
     */
    tags : {
  
      /**
       * Tags to collect about server
       */
      server: [
        'admin',
        'banned_IPs',
        'build',
        'client_connections',
        'clients',
        'connections',
        'file_connections',
        'host',
        'listener_connections',
        'listeners',
        'location',
        'outgoing_kbitrate',
        'server_id',
        'server_start',
        'source_client_connections',
        'source_relay_connections',
        'sources',
        'source_total_connections',
        'stats',
        'stats_connections',
        'stream_kbytes_read',
        'stream_kbytes_sent'
      ],
  
      /**
       * Tags to collect about every source
       */
      source: [
        'audio_codecid',
        'audio_info',
        'authenticator',
        'bitrate',
        'connected',
        'genre',
        'incoming_bitrate',
        'listener_connections',
        'listener_peak',
        'listeners',
        'listenurl',
        'max_listeners',
        'metadata_updated',
        'mpeg_channels',
        'mpeg_samplerate',
        'outgoing_kbitrate',
        'public',
        'queue_size',
        'server_description',
        'server_name',
        'server_type',
        'server_url',
        'slow_listeners',
        'source_ip',
        'stream_start',
        'title',
        'total_bytes_read',
        'total_bytes_sent',
        'total_mbytes_sent',
        'yp_currently_playing'
      ],
  
      /**
       * Tags to collect about every listener
       */
      listener: [
        'ID',
        'IP',
        'UserAgent',
        'Referer',
        'lag',
        'Connected'
      ]
    },
  
    /**
     * Depth level for different data types
     * @var {object}
     */
    dataDepthLevels: {
      2: 'server',
      3: 'source',
      4: 'listener'
    }
  };
  
  /**
   * Creates data item with normalized tag names as keys and null values.
   *
   * @param {Array} tags
   */
  function initDataItem(tags) {
    var item = {};
  
    tags.forEach(function(tag) {
      var param = Param.normalizeName(tag);
      item[param] = null;
    });
  
    return item;
  }
  
  /**
   * Constructor
   */
  function XmlStreamParser () {
  
    /**
     * Instance link
     * @var {XmlStreamParser}
     */
    var parser = this;
  
    /**
     * Represents currently processing data item (server, source, listener). 
     * After processing is finished, event is emitted, data[object] is flushed.
     * @var {object}
     */
    this.data = {};
  
    /**
     * Sax writeable stream
     * @var {stream.Writeable}
     */
    this.saxStream = sax.createStream(true);
  
    /**
     * Current depth level
     * @var {integer}
     */
    this.currentDepth;
    
    /**
     * Current tag
     * @var {string}
     */
    this.currentTag;
  
    /**
     * Current source mountpoint
     * @var {string}
     */
    this.currentMount;
  
    // Inherit from writeable stream
    stream.Writable.call(this);  
  
    // Handle errors
    parser.saxStream.on('error', function(error) {
      parser.emit('error', error);
    });
  
    // Handle data
    parser.saxStream.on('opentag', function(node) {
      parser.handleOpenTag(node);
    });
  
    parser.saxStream.on('text', function(text) {
      parser.handleText(text);
    });
  
    parser.saxStream.on('closetag', function(tagName) {
      parser.handleCloseTag(tagName);
    });
  }
  util.inherits(XmlStreamParser, stream.Writable);
  
  /**
   * Handle tag opening.
   *
   * @param {object} node
   */
  XmlStreamParser.prototype.handleOpenTag = function(node) {
    
    // Re-init data containers
    if (node.name === 'icestats') {
      this.currentDepth = 0;
  
      this.data = {
        server: null,
        source: null,
        listener: null
      };
    }
  
    // Calculate depth level
    this.currentDepth++;
  
    if (node.name === 'source') {
      this.currentMount = node.attributes.mount;
    }
  
    // Check current depth level should be handled
    if ( ! config.dataDepthLevels[this.currentDepth]) {
      return;
    }
  
    // Get current data type: server, source or listener
    var dataType = config.dataDepthLevels[this.currentDepth];
  
    // Check that we need to handle this tag for current data type
    if (config.tags[dataType].indexOf(node.name) !== -1) {
      if ( ! this.data[dataType]) {
  
        // Create data item and fill it with nullable values
        this.data[dataType] = initDataItem(config.tags[dataType]);
  
        // Set mount name
        if (dataType === 'source' || dataType === 'listener') {
          this.data[dataType].mount = this.currentMount;
        }
      }
      this.currentTag = node.name;
    }
  };
  
  /**
   * Handles text.
   *
   * @param {string} text
   */
  XmlStreamParser.prototype.handleText = function(text) {
  
    // Check current depth level should be handled
    if ( ! config.dataDepthLevels[this.currentDepth]) {
      return;
    }
  
    // Check, if we need to handle current tag
    if ( ! this.currentTag) {
      return;
    }
  
    // Get current data type: server, source or listener
    var dataType = config.dataDepthLevels[this.currentDepth];
  
    // Fill with normalized parameter & data
    var param = Param.normalizeName(this.currentTag);
  
    this.data[dataType][param] = Param.normalizeData(param, [text]);
  };
  
  /**
   * Handles tag closing.
   *
   * @param {string} tagName
   */
  XmlStreamParser.prototype.handleCloseTag = function(tagName) {
  
    this.currentDepth--;
    this.currentTag = null;
  
    switch(tagName) {
  
      // Source is finished
      case 'source':
        this.emit('source', this.data.source);
        this.data.source = null;
        this.currentMount = null;
        break;
  
      // Listener is finished
      case 'listener':
        this.emit('listener', this.data.listener);
        this.data.listener = null;
        break;
  
      // Server info is ready (xml file is finished)
      case 'icestats':
        this.emit('server', this.data.server);
        this.data.server = null;
        break;
    }
  };
  
  /**
   * Handle input data.
   *
   * @param {Buffer|string} chunk
   * @param {string} encoding
   * @param {function} done
   */
  XmlStreamParser.prototype._write = function (chunk, encoding, done) {
    this.saxStream.write(chunk);
    done();
  };
  