/**
 * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 *
 * ported from https://github.com/photopea/UZIP.js/blob/master/UZIP.js
 * MIT License, Copyright (c) 2018 Photopea
 */

export const U = (function(){
    const u16=Uint16Array, u32=Uint32Array;
    return {
        next_code : new u16(16),
        bl_count  : new u16(16),
        ordr : [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ],
        of0  : [3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],
        exb  : [0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0,  0,  0,  0],
        ldef : new u16(32),
        df0  : [1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 65535, 65535],
        dxb  : [0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5,  6,  6,  7,  7,  8,  8,   9,   9,  10,  10,  11,  11,  12,   12,   13,   13,     0,     0],
        ddef : new u32(32),
        flmap: new u16(  512),  fltree: [] as number[],
        fdmap: new u16(   32),  fdtree: [] as number[],
        lmap : new u16(32768),  ltree : [] as number[],  ttree:[] as number[],
        dmap : new u16(32768),  dtree : [] as number[],
        imap : new u16(  512),  itree : [] as number[],
        // rev9 : new u16(  512)
        rev15: new u16(1<<15),
        lhst : new u32(286), dhst : new u32( 30), ihst : new u32(19),
        lits : new u32(15000),
        strt : new u16(1<<16),
        prev : new u16(1<<15)
    };
})();

(function(){
    const len = 1<<15;
    for(let i=0; i<len; i++) {
        let x = i;
        x = (((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1));
        x = (((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2));
        x = (((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4));
        x = (((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8));
        U.rev15[i] = (((x >>> 16) | (x << 16)))>>>17;
    }

    function pushV(tgt: number[], n: number, sv: number) {
        while(n-- !== 0) tgt.push(0, sv);
    }

    for(let i=0; i<32; i++) {
        U.ldef[i] = (U.of0[i]<<3)|U.exb[i];
        U.ddef[i] = (U.df0[i]<<4)|U.dxb[i];
    }

    pushV(U.fltree, 144, 8);
    pushV(U.fltree, 255-143, 9);
    pushV(U.fltree, 279-255, 7);
    pushV(U.fltree,287-279,8);
    /*
    var i = 0;
    for(; i<=143; i++) U.fltree.push(0,8);
    for(; i<=255; i++) U.fltree.push(0,9);
    for(; i<=279; i++) U.fltree.push(0,7);
    for(; i<=287; i++) U.fltree.push(0,8);
    */
    makeCodes(U.fltree, 9);
    codes2map(U.fltree, 9, U.flmap);
    revCodes (U.fltree, 9)

    pushV(U.fdtree,32,5);
    // for(i=0;i<32; i++) U.fdtree.push(0,5);
    makeCodes(U.fdtree, 5);
    codes2map(U.fdtree, 5, U.fdmap);
    revCodes (U.fdtree, 5)

    pushV(U.itree,19,0);  pushV(U.ltree,286,0);  pushV(U.dtree,30,0);  pushV(U.ttree,320,0);
    /*
    for(var i=0; i< 19; i++) U.itree.push(0,0);
    for(var i=0; i<286; i++) U.ltree.push(0,0);
    for(var i=0; i< 30; i++) U.dtree.push(0,0);
    for(var i=0; i<320; i++) U.ttree.push(0,0);
    */
})();

export function codes2map(tree: number[], MAX_BITS: number, map: Uint16Array) {
    const max_code = tree.length;
    const r15 = U.rev15;
    for(let i=0; i<max_code; i+=2) {
        if(tree[i+1] !== 0)  {
            const lit = i>>1;
            const cl = tree[i+1], val = (lit<<4)|cl; // :  (0x8000 | (U.of0[lit-257]<<7) | (U.exb[lit-257]<<4) | cl);
            const rest = (MAX_BITS-cl)
            let i0 = tree[i]<<rest
            const i1 = i0 + (1<<rest);
            // tree[i]=r15[i0]>>>(15-MAX_BITS);
            while(i0 !== i1) {
                const p0 = r15[i0]>>>(15-MAX_BITS);
                map[p0]=val;  i0++;
            }
        }
    }
}

export function makeCodes(tree: number[], MAX_BITS: number) {  // code, length
    const max_code = tree.length;

    const bl_count = U.bl_count;
    for(let i=0; i<=MAX_BITS; i++) bl_count[i]=0;
    for(let i=1; i<max_code; i+=2) bl_count[tree[i]]++;

    const next_code = U.next_code;	// smallest code for each length

    let code = 0;
    bl_count[0] = 0;
    for (let bits = 1; bits <= MAX_BITS; bits++) {
        code = (code + bl_count[bits-1]) << 1;
        next_code[bits] = code;
    }

    for (let n = 0; n < max_code; n+=2) {
        const len = tree[n+1];
        if (len !== 0) {
            tree[n] = next_code[len];
            next_code[len]++;
        }
    }
}

export function revCodes(tree: number[], MAX_BITS: number) {
    const r15 = U.rev15, imb = 15-MAX_BITS;
    for(let i=0; i<tree.length; i+=2) {
        const i0 = (tree[i]<<(MAX_BITS-tree[i+1]));
        tree[i] = r15[i0]>>>imb;
    }
}