'use strict';
var common = require('../common');

var stream = require('../../');
var Readable = stream.Readable;
var Writable = stream.Writable;

module.exports = function (t){
  t.test('pause then read', function (t) {
    var totalChunks = 100;
    var chunkSize = 99;
    var expectTotalData = totalChunks * chunkSize;
    var expectEndingData = expectTotalData;

    var r = new Readable({ highWaterMark: 1000 });
    var chunks = totalChunks;
    r._read = function(n) {
      if (!(chunks % 2))
        setImmediate(push);
      else if (!(chunks % 3))
        process.nextTick(push);
      else
        push();
    };

    var totalPushed = 0;
    function push() {
      var chunk = chunks-- > 0 ? new Buffer(chunkSize) : null;
      if (chunk) {
        totalPushed += chunk.length;
        chunk.fill('x');
      }
      r.push(chunk);
    }

    read100();

    // first we read 100 bytes
    function read100() {
      readn(100, onData);
    }

    function readn(n, then) {
      //console.error('read %d', n);
      expectEndingData -= n;
      ;(function read() {
        var c = r.read(n);
        if (!c)
          r.once('readable', read);
        else {
          t.equal(c.length, n);
          t.notOk(r._readableState.flowing);
          then();
        }
      })();
    }

    // then we listen to some data events
    function onData() {
      expectEndingData -= 100;
      //console.error('onData');
      var seen = 0;
      r.on('data', function od(c) {
        seen += c.length;
        if (seen >= 100) {
          // seen enough
          r.removeListener('data', od);
          r.pause();
          if (seen > 100) {
            // oh no, seen too much!
            // put the extra back.
            var diff = seen - 100;
            r.unshift(c.slice(c.length - diff));
            console.error('seen too much', seen, diff);
          }

          // Nothing should be lost in between
          setImmediate(pipeLittle);
        }
      });
    }

    // Just pipe 200 bytes, then unshift the extra and unpipe
    function pipeLittle() {
      expectEndingData -= 200;
      //console.error('pipe a little');
      var w = new Writable();
      var written = 0;
      w.on('finish', function() {
        t.equal(written, 200);
        setImmediate(read1234);
      });
      w._write = function(chunk, encoding, cb) {
        written += chunk.length;
        if (written >= 200) {
          r.unpipe(w);
          w.end();
          cb();
          if (written > 200) {
            var diff = written - 200;
            written -= diff;
            r.unshift(chunk.slice(chunk.length - diff));
          }
        } else {
          setImmediate(cb);
        }
      };
      r.pipe(w);
    }

    // now read 1234 more bytes
    function read1234() {
      readn(1234, resumePause);
    }

    function resumePause() {
      //console.error('resumePause');
      // don't read anything, just resume and re-pause a whole bunch
      r.resume();
      r.pause();
      r.resume();
      r.pause();
      r.resume();
      r.pause();
      r.resume();
      r.pause();
      r.resume();
      r.pause();
      setImmediate(pipe);
    }


    function pipe() {
      //console.error('pipe the rest');
      var w = new Writable();
      var written = 0;
      w._write = function(chunk, encoding, cb) {
        written += chunk.length;
        cb();
      };
      w.on('finish', function() {
        //console.error('written', written, totalPushed);
        t.equal(written, expectEndingData);
        t.equal(totalPushed, expectTotalData);
        t.end();
      });
      r.pipe(w);
    }
  });
}
