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

/**
 * This is essentially a {@link memcopy} implementation for ArrayBuffers
 * NOTE: allocates light-weight typed arrays under the hood, so beware of potential GC implications. Generally not an issue, unless you're copying very large number of very small arrays, consider alternatives such as {@link array_copy} instead
 * @param {ArrayBuffer} source
 * @param {number} source_offset
 * @param {ArrayBuffer} target
 * @param {number} target_offset
 * @param {number} byte_length
 */
export function array_buffer_copy(
    source, source_offset,
    target, target_offset,
    byte_length
) {
    assert.isNonNegativeInteger(source_offset, 'source_offset');
    assert.isNonNegativeInteger(target_offset, 'target_offset');
    assert.isNonNegativeInteger(byte_length, 'byte_length');
    // assert.ok(source instanceof ArrayBuffer || source instanceof SharedArrayBuffer,'source is not an ArrayBuffer');
    // assert.ok(target instanceof ArrayBuffer || target instanceof SharedArrayBuffer,'source is not an ArrayBuffer');

    /**
     * @type {Uint8Array|Uint16Array|Uint32Array}
     */
    let source_array;
    /**
     * @type {Uint8Array|Uint16Array|Uint32Array}
     */
    let target_array;

    const alignment = largest_common_alignment_uint32(source_offset,target_offset,byte_length);

    if (
        alignment === 4
    ) {
        // aligned on 4-byte boundary
        source_array = new Uint32Array(source, source_offset, byte_length >>> 2);
        target_array = new Uint32Array(target, target_offset, byte_length >>> 2);

    } else if (
        alignment === 2
    ) {
        // aligned on 2-byte boundary
        source_array = new Uint16Array(source, source_offset, byte_length >>> 1);
        target_array = new Uint16Array(target, target_offset, byte_length >>> 1);

    } else {
        // worst-case, assume 1-byte boundary
        source_array = new Uint8Array(source, source_offset, byte_length);
        target_array = new Uint8Array(target, target_offset, byte_length);
    }

    target_array.set(source_array);
}

