
var ArrayBuffer = require('./typedarrays').ArrayBuffer;
var DataView = require('./typedarrays').DataView;
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var Thrift = {};
var Promise = require('bluebird');

Thrift.AsyncMemBuffer = function (buffer) {
    EventEmitter.call(this);

    this.writes = [];
    this.reads = [];
    this.buffer = buffer;
    this.offset = 0;
    this.length = buffer ? buffer.length : 0;
};

util.inherits(Thrift.AsyncMemBuffer, EventEmitter);

Thrift.AsyncMemBuffer.prototype.read = function (len) {
    var me = this;
    var promise = new Promise(function (resolve, reject) {
        me.reads.push({len: len, resolve: resolve, reject: reject});
        me.attemptReads();
    });
    return promise;
};

Thrift.AsyncMemBuffer.prototype.attemptReads = function () {
    var request;
    var view;
    while (this.reads.length > 0 && this.reads[0].len + this.offset <= this.length) {
        request = this.reads.shift();
        view = new DataView(new ArrayBuffer(this.buffer), this.offset, request.len);
        this.offset += request.len;
        request.resolve(view);
    }
};

Thrift.AsyncMemBuffer.prototype.write = function (bytes) {
    var buf;

    if (bytes instanceof Buffer) {
        buf = bytes;
    } else if (bytes.buf) {
        buf = bytes.buf;
    } else if (bytes.buffer && bytes.buffer.buf) {
        if (bytes.buffer.buf.length != bytes.byteLength) {
            buf = bytes.buffer.buf.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
        } else {
            buf = bytes.buffer.buf;
        }
    } else {
        throw Error('Unsupported type sent to AsyncMemBuffer.write. Accepts Buffer, ArrayBuffer polyfill, and DataView polyfill');
    }
    this.writes.push(buf);
};

Thrift.AsyncMemBuffer.prototype.clear = function () {
    this.writes = [];
    this.reads = [];
    this.buffer = null;
    this.offset = 0;
    this.length = 0;
};

Thrift.AsyncMemBuffer.prototype.flush = function () {
    if (this.buffer) this.writes.unshift(this.buffer);
    this.buffer = Buffer.concat(this.writes);
    this.writes = [];
    this.length = this.buffer.length;
    this.attemptReads();
};

exports.Buffer = Thrift.AsyncMemBuffer;
