/*
 input bit stream reader
 (c) 2012/11/17- yoya@awm.jp
*/
goog.provide('FlappIBit');

goog.scope(function() {

/**
 * @constructor
 */
FlappIBit = function() {
    this.byteOffset = 0;
    this.bitOffset = 0;
    this.dataAllocLen = 0;
    this.data = null;
    this.dataLen = 0;
};

FlappIBit.prototype = {
    input: function(data) {
        if (typeof data === 'string') {
            this.inputStr(data);
        } else {
            this.inputArray(data);
        }
    },
    inputStr: function(data) {
        var dataLen = data.length;
        if (this.dataAllocLen < dataLen) {
            var oldData = this.data;
            this.data = new Uint8Array(dataLen);
            for (var i = 0 , l = this.dataLen; i < l ; i++) {
                this.data[i] = data.charCodeAt(i);
            }
            this.dataAllocLen = dataLen;
        }
        for (var i = this.dataLen ; i < dataLen ; i++) {
            this.data[i] = data.charCodeAt(i);
        }
        this.dataLen = dataLen;
    },
    inputArray: function(data) {
        this.data = data;
        this.dataAllocLen = this.dataLen = data.length;
    },
    len: function() { // data lengh
        return this.dataLen;
    },
    hasNext: function(len) {
        var nextOffset = this.byteOffset + (this.bitOffset?1:0) + len;
        return (nextOffset < this.dataLen);
    },
    getBytePos: function() { // get byte position
        return this.byteOffset;
    },
    setPos: function(byteOffset, bitOffset) { // set byte position
        this.byteOffset = byteOffset;
        this.bitOffset = bitOffset;
    },
    seek: function(byteDelta, bitDelta) { // relative seek
        this.byteOffset += byteDelta;
        this.bitOffset += bitDelta;
        if (this.bitOffset < 0) {
            do {
                this.byteOffset --;
                this.bitOffset += 8;
            } while (this.bitOffset < 0);
        } else if (this.bitOffset > 7) {
            do {
                this.byteOffset ++;
                this.bitOffset -= 8;
            } while (this.bitOffset > 7);
        }
    },
    a: function() { // skip bit
        if (this.bitOffset) {
            this.byteOffset++;
            this.bitOffset = 0;
        }
    },
    b: function() { // bit
        var value = (this.data[this.byteOffset] >>> (7 - this.bitOffset++)) & 1;
        if (this.bitOffset === 8) {
            this.byteOffset++;
            this.bitOffset = 0;
        }
        return value;
    },
    ub: function(len) { // unsigned bits
        var value = 0;
        while (len--) {
            value = (value << 1) | this.b();
        }
        return value;
    },
    sb: function(len) { // signed bits
        var value = 0;
        if (len === 0) return 0; // XXX
        var msb = this.b();
        if (msb) {
            var orig_len = len;
        }
        while (--len) {
            value = (value << 1) | this.b();
        }
        if (msb) {
            return value - (1 << (orig_len - 1));
        }
        return value;
    },
    si8: function() { // snsigned 8-bit integer
        var value = this.ui8();
        return (value & 0x80)?(value - 0x100):value;
    },
    si16: function() { // snsigned 16-bit integer
        var value = this.ui16();
        return (value & 0x8000)?(value - 0x10000):value;

    },
    si32: function() { // snsigned 32-bit integer
        var value = this.ui32();
        return (value & 0x80000000)?(value - 0x100000000):value;
    },
    ui8: function() { // unsigned 8-bit integer
        return this.data[this.byteOffset++];
    },
    ui16: function() { // unsigned 16-bit integer
        return this.data[this.byteOffset++] |
            (this.data[this.byteOffset++] << 8);
    },
    ui32: function() { // unsigned 32-bit integer
        return this.data[this.byteOffset++] |
            (this.data[this.byteOffset++] << 8) |
            (this.data[this.byteOffset++] << 16) |
            (this.data[this.byteOffset++] << 24);
    },
    ui16be: function() { // unsigned 16-bit integer
        return (this.data[this.byteOffset++] << 8) |
            this.data[this.byteOffset++];
    },
    sub: function(len) {
        this.a();
        var data = this.data.subarray(this.byteOffset, this.byteOffset + len);
        this.byteOffset += len;
        return data;
    },
    strN: function(len) {
        this.a();
        var data = this.data;
        var str = [];
        for (var i = this.byteOffset, l = this.byteOffset + len ; i < len ; i++) {
            str.push(String.fromCharCode(data[i]))
        }
        this.byteOffset += len;
//            return new FlappUTF8().fromSJISArray(data);
        return str.join('');
    },
    str: function(len) {
        this.a();
        var byteOffset = this.byteOffset;
        var dataLen = this.dataLen;
        var data;
        for (var i = byteOffset ; i < dataLen ; i++) {
            if (this.data[i] === 0) {
                data = this.strN(i - byteOffset);
                this.byteOffset++; // skip "\0"
                return data;
            }
        }
        return this.strN(i - byteOffset);
    }
};

});
