//
import { assert } from "../../../assert.js";
import { largest_common_alignment_uint32 } from "../../../binary/largest_common_alignment_uint32.js";
import { isArrayEqualStrict } from "../isArrayEqualStrict.js";

/**
 * Optimized equality method for typed arrays, typically significantly faster than `isArrayEqualStrict`, especially for larger arrays
 * @param {Uint8Array|Uint32Array} a
 * @param {Uint8Array|Uint32Array} b
 * @returns {boolean}
 */
export function is_typed_array_equals(a, b) {

    if (a === b) {
        // quick shortcut
        return true;
    }

    const a_length = a.length;
    const b_length = b.length;

    if (a_length !== b_length) {
        return false;
    }

    const a_constructor = a.constructor;
    const b_constructor = b.constructor;

    if (a_constructor !== b_constructor) {
        // incompatible constructors
        return false;
    }

    if (a_length === 0) {
        // both arrays are empty
        return true;
    }

    if (a_length < 128) { // size is determined empirically, nothing special about the number itself
        // small array, do direct compare
        return isArrayEqualStrict(a, b);
    }

    const byte_length = a.byteLength;

    if (byte_length !== b.byteLength) {
        return false;
    }


    const a_buffer = a.buffer;
    const b_buffer = b.buffer;

    // check if buffers are the same
    const a_offset = a.byteOffset;
    const b_offset = b.byteOffset;

    if (a_buffer === b_buffer && a_offset === b_offset) {
        // using the same buffer and pointing to the same range within it
        return true;
    }

    let a_proxy = a;
    let b_proxy = b;

    const bytes_per_element = a_constructor.BYTES_PER_ELEMENT;

    assert.isNonNegativeInteger(bytes_per_element, 'constructor.BYTES_PER_ELEMENT');

    const alignment = largest_common_alignment_uint32(a_offset, b_offset, byte_length);

    if (
        bytes_per_element < 4
        && alignment === 4
    ) {

        // 4 byte alignment, can use uint32
        a_proxy = new Uint32Array(a_buffer, a_offset, byte_length >>> 2);
        b_proxy = new Uint32Array(b_buffer, b_offset, byte_length >>> 2);

    } else if (
        bytes_per_element < 2
        && alignment === 2
    ) {

        // 2 byte alignment, can use uint16
        a_proxy = new Uint16Array(a_buffer, a_offset, byte_length >>> 1);
        b_proxy = new Uint16Array(b_buffer, b_offset, byte_length >>> 1);

    }

    return isArrayEqualStrict(a_proxy, b_proxy);
}
