import { assert } from "../../../../../core/assert.js";

/*
 * Ported from C source at https://www.w3.org/TR/PNG-CRCAppendix.html
 * See also ISO 3309 [ISO-3309] or ITU-T V.42 [ITU-V42] for a formal specification
 */

/**
 *
 * @type {Uint32Array}
 */
const crc_table = new Uint32Array(256);

// Precompute checksum table
for (let n = 0; n < 256; n++) {
    let c = n;

    for (let k = 0; k < 8; k++) {

        if ((c & 1) !== 0) {
            // CRC32b polynomial
            c = 0xedb88320 ^ (c >>> 1);
        } else {
            c = c >>> 1;
        }

    }

    crc_table[n] = c;
}

const INITIAL = 0xffffffff;

/**
 * Update a running CRC with the bytes buf[0..len-1]--the CRC
 * should be initialized to all 1's, and the transmitted value
 * is the 1's complement of the final running CRC (see the crc32() routine below)).
 * @param {number} input_crc starting CRC value
 * @param {Uint8Array} buffer
 * @param {number} offset
 * @param {number} length
 * @return {number}
 */
function update_crc(
    input_crc,
    buffer,
    offset,
    length,
) {
    assert.isNonNegativeInteger(input_crc, 'input_crc');
    assert.defined(buffer, 'buffer');
    assert.isNonNegativeInteger(offset, 'offset');
    assert.isNonNegativeInteger(length, 'length');

    let crc = input_crc;

    const end = offset + length;

    for (let n = offset; n < end; n++) {

        const byte = buffer[n];

        const lookup_index = (crc ^ byte) & 0xff;

        crc = crc_table[lookup_index] ^ (crc >>> 8);

    }

    return crc;
}

/**
 * 32-bit Cyclic Redundancy Checksum.
 * @see https://en.wikipedia.org/wiki/Cyclic_redundancy_check
 * @param {Uint8Array} buffer byte buffer
 * @param {number} [offset=0] where to start in the input buffer
 * @param {number} [length=buffer.length] how many bytes to include in checksum calculation
 * @return {number} Unsigned 32-bit integer value
 * @copyright Company Named Limited (c) 2026
 *
 * @example
 *      const buffer = new BinaryBuffer();
 *      buffer.writeASCIIString("Hello World");
 *      const checksum = crc32(buffer.raw_bytes, 0, buffer.position);
 */
export function crc32(
    buffer,
    offset=0,
    length=buffer.length,
) {
    return (update_crc(INITIAL, buffer, offset, length) ^ INITIAL) >>> 0;
}
