'use strict';

var transport = require('../../../spdy-transport');

var util = require('util');
var utils = require('./').utils;
var OffsetBuffer = require('obuf');
var Transform = require('readable-stream').Transform;

function Parser(options) {
  Transform.call(this, {
    readableObjectMode: true
  });

  this.buffer = new OffsetBuffer();
  this.partial = false;
  this.waiting = 0;

  this.window = options.window;

  this.version = null;
  this.decompress = null;
  this.dead = false;
}
module.exports = Parser;
util.inherits(Parser, Transform);

Parser.prototype.error = utils.error;

Parser.prototype.kill = function kill() {
  this.dead = true;
};

Parser.prototype._transform = function transform(data, encoding, cb) {
  if (!this.dead)
    this.buffer.push(data);

  this._consume(cb);
};

Parser.prototype._consume = function _consume(cb) {
  var self = this;

  function next(err, frame) {
    if (err)
      return cb(err);

    if (Array.isArray(frame)) {
      for (var i = 0; i < frame.length; i++)
        self.push(frame[i]);
    } else if (frame) {
      self.push(frame);
    }

    // Consume more packets
    if (!sync)
      return self._consume(cb);

    process.nextTick(function() {
      self._consume(cb);
    });
  }

  if (this.dead)
    return cb();


  if (this.buffer.size < this.waiting) {
    // No data at all
    if (this.buffer.size === 0)
      return cb();

    // Partial DATA frame or something that we can process partially
    if (this.partial) {
      var partial = this.buffer.clone(this.buffer.size);
      this.buffer.skip(partial.size);
      this.waiting -= partial.size;

      this.executePartial(partial, next);
      return;
    }

    // We shall not do anything until we get all expected data
    return cb();
  }

  var sync = true;

  var content = this.buffer.clone(this.waiting);
  this.buffer.skip(this.waiting);

  this.execute(content, next);
  sync = false;
};

Parser.prototype.setVersion = function setVersion(version) {
  this.version = version;
  this.emit('version', version);
};

Parser.prototype.setCompression = function setCompresion(pair) {
  this.decompress = new transport.utils.LockStream(pair.decompress);
};
