//
// ArrayBuffer
//
function ArrayBuffer(lenOrBuf) {
    this.buf = (lenOrBuf instanceof Buffer) ? lenOrBuf : new Buffer(lenOrBuf);
    this.byteLength = this.buf.length;
}

ArrayBuffer.prototype.slice = function (begin, end) {
    return new ArrayBuffer(this.buf.slice(begin, end));
};

//
// DataView
//
function DataView(arrayBuffer, byteOffset, byteLength) {
    byteOffset = byteOffset || 0;
    byteLength = (byteLength == null) ? arrayBuffer.byteLength - byteOffset : byteLength;
    if (arrayBuffer.byteLength < byteOffset + byteLength) throw Error('DataView outside of ArrayBuffer bounds');

    this.buffer = arrayBuffer;
    this.byteOffset = byteOffset;
    this.byteLength = byteLength;
}

DataView.prototype.getInt8 = function (offset) {
    if (this.byteLength < offset + 1) throw Error('DataView.getInt8 offset out of bounds');
    return this.buffer.buf.readInt8(this.byteOffset + offset)
};

DataView.prototype.getUint8 = function (offset) {
    if (this.byteLength < offset + 1) throw Error('DataView.getUInt8 offset out of bounds');
    return this.buffer.buf.readUInt8(this.byteOffset + offset)
};

DataView.prototype.getInt16 = function (offset, littleEndian) {
    if (this.byteLength < offset + 2) throw Error('DataView.getInt16 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readInt16LE(this.byteOffset + offset) : 
        this.buffer.buf.readInt16BE(this.byteOffset + offset);
};

DataView.prototype.getUint16 = function (offset, littleEndian) {
    if (this.byteLength < offset + 2) throw Error('DataView.getUInt16 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readUInt16LE(this.byteOffset + offset) : 
        this.buffer.buf.readUInt16BE(this.byteOffset + offset);
};

DataView.prototype.getInt32 = function (offset, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.getInt32 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readInt32LE(this.byteOffset + offset) : 
        this.buffer.buf.readInt32BE(this.byteOffset + offset);
};

DataView.prototype.getUint32 = function (offset, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.getUInt32 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readUInt32LE(this.byteOffset + offset) : 
        this.buffer.buf.readUInt32BE(this.byteOffset + offset);
};

DataView.prototype.getFloat32 = function (offset, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.getFloat32 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readFloatLE(this.byteOffset + offset) : 
        this.buffer.buf.readFloatBE(this.byteOffset + offset);
};

DataView.prototype.getFloat64 = function (offset, littleEndian) {
    if (this.byteLength < offset + 8) throw Error('DataView.getFloat64 offset out of bounds');
    return littleEndian ? 
        this.buffer.buf.readDoubleLE(this.byteOffset + offset) : 
        this.buffer.buf.readDoubleBE(this.byteOffset + offset);
};

DataView.prototype.setInt8 = function (offset, value) {
    if (this.byteLength < offset + 1) throw Error('DataView.setInt8 offset out of bounds');
    this.buffer.buf.writeInt8(value, this.byteOffset + offset)
};

DataView.prototype.setUint8 = function (offset, value) {
    if (this.byteLength < offset + 1) throw Error('DataView.setUInt8 offset out of bounds');
    this.buffer.buf.writeUInt8(value, this.byteOffset + offset)
};

DataView.prototype.setInt16 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 2) throw Error('DataView.setInt16 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeInt16LE(value, this.byteOffset + offset)
    else this.buffer.buf.writeInt16BE(value, this.byteOffset + offset);
};

DataView.prototype.setUint16 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 2) throw Error('DataView.setUInt16 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeUInt16LE(value, this.byteOffset + offset)
    else this.buffer.buf.writeUInt16BE(value, this.byteOffset + offset);
};

DataView.prototype.setInt32 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.setInt32 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeInt32LE(value, this.byteOffset + offset)
    else this.buffer.buf.writeInt32BE(value, this.byteOffset + offset);
};

DataView.prototype.setUint32 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.setUInt32 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeUInt32LE(value, this.byteOffset + offset)
    else this.buffer.buf.writeUInt32BE(value, this.byteOffset + offset);
};

DataView.prototype.setFloat32 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 4) throw Error('DataView.setFloat32 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeFloatLE(value, this.byteOffset + offset)
    else this.buffer.buf.writeFloatBE(value, this.byteOffset + offset);
};

DataView.prototype.setFloat64 = function (offset, value, littleEndian) {
    if (this.byteLength < offset + 1) throw Error('DataView.setFloat64 offset out of bounds');

    if (littleEndian) this.buffer.buf.writeDoubleLE(value, this.byteOffset + offset)
    else this.buffer.buf.writeDoubleBE(value, this.byteOffset + offset);
};

exports.ArrayBuffer = ArrayBuffer;
exports.ArrayBuffer = ArrayBuffer;
exports.DataView = DataView;

