/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const Stream = require("./../../../unisharp/Stream");

// This class allows us to retrieve a specified number of bits from
// the input buffer, as well as copy big byte blocks.
// It uses an int buffer to store up to 31 bits for direct
// manipulation.  This guarantees that we can get at least 16 bits,
// but we only need at most 15, so this is all safe.
// There are some optimizations in this class, for example, you must
// never peek more than 8 bits more than needed, and you must first
// peek bits before you may drop them.  This is not a general purpose
// class but optimized for the behaviour of the Inflater.
class StreamManipulator {
    
    constructor() {
        this.window_ = null;
        this.windowStart_ = 0;
        this.windowEnd_ = 0;
        this.buffer_ = 0;
        this.bitsInBuffer_ = 0;
    }
    
    peekBits(bitCount) {
        if (this.bitsInBuffer_ < bitCount) {
            if (this.windowStart_ === this.windowEnd_) 
                return -1;
            this.buffer_ |= (((((this.window_[this.windowStart_]) & 0xff) | (((this.window_[this.windowStart_ + 1]) & 0xff)) << 8)) << this.bitsInBuffer_);
            this.windowStart_ += 2;
            this.bitsInBuffer_ += 16;
        }
        return ((this.buffer_) & (((1 << bitCount) - 1)));
    }
    
    dropBits(bitCount) {
        this.buffer_ >>= (bitCount);
        this.bitsInBuffer_ -= bitCount;
    }
    
    getBits(bitCount) {
        let bits = this.peekBits(bitCount);
        if (bits >= 0) 
            this.dropBits(bitCount);
        return bits;
    }
    
    get availableBits() {
        return this.bitsInBuffer_;
    }
    
    get availableBytes() {
        return (this.windowEnd_ - this.windowStart_) + ((this.bitsInBuffer_ >> 3));
    }
    
    skipToByteBoundary() {
        this.buffer_ >>= ((this.bitsInBuffer_ & 7));
        this.bitsInBuffer_ &= (~7);
    }
    
    get isNeedingInput() {
        return this.windowStart_ === this.windowEnd_;
    }
    
    copyBytes(output, offset, length) {
        if (length < 0) 
            throw new Error("length");
        if (((this.bitsInBuffer_ & 7)) !== 0) 
            throw new Error("Bit buffer is not byte aligned!");
        let count = 0;
        while ((this.bitsInBuffer_ > 0) && (length > 0)) {
            output[offset++] = this.buffer_;
            this.buffer_ >>= (8);
            this.bitsInBuffer_ -= 8;
            length--;
            count++;
        }
        if (length === 0) 
            return count;
        let avail = this.windowEnd_ - this.windowStart_;
        if (length > avail) 
            length = avail;
        for(let iii = 0; iii < (length); iii++) output[(offset) + iii] = this.window_[(this.windowStart_) + iii];
        this.windowStart_ += length;
        if (((((this.windowStart_ - this.windowEnd_)) & 1)) !== 0) {
            this.buffer_ = ((this.window_[this.windowStart_++]) & 0xff);
            this.bitsInBuffer_ = 8;
        }
        return count + length;
    }
    
    reset() {
        this.buffer_ = 0;
        this.windowStart_ = (this.windowEnd_ = (this.bitsInBuffer_ = 0));
    }
    
    setInput(buffer, offset, count) {
        if (buffer === null) 
            throw new Error("buffer");
        if (offset < 0) 
            throw new Error("offset", "Cannot be negative");
        if (count < 0) 
            throw new Error("count", "Cannot be negative");
        if (this.windowStart_ < this.windowEnd_) 
            throw new Error("Old input was not completely processed");
        let end = offset + count;
        if ((offset > end) || (end > buffer.length)) 
            throw new Error("count");
        if (((count & 1)) !== 0) {
            this.buffer_ |= ((((buffer[offset++]) & 0xff)) << this.bitsInBuffer_);
            this.bitsInBuffer_ += 8;
        }
        this.window_ = buffer;
        this.windowStart_ = offset;
        this.windowEnd_ = end;
    }
}


module.exports = StreamManipulator