/*
 * 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 is the Deflater class.  The deflater class compresses input
// with the deflate algorithm described in RFC 1951.  It has several
// compression levels and three different strategies described below.
// This class is <i>not</i> thread safe.  This is inherent in the API, due
// to the split of deflate and setInput.
public class Deflater {

    public static final int BEST_COMPRESSION = 9;

    public static final int BEST_SPEED = 1;

    public static final int DEFAULT_COMPRESSION = -1;

    public static final int NO_COMPRESSION = 0;

    public static final int DEFLATED = 8;

    private static final int iS_SETDICT = 0x01;

    private static final int iS_FLUSHING = 0x04;

    private static final int iS_FINISHING = 0x08;

    private static final int iNIT_STATE = 0x00;

    private static final int sETDICT_STATE = 0x01;

    private static final int bUSY_STATE = 0x10;

    private static final int fLUSHING_STATE = 0x14;

    private static final int fINISHING_STATE = 0x1c;

    private static final int fINISHED_STATE = 0x1e;

    private static final int cLOSED_STATE = 0x7f;

    public Deflater(int _level, boolean _noZlibHeaderOrFooter) {
        try {
            if (_level == DEFAULT_COMPRESSION) 
                _level = 6;
            else if ((_level < NO_COMPRESSION) || _level > BEST_COMPRESSION) 
                throw new IndexOutOfBoundsException("level");
            pending = new DeflaterPending();
            engine = new DeflaterEngine(pending);
            this.noZlibHeaderOrFooter = _noZlibHeaderOrFooter;
            this.setStrategy(DeflateStrategy.DEFAULT);
            this.setLevel(_level);
            this.reset();
        } catch (Exception ex) {
        }
    }

    public void reset() {
        state = (noZlibHeaderOrFooter ? bUSY_STATE : iNIT_STATE);
        m_TotalOut = 0;
        pending.reset();
        engine.reset();
    }

    public int getAdler() {
        return engine.getAdler();
    }


    public int getTotalIn() {
        return engine.getTotalIn();
    }


    public int getTotalOut() {
        return m_TotalOut;
    }


    public void flush() {
        state |= iS_FLUSHING;
    }

    public void finish() {
        state |= ((iS_FLUSHING | iS_FINISHING));
    }

    public boolean isFinished() {
        return (state == fINISHED_STATE) && pending.isFlushed();
    }


    public boolean isNeedingInput() {
        return engine.needsInput();
    }


    public void setInput(byte[] input) throws UnsupportedOperationException, IllegalArgumentException, IndexOutOfBoundsException {
        this.setInputEx(input, 0, input.length);
    }

    public void setInputEx(byte[] input, int offset, int count) throws UnsupportedOperationException, IllegalArgumentException, IndexOutOfBoundsException {
        if (((state & iS_FINISHING)) != 0) 
            throw new UnsupportedOperationException("Finish() already called");
        engine.setInput(input, offset, count);
    }

    public void setLevel(int _level) throws IndexOutOfBoundsException, Exception {
        if (_level == DEFAULT_COMPRESSION) 
            _level = 6;
        else if ((_level < NO_COMPRESSION) || _level > BEST_COMPRESSION) 
            throw new IndexOutOfBoundsException("level");
        if (this.level != _level) {
            this.level = _level;
            engine.setLevel(_level);
        }
    }

    public int getLevel() {
        return level;
    }

    public void setStrategy(DeflateStrategy strategy) {
        engine.setStrategy(strategy);
    }

    public int deflate(byte[] output) throws UnsupportedOperationException, Exception {
        return this.deflateEx(output, 0, output.length);
    }

    public int deflateEx(byte[] output, int offset, int length) throws UnsupportedOperationException, Exception {
        int origLength = length;
        if (state == cLOSED_STATE) 
            throw new UnsupportedOperationException("Deflater closed");
        if (state < bUSY_STATE) {
            int header = ((DEFLATED + (((DeflaterConstants.MAX_WBITS - 8)) << 4))) << 8;
            int level_flags = ((level - 1)) >> 1;
            if ((level_flags < 0) || level_flags > 3) 
                level_flags = 3;
            header |= level_flags << 6;
            if (((state & iS_SETDICT)) != 0) 
                header |= DeflaterConstants.PRESET_DICT;
            header += (31 - ((header % 31)));
            pending.writeShortMSB(header);
            if (((state & iS_SETDICT)) != 0) {
                int chksum = engine.getAdler();
                engine.resetAdler();
                pending.writeShortMSB(chksum >> 16);
                pending.writeShortMSB(chksum & 0xffff);
            }
            state = bUSY_STATE | ((state & ((iS_FLUSHING | iS_FINISHING))));
        }
        for (; ; ) {
            int count = pending.flush(output, offset, length);
            offset += count;
            m_TotalOut += count;
            length -= count;
            if (length == 0 || state == fINISHED_STATE) 
                break;
            if (!engine.deflate(((state & iS_FLUSHING)) != 0, ((state & iS_FINISHING)) != 0)) {
                if (state == bUSY_STATE) 
                    return origLength - length;
                else if (state == fLUSHING_STATE) {
                    if (level != NO_COMPRESSION) {
                        int neededbits = 8 + ((((-pending.getBitCount())) & 7));
                        while (neededbits > 0) {
                            pending.writeBits(2, 10);
                            neededbits -= 10;
                        }
                    }
                    state = bUSY_STATE;
                }
                else if (state == fINISHING_STATE) {
                    pending.alignToByte();
                    if (!noZlibHeaderOrFooter) {
                        int _adler = engine.getAdler();
                        pending.writeShortMSB(_adler >> 16);
                        pending.writeShortMSB(_adler & 0xffff);
                    }
                    state = fINISHED_STATE;
                }
            }
        }
        return origLength - length;
    }

    public void setDictionary(byte[] dictionary) throws UnsupportedOperationException {
        this.setDictionaryEx(dictionary, 0, dictionary.length);
    }

    public void setDictionaryEx(byte[] dictionary, int index, int count) throws UnsupportedOperationException {
        if (state != iNIT_STATE) 
            throw new UnsupportedOperationException();
        state = sETDICT_STATE;
        engine.setDictionary(dictionary, index, count);
    }

    private int level;

    private boolean noZlibHeaderOrFooter;

    private int state;

    private int m_TotalOut;

    private DeflaterPending pending;

    private DeflaterEngine engine;
    public Deflater() {
        this(DEFAULT_COMPRESSION, false);
    }
}
