import { log } from '@/logger'
// Internal function to compress drawing using run length encoding
// inputs
// data: Uint8Array to compress
// output
// returns rle compressed Uint8Array
export function encodeRLE(data: Uint8Array): Uint8Array {
    // https://en.wikipedia.org/wiki/PackBits
    // run length encoding
    // input and output are Uint8Array
    // Will compress data with long runs up to x64
    // Worst case encoded size is ~1% larger than input
    const dl = data.length // input length
    let dp = 0 // input position
    // worst case: run length encoding (1+1/127) times larger than input
    const r = new Uint8Array(dl + Math.ceil(0.01 * dl))
    const rI = new Int8Array(r.buffer) // typecast as header can be negative
    let rp = 0 // run length position
    while (dp < dl) {
        // for each byte in input
        let v = data[dp]
        dp++
        let rl = 1 // run length
        while (rl < 129 && dp < dl && data[dp] === v) {
            dp++
            rl++
        }
        if (rl > 1) {
            // header
            rI[rp] = -rl + 1
            rp++
            r[rp] = v
            rp++
            continue
        }
        // count literal length
        while (dp < dl) {
            if (rl > 127) {
                break
            }
            if (dp + 2 < dl) {
                if (v !== data[dp] && data[dp + 2] === data[dp] && data[dp + 1] === data[dp]) {
                    break
                }
            }
            v = data[dp]
            dp++
            rl++
        }
        // write header
        r[rp] = rl - 1
        rp++
        for (let i = 0; i < rl; i++) {
            r[rp] = data[dp - rl + i]
            rp++
        }
    }
    log.debug('PackBits ' + dl + ' -> ' + rp + ' bytes (x' + dl / rp + ')')
    return r.slice(0, rp)
}

// Internal function to decompress drawing using run length encoding
// inputs
// rle: packbits compressed stream
// decodedlen: size of uncompressed data
// output
// returns Uint8Array of decodedlen bytes
export function decodeRLE(rle: Uint8Array, decodedlen: number): Uint8Array {
    const r = new Uint8Array(rle.buffer)
    const rI = new Int8Array(r.buffer) // typecast as header can be negative
    let rp = 0 // input position in rle array
    // d: output uncompressed data array
    const d = new Uint8Array(decodedlen)
    let dp = 0 // output position in decoded array
    while (rp < r.length) {
        // read header
        const hdr = rI[rp]
        rp++
        if (hdr < 0) {
            // write run
            const v = rI[rp]
            rp++
            for (let i = 0; i < 1 - hdr; i++) {
                d[dp] = v
                dp++
            }
        } else {
            // write literal
            for (let i = 0; i < hdr + 1; i++) {
                d[dp] = rI[rp]
                rp++
                dp++
            }
        }
    }
    return d
}
