/*
 * 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 Utils = require("./../../../unisharp/Utils");

const DeflaterConstants = require("./DeflaterConstants");

// This is the DeflaterHuffman class.
// This class is <i>not</i> thread safe.  This is inherent in the API, due
// to the split of Deflate and SetInput.
class DeflaterHuffman {
    
    constructor(_pending) {
        this.pending = null;
        this.literalTree = null;
        this.distTree = null;
        this.blTree = null;
        this.d_buf = null;
        this.l_buf = null;
        this.last_lit = 0;
        this.extra_bits = 0;
        this.pending = _pending;
        this.literalTree = new DeflaterHuffman.Tree(this, DeflaterHuffman.lITERAL_NUM, 257, 15);
        this.distTree = new DeflaterHuffman.Tree(this, DeflaterHuffman.dIST_NUM, 1, 15);
        this.blTree = new DeflaterHuffman.Tree(this, DeflaterHuffman.bITLEN_NUM, 4, 7);
        this.d_buf = new Int16Array(DeflaterHuffman.bUFSIZE);
        this.l_buf = new Uint8Array(DeflaterHuffman.bUFSIZE);
    }
    
    reset() {
        this.last_lit = 0;
        this.extra_bits = 0;
        this.literalTree.reset();
        this.distTree.reset();
        this.blTree.reset();
    }
    
    sendAllTrees(blTreeCodes) {
        this.blTree.buildCodes();
        this.literalTree.buildCodes();
        this.distTree.buildCodes();
        this.pending.writeBits(this.literalTree.numCodes - 257, 5);
        this.pending.writeBits(this.distTree.numCodes - 1, 5);
        this.pending.writeBits(blTreeCodes - 4, 4);
        for (let rank = 0; rank < blTreeCodes; rank++) {
            this.pending.writeBits(this.blTree.length[DeflaterHuffman.bL_ORDER[rank]], 3);
        }
        this.literalTree.writeTree(this.blTree);
        this.distTree.writeTree(this.blTree);
    }
    
    compressBlock() {
        for (let i = 0; i < this.last_lit; i++) {
            let litlen = (this.l_buf[i]) & 0xff;
            let dist = (this.d_buf[i]) - 1;
            if (dist !== -1) {
                let lc = DeflaterHuffman.lcode(litlen);
                this.literalTree.writeSymbol(lc);
                let bits = Utils.intDiv((lc - 261), 4);
                if (bits > 0 && bits <= 5) 
                    this.pending.writeBits(litlen & (((1 << bits) - 1)), bits);
                let dc = DeflaterHuffman.dcode(dist);
                this.distTree.writeSymbol(dc);
                bits = (Utils.intDiv(dc, 2)) - 1;
                if (bits > 0) 
                    this.pending.writeBits(dist & (((1 << bits) - 1)), bits);
            }
            else 
                this.literalTree.writeSymbol(litlen);
        }
        this.literalTree.writeSymbol(DeflaterHuffman.eOF_SYMBOL);
    }
    
    flushStoredBlock(stored, storedOffset, storedLength, lastBlock) {
        this.pending.writeBits((DeflaterConstants.STORED_BLOCK << 1) + ((lastBlock ? 1 : 0)), 3);
        this.pending.alignToByte();
        this.pending.writeShort(storedLength);
        this.pending.writeShort(~storedLength);
        this.pending.writeBlock(stored, storedOffset, storedLength);
        this.reset();
    }
    
    flushBlock(stored, storedOffset, storedLength, lastBlock) {
        this.literalTree.freqs[DeflaterHuffman.eOF_SYMBOL]++;
        this.literalTree.buildTree();
        this.distTree.buildTree();
        this.literalTree.calcBLFreq(this.blTree);
        this.distTree.calcBLFreq(this.blTree);
        this.blTree.buildTree();
        let blTreeCodes = 4;
        for (let i = 18; i > blTreeCodes; i--) {
            if (this.blTree.length[DeflaterHuffman.bL_ORDER[i]] > (0)) 
                blTreeCodes = i + 1;
        }
        let opt_len = ((14 + (blTreeCodes * 3) + this.blTree.getEncodedLength()) + this.literalTree.getEncodedLength() + this.distTree.getEncodedLength()) + this.extra_bits;
        let static_len = this.extra_bits;
        for (let i = 0; i < DeflaterHuffman.lITERAL_NUM; i++) {
            static_len += ((this.literalTree.freqs[i]) * (DeflaterHuffman.staticLLength[i]));
        }
        for (let i = 0; i < DeflaterHuffman.dIST_NUM; i++) {
            static_len += ((this.distTree.freqs[i]) * (DeflaterHuffman.staticDLength[i]));
        }
        if (opt_len >= static_len) 
            opt_len = static_len;
        if (storedOffset >= 0 && ((storedLength + 4) < (opt_len >> 3))) 
            this.flushStoredBlock(stored, storedOffset, storedLength, lastBlock);
        else if (opt_len === static_len) {
            this.pending.writeBits((DeflaterConstants.STATIC_TREES << 1) + ((lastBlock ? 1 : 0)), 3);
            this.literalTree.setStaticCodes(DeflaterHuffman.staticLCodes, DeflaterHuffman.staticLLength);
            this.distTree.setStaticCodes(DeflaterHuffman.staticDCodes, DeflaterHuffman.staticDLength);
            this.compressBlock();
            this.reset();
        }
        else {
            this.pending.writeBits((DeflaterConstants.DYN_TREES << 1) + ((lastBlock ? 1 : 0)), 3);
            this.sendAllTrees(blTreeCodes);
            this.compressBlock();
            this.reset();
        }
    }
    
    isFull() {
        return this.last_lit >= DeflaterHuffman.bUFSIZE;
    }
    
    tallyLit(literal) {
        this.d_buf[this.last_lit] = 0;
        this.l_buf[this.last_lit++] = literal;
        this.literalTree.freqs[literal]++;
        return this.isFull();
    }
    
    tallyDist(distance, length) {
        this.d_buf[this.last_lit] = distance;
        this.l_buf[this.last_lit++] = (length - 3);
        let lc = DeflaterHuffman.lcode(length - 3);
        this.literalTree.freqs[lc]++;
        if (lc >= 265 && (lc < 285)) 
            this.extra_bits += (Utils.intDiv((lc - 261), 4));
        let dc = DeflaterHuffman.dcode(distance - 1);
        this.distTree.freqs[dc]++;
        if (dc >= 4) 
            this.extra_bits += ((Utils.intDiv(dc, 2)) - 1);
        return this.isFull();
    }
    
    static bitReverse(toReverse) {
        return ((((DeflaterHuffman.bit4Reverse[toReverse & 0xF]) << 12) | ((DeflaterHuffman.bit4Reverse[((toReverse >> 4)) & 0xF]) << 8) | ((DeflaterHuffman.bit4Reverse[((toReverse >> 8)) & 0xF]) << 4)) | (DeflaterHuffman.bit4Reverse[((toReverse >> 12)) & 0xF]));
    }
    
    static lcode(length) {
        if (length === 255) 
            return 285;
        let code = 257;
        while (length >= 8) {
            code += 4;
            length >>= 1;
        }
        return code + length;
    }
    
    static dcode(distance) {
        let code = 0;
        while (distance >= 4) {
            code += 2;
            distance >>= 1;
        }
        return code + distance;
    }
    
    static static_constructor() {
        DeflaterHuffman.bUFSIZE = 1 << ((DeflaterConstants.DEFAULT_MEM_LEVEL + 6));
        DeflaterHuffman.lITERAL_NUM = 286;
        DeflaterHuffman.dIST_NUM = 30;
        DeflaterHuffman.bITLEN_NUM = 19;
        DeflaterHuffman.rEP_3_6 = 16;
        DeflaterHuffman.rEP_3_10 = 17;
        DeflaterHuffman.rEP_11_138 = 18;
        DeflaterHuffman.eOF_SYMBOL = 256;
        DeflaterHuffman.bL_ORDER = new Int32Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
        DeflaterHuffman.bit4Reverse = new Uint8Array([0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]);
        DeflaterHuffman.staticLCodes = null;
        DeflaterHuffman.staticLLength = null;
        DeflaterHuffman.staticDCodes = null;
        DeflaterHuffman.staticDLength = null;
        DeflaterHuffman.staticLCodes = new Int16Array(DeflaterHuffman.lITERAL_NUM);
        DeflaterHuffman.staticLLength = new Uint8Array(DeflaterHuffman.lITERAL_NUM);
        let i = 0;
        while (i < 144) {
            DeflaterHuffman.staticLCodes[i] = DeflaterHuffman.bitReverse(((0x030 + i)) << 8);
            DeflaterHuffman.staticLLength[i++] = 8;
        }
        while (i < 256) {
            DeflaterHuffman.staticLCodes[i] = DeflaterHuffman.bitReverse((((0x190 - 144) + i)) << 7);
            DeflaterHuffman.staticLLength[i++] = 9;
        }
        while (i < 280) {
            DeflaterHuffman.staticLCodes[i] = DeflaterHuffman.bitReverse((((0x000 - 256) + i)) << 9);
            DeflaterHuffman.staticLLength[i++] = 7;
        }
        while (i < DeflaterHuffman.lITERAL_NUM) {
            DeflaterHuffman.staticLCodes[i] = DeflaterHuffman.bitReverse((((0x0c0 - 280) + i)) << 8);
            DeflaterHuffman.staticLLength[i++] = 8;
        }
        DeflaterHuffman.staticDCodes = new Int16Array(DeflaterHuffman.dIST_NUM);
        DeflaterHuffman.staticDLength = new Uint8Array(DeflaterHuffman.dIST_NUM);
        for (i = 0; i < DeflaterHuffman.dIST_NUM; i++) {
            DeflaterHuffman.staticDCodes[i] = DeflaterHuffman.bitReverse(i << 11);
            DeflaterHuffman.staticDLength[i] = 5;
        }
    }
}


DeflaterHuffman.Tree = class  {
    
    constructor(_dh, elems, minCodes, _maxLength) {
        this.freqs = null;
        this.length = null;
        this.minNumCodes = 0;
        this.numCodes = 0;
        this.codes = null;
        this.bl_counts = null;
        this.maxLength = 0;
        this.dh = null;
        this.dh = _dh;
        this.minNumCodes = minCodes;
        this.maxLength = _maxLength;
        this.freqs = new Int16Array(elems);
        this.bl_counts = new Int32Array(_maxLength);
    }
    
    reset() {
        for (let i = 0; i < this.freqs.length; i++) {
            this.freqs[i] = 0;
        }
        this.codes = null;
        this.length = null;
    }
    
    writeSymbol(code) {
        this.dh.pending.writeBits((this.codes[code]) & 0xffff, this.length[code]);
    }
    
    checkEmpty() {
        let empty = true;
        for (let i = 0; i < this.freqs.length; i++) {
            if (this.freqs[i] !== (0)) 
                empty = false;
        }
        if (!empty) 
            throw new Error("!Empty");
    }
    
    setStaticCodes(staticCodes, staticLengths) {
        this.codes = staticCodes;
        this.length = staticLengths;
    }
    
    buildCodes() {
        let numSymbols = this.freqs.length;
        let nextCode = new Int32Array(this.maxLength);
        let code = 0;
        this.codes = new Int16Array(this.freqs.length);
        for (let bits = 0; bits < this.maxLength; bits++) {
            nextCode[bits] = code;
            code += this.bl_counts[bits] << ((15 - bits));
        }
        for (let i = 0; i < this.numCodes; i++) {
            let bits = this.length[i];
            if (bits > 0) {
                this.codes[i] = DeflaterHuffman.bitReverse(nextCode[bits - 1]);
                nextCode[bits - 1] += 1 << ((16 - bits));
            }
        }
    }
    
    buildTree() {
        let numSymbols = this.freqs.length;
        let heap = new Int32Array(numSymbols);
        let heapLen = 0;
        let maxCode = 0;
        for (let n = 0; n < numSymbols; n++) {
            let freq = this.freqs[n];
            if (freq !== 0) {
                let pos = heapLen++;
                let ppos = 0;
                while (pos > 0 && this.freqs[heap[(ppos = Utils.intDiv((pos - 1), 2))]] > freq) {
                    heap[pos] = heap[ppos];
                    pos = ppos;
                }
                heap[pos] = n;
                maxCode = n;
            }
        }
        while (heapLen < 2) {
            let node = (maxCode < 2 ? ++maxCode : 0);
            heap[heapLen++] = node;
        }
        this.numCodes = Math.max(maxCode + 1, this.minNumCodes);
        let numLeafs = heapLen;
        let childs = new Int32Array((4 * heapLen) - 2);
        let values = new Int32Array((2 * heapLen) - 1);
        let numNodes = numLeafs;
        for (let i = 0; i < heapLen; i++) {
            let node = heap[i];
            childs[2 * i] = node;
            childs[(2 * i) + 1] = -1;
            values[i] = (this.freqs[node]) << 8;
            heap[i] = i;
        }
        do{
            let first = heap[0];
            let last = heap[--heapLen];
            let ppos = 0;
            let path = 1;
            while (path < heapLen) {
                if (((path + 1) < heapLen) && values[heap[path]] > values[heap[path + 1]]) 
                    path++;
                heap[ppos] = heap[path];
                ppos = path;
                path = (path * 2) + 1;
            }
            let lastVal = values[last];
            while ((((path = ppos))) > 0 && values[heap[(ppos = Utils.intDiv((path - 1), 2))]] > lastVal) {
                heap[path] = heap[ppos];
            }
            heap[path] = last;
            let second = heap[0];
            last = numNodes++;
            childs[2 * last] = first;
            childs[(2 * last) + 1] = second;
            let mindepth = Math.min(values[first] & 0xff, values[second] & 0xff);
            values[last] = (lastVal = ((values[first] + values[second]) - mindepth) + 1);
            ppos = 0;
            path = 1;
            while (path < heapLen) {
                if (((path + 1) < heapLen) && values[heap[path]] > values[heap[path + 1]]) 
                    path++;
                heap[ppos] = heap[path];
                ppos = path;
                path = (ppos * 2) + 1;
            }
            while ((((path = ppos))) > 0 && values[heap[(ppos = Utils.intDiv((path - 1), 2))]] > lastVal) {
                heap[path] = heap[ppos];
            }
            heap[path] = last;
        } while (heapLen > 1); 
        if (heap[0] !== ((Utils.intDiv(childs.length, 2)) - 1)) 
            throw new Error("Heap invariant violated");
        this.buildLength(childs);
    }
    
    getEncodedLength() {
        let len = 0;
        for (let i = 0; i < this.freqs.length; i++) {
            len += ((this.freqs[i]) * (this.length[i]));
        }
        return len;
    }
    
    calcBLFreq(blTree) {
        let max_count = 0;
        let min_count = 0;
        let count = 0;
        let curlen = -1;
        let i = 0;
        while (i < this.numCodes) {
            count = 1;
            let nextlen = this.length[i];
            if (nextlen === 0) {
                max_count = 138;
                min_count = 3;
            }
            else {
                max_count = 6;
                min_count = 3;
                if (curlen !== nextlen) {
                    blTree.freqs[nextlen]++;
                    count = 0;
                }
            }
            curlen = nextlen;
            i++;
            while ((i < this.numCodes) && curlen === (this.length[i])) {
                i++;
                if ((++count) >= max_count) 
                    break;
            }
            if (count < min_count) 
                blTree.freqs[curlen] += (count);
            else if (curlen !== 0) 
                blTree.freqs[DeflaterHuffman.rEP_3_6]++;
            else if (count <= 10) 
                blTree.freqs[DeflaterHuffman.rEP_3_10]++;
            else 
                blTree.freqs[DeflaterHuffman.rEP_11_138]++;
        }
    }
    
    writeTree(blTree) {
        let max_count = 0;
        let min_count = 0;
        let count = 0;
        let curlen = -1;
        let i = 0;
        while (i < this.numCodes) {
            count = 1;
            let nextlen = this.length[i];
            if (nextlen === 0) {
                max_count = 138;
                min_count = 3;
            }
            else {
                max_count = 6;
                min_count = 3;
                if (curlen !== nextlen) {
                    blTree.writeSymbol(nextlen);
                    count = 0;
                }
            }
            curlen = nextlen;
            i++;
            while ((i < this.numCodes) && curlen === (this.length[i])) {
                i++;
                if ((++count) >= max_count) 
                    break;
            }
            if (count < min_count) {
                while ((count--) > 0) {
                    blTree.writeSymbol(curlen);
                }
            }
            else if (curlen !== 0) {
                blTree.writeSymbol(DeflaterHuffman.rEP_3_6);
                this.dh.pending.writeBits(count - 3, 2);
            }
            else if (count <= 10) {
                blTree.writeSymbol(DeflaterHuffman.rEP_3_10);
                this.dh.pending.writeBits(count - 3, 3);
            }
            else {
                blTree.writeSymbol(DeflaterHuffman.rEP_11_138);
                this.dh.pending.writeBits(count - 11, 7);
            }
        }
    }
    
    buildLength(childs) {
        this.length = new Uint8Array(this.freqs.length);
        let numNodes = Utils.intDiv(childs.length, 2);
        let numLeafs = Utils.intDiv((numNodes + 1), 2);
        let overflow = 0;
        for (let i = 0; i < this.maxLength; i++) {
            this.bl_counts[i] = 0;
        }
        let lengths = new Int32Array(numNodes);
        lengths[numNodes - 1] = 0;
        for (let i = numNodes - 1; i >= 0; i--) {
            if (childs[(2 * i) + 1] !== -1) {
                let bitLength = lengths[i] + 1;
                if (bitLength > this.maxLength) {
                    bitLength = this.maxLength;
                    overflow++;
                }
                lengths[childs[2 * i]] = (lengths[childs[(2 * i) + 1]] = bitLength);
            }
            else {
                let bitLength = lengths[i];
                this.bl_counts[bitLength - 1]++;
                this.length[childs[2 * i]] = lengths[i];
            }
        }
        if (overflow === 0) 
            return;
        let incrBitLen = this.maxLength - 1;
        do{
            while (this.bl_counts[--incrBitLen] === 0) {
            }
            do{
                this.bl_counts[incrBitLen]--;
                this.bl_counts[++incrBitLen]++;
                overflow -= 1 << ((this.maxLength - 1 - incrBitLen));
            } while (overflow > 0 && (incrBitLen < (this.maxLength - 1))); 
        } while (overflow > 0); 
        this.bl_counts[this.maxLength - 1] += overflow;
        this.bl_counts[this.maxLength - 2] -= overflow;
        let nodePtr = 2 * numLeafs;
        for (let bits = this.maxLength; bits !== 0; bits--) {
            let n = this.bl_counts[bits - 1];
            while (n > 0) {
                let childPtr = 2 * childs[nodePtr++];
                if (childs[childPtr + 1] === -1) {
                    this.length[childs[childPtr]] = bits;
                    n--;
                }
            }
        }
    }
}


DeflaterHuffman.static_constructor();

module.exports = DeflaterHuffman