/*
 * 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 DeflateStrategy = require("./DeflateStrategy");
const DeflaterConstants = require("./DeflaterConstants");
const DeflaterPending = require("./DeflaterPending");
const DeflaterEngine = require("./DeflaterEngine");

// 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.
class Deflater {
    
    constructor(_level = Deflater.DEFAULT_COMPRESSION, _noZlibHeaderOrFooter = false) {
        this.level = 0;
        this.noZlibHeaderOrFooter = false;
        this.state = 0;
        this.m_TotalOut = 0;
        this.pending = null;
        this.engine = null;
        try {
            if (_level === Deflater.DEFAULT_COMPRESSION) 
                _level = 6;
            else if ((_level < Deflater.NO_COMPRESSION) || _level > Deflater.BEST_COMPRESSION) 
                throw new Error("level");
            this.pending = new DeflaterPending();
            this.engine = new DeflaterEngine(this.pending);
            this.noZlibHeaderOrFooter = _noZlibHeaderOrFooter;
            this.setStrategy(DeflateStrategy.DEFAULT);
            this.setLevel(_level);
            this.reset();
        } catch (ex) {
        }
    }
    
    reset() {
        this.state = (this.noZlibHeaderOrFooter ? Deflater.bUSY_STATE : Deflater.iNIT_STATE);
        this.m_TotalOut = 0;
        this.pending.reset();
        this.engine.reset();
    }
    
    get adler() {
        return this.engine.adler;
    }
    
    get totalIn() {
        return this.engine.totalIn;
    }
    
    get totalOut() {
        return this.m_TotalOut;
    }
    
    flush() {
        this.state |= Deflater.iS_FLUSHING;
    }
    
    finish() {
        this.state |= ((Deflater.iS_FLUSHING | Deflater.iS_FINISHING));
    }
    
    get isFinished() {
        return (this.state === Deflater.fINISHED_STATE) && this.pending.isFlushed;
    }
    
    get isNeedingInput() {
        return this.engine.needsInput();
    }
    
    setInput(input) {
        this.setInputEx(input, 0, input.length);
    }
    
    setInputEx(input, offset, count) {
        if (((this.state & Deflater.iS_FINISHING)) !== 0) 
            throw new Error("Finish() already called");
        this.engine.setInput(input, offset, count);
    }
    
    setLevel(_level) {
        if (_level === Deflater.DEFAULT_COMPRESSION) 
            _level = 6;
        else if ((_level < Deflater.NO_COMPRESSION) || _level > Deflater.BEST_COMPRESSION) 
            throw new Error("level");
        if (this.level !== _level) {
            this.level = _level;
            this.engine.setLevel(_level);
        }
    }
    
    getLevel() {
        return this.level;
    }
    
    setStrategy(strategy) {
        this.engine.strategy = strategy;
    }
    
    deflate(output) {
        return this.deflateEx(output, 0, output.length);
    }
    
    deflateEx(output, offset, length) {
        let origLength = length;
        if (this.state === Deflater.cLOSED_STATE) 
            throw new Error("Deflater closed");
        if (this.state < Deflater.bUSY_STATE) {
            let header = ((Deflater.DEFLATED + (((DeflaterConstants.MAX_WBITS - 8)) << 4))) << 8;
            let level_flags = ((this.level - 1)) >> 1;
            if ((level_flags < 0) || level_flags > 3) 
                level_flags = 3;
            header |= level_flags << 6;
            if (((this.state & Deflater.iS_SETDICT)) !== 0) 
                header |= DeflaterConstants.PRESET_DICT;
            header += (31 - ((header % 31)));
            this.pending.writeShortMSB(header);
            if (((this.state & Deflater.iS_SETDICT)) !== 0) {
                let chksum = this.engine.adler;
                this.engine.resetAdler();
                this.pending.writeShortMSB(chksum >> 16);
                this.pending.writeShortMSB(chksum & 0xffff);
            }
            this.state = Deflater.bUSY_STATE | ((this.state & ((Deflater.iS_FLUSHING | Deflater.iS_FINISHING))));
        }
        for (; ; ) {
            let count = this.pending.flush(output, offset, length);
            offset += count;
            this.m_TotalOut += count;
            length -= count;
            if (length === 0 || this.state === Deflater.fINISHED_STATE) 
                break;
            if (!this.engine.deflate(((this.state & Deflater.iS_FLUSHING)) !== 0, ((this.state & Deflater.iS_FINISHING)) !== 0)) {
                if (this.state === Deflater.bUSY_STATE) 
                    return origLength - length;
                else if (this.state === Deflater.fLUSHING_STATE) {
                    if (this.level !== Deflater.NO_COMPRESSION) {
                        let neededbits = 8 + ((((-this.pending.bitCount)) & 7));
                        while (neededbits > 0) {
                            this.pending.writeBits(2, 10);
                            neededbits -= 10;
                        }
                    }
                    this.state = Deflater.bUSY_STATE;
                }
                else if (this.state === Deflater.fINISHING_STATE) {
                    this.pending.alignToByte();
                    if (!this.noZlibHeaderOrFooter) {
                        let _adler = this.engine.adler;
                        this.pending.writeShortMSB(_adler >> 16);
                        this.pending.writeShortMSB(_adler & 0xffff);
                    }
                    this.state = Deflater.fINISHED_STATE;
                }
            }
        }
        return origLength - length;
    }
    
    setDictionary(dictionary) {
        this.setDictionaryEx(dictionary, 0, dictionary.length);
    }
    
    setDictionaryEx(dictionary, index, count) {
        if (this.state !== Deflater.iNIT_STATE) 
            throw new Error();
        this.state = Deflater.sETDICT_STATE;
        this.engine.setDictionary(dictionary, index, count);
    }
    
    static static_constructor() {
        Deflater.BEST_COMPRESSION = 9;
        Deflater.BEST_SPEED = 1;
        Deflater.DEFAULT_COMPRESSION = -1;
        Deflater.NO_COMPRESSION = 0;
        Deflater.DEFLATED = 8;
        Deflater.iS_SETDICT = 0x01;
        Deflater.iS_FLUSHING = 0x04;
        Deflater.iS_FINISHING = 0x08;
        Deflater.iNIT_STATE = 0x00;
        Deflater.sETDICT_STATE = 0x01;
        Deflater.bUSY_STATE = 0x10;
        Deflater.fLUSHING_STATE = 0x14;
        Deflater.fINISHING_STATE = 0x1c;
        Deflater.fINISHED_STATE = 0x1e;
        Deflater.cLOSED_STATE = 0x7f;
    }
}


Deflater.static_constructor();

module.exports = Deflater