/*
 * 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
 */

package com.pullenti.unitext.internal.zip;

// 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.
public class StreamManipulator {

    public StreamManipulator() {
    }

    public int peekBits(int bitCount) {
        if (bitsInBuffer_ < bitCount) {
            if (windowStart_ == windowEnd_) 
                return -1;
            buffer_ |= ((int)(((((Byte.toUnsignedInt(window_[windowStart_])) & 0xff) | (((Byte.toUnsignedInt(window_[windowStart_ + 1])) & 0xff)) << 8)) << bitsInBuffer_));
            windowStart_ += 2;
            bitsInBuffer_ += 16;
        }
        return (((int)buffer_) & (((1 << bitCount) - 1)));
    }

    public void dropBits(int bitCount) {
        buffer_ >>= ((int)bitCount);
        bitsInBuffer_ -= bitCount;
    }

    public int getBits(int bitCount) {
        int bits = this.peekBits(bitCount);
        if (bits >= 0) 
            this.dropBits(bitCount);
        return bits;
    }

    public int getAvailableBits() {
        return bitsInBuffer_;
    }


    public int getAvailableBytes() {
        return (windowEnd_ - windowStart_) + ((bitsInBuffer_ >> 3));
    }


    public void skipToByteBoundary() {
        buffer_ >>= ((int)((bitsInBuffer_ & 7)));
        bitsInBuffer_ &= (~7);
    }

    public boolean isNeedingInput() {
        return windowStart_ == windowEnd_;
    }


    public int copyBytes(byte[] output, int offset, int length) throws IndexOutOfBoundsException, UnsupportedOperationException {
        if (length < 0) 
            throw new IndexOutOfBoundsException("length");
        if (((bitsInBuffer_ & 7)) != 0) 
            throw new UnsupportedOperationException("Bit buffer is not byte aligned!");
        int count = 0;
        while ((bitsInBuffer_ > 0) && (length > 0)) {
            output[offset++] = (byte)buffer_;
            buffer_ >>= ((int)8);
            bitsInBuffer_ -= 8;
            length--;
            count++;
        }
        if (length == 0) 
            return count;
        int avail = windowEnd_ - windowStart_;
        if (length > avail) 
            length = avail;
        for(int iii = 0; iii < (length); iii++) output[(offset) + iii] = window_[(windowStart_) + iii];
        windowStart_ += length;
        if (((((windowStart_ - windowEnd_)) & 1)) != 0) {
            buffer_ = (int)(((Byte.toUnsignedInt(window_[windowStart_++])) & 0xff));
            bitsInBuffer_ = 8;
        }
        return count + length;
    }

    public void reset() {
        buffer_ = (int)0;
        windowStart_ = (windowEnd_ = (bitsInBuffer_ = 0));
    }

    public void setInput(byte[] buffer, int offset, int count) throws IllegalArgumentException, IndexOutOfBoundsException, UnsupportedOperationException {
        if (buffer == null) 
            throw new IllegalArgumentException("buffer");
        if (offset < 0) 
            throw new IndexOutOfBoundsException("offset" + " " + "Cannot be negative");
        if (count < 0) 
            throw new IndexOutOfBoundsException("count" + " " + "Cannot be negative");
        if (windowStart_ < windowEnd_) 
            throw new UnsupportedOperationException("Old input was not completely processed");
        int end = offset + count;
        if ((offset > end) || (end > buffer.length)) 
            throw new IndexOutOfBoundsException("count");
        if (((count & 1)) != 0) {
            buffer_ |= ((int)((((Byte.toUnsignedInt(buffer[offset++])) & 0xff)) << bitsInBuffer_));
            bitsInBuffer_ += 8;
        }
        window_ = buffer;
        windowStart_ = offset;
        windowEnd_ = end;
    }

    private byte[] window_;

    private int windowStart_;

    private int windowEnd_;

    private int buffer_;

    private int bitsInBuffer_;
}
