//! SIMD vectorization for array operations (int[], float[], etc.).
//!
//! Provides accelerated array copy and fill using portable SIMD.

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use std::arch::x86_64::*;

/// SIMD-enabled array copy for int[] - copies src to dst using vectorized loads/stores
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx2")]
pub unsafe fn array_copy_int_simd(dst: &mut [i32], src: &[i32], len: usize) {
    let chunks = len / 8; // 8 x i32 = 256 bits
    for i in 0..chunks {
        let idx = i * 8;
        let v = _mm256_loadu_si256(src.as_ptr().add(idx) as *const __m256i);
        _mm256_storeu_si256(dst.as_mut_ptr().add(idx) as *mut __m256i, v);
    }
    // Scalar remainder
    for i in (chunks * 8)..len {
        dst[i] = src[i];
    }
}

/// Fallback scalar array copy when SIMD not available
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub fn array_copy_int_simd(dst: &mut [i32], src: &[i32], len: usize) {
    let end = len.min(dst.len()).min(src.len());
    dst[..end].copy_from_slice(&src[..end]);
}

/// SIMD-enabled array copy for float[] - copies src to dst
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx")]
pub unsafe fn array_copy_float_simd(dst: &mut [f32], src: &[f32], len: usize) {
    let chunks = len / 8;
    for i in 0..chunks {
        let idx = i * 8;
        let v = _mm256_loadu_ps(src.as_ptr().add(idx));
        _mm256_storeu_ps(dst.as_mut_ptr().add(idx), v);
    }
    for i in (chunks * 8)..len {
        dst[i] = src[i];
    }
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub fn array_copy_float_simd(dst: &mut [f32], src: &[f32], len: usize) {
    let end = len.min(dst.len()).min(src.len());
    dst[..end].copy_from_slice(&src[..end]);
}

/// Copy within HeapArray int[] using SIMD when available
pub fn heap_array_copy_int(
    dst: &mut [i32],
    src: &[i32],
    src_pos: usize,
    dst_pos: usize,
    len: usize,
) {
    let copy_len = len
        .min(src.len().saturating_sub(src_pos))
        .min(dst.len().saturating_sub(dst_pos));
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    {
        if copy_len >= 8 && is_x86_feature_detected!("avx2") {
            unsafe {
                array_copy_int_simd(&mut dst[dst_pos..], &src[src_pos..], copy_len);
            }
        } else {
            dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
        }
    }
    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
    {
        dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
    }
}

/// Copy within HeapArray float[] using SIMD when available
pub fn heap_array_copy_float(
    dst: &mut [f32],
    src: &[f32],
    src_pos: usize,
    dst_pos: usize,
    len: usize,
) {
    let copy_len = len
        .min(src.len().saturating_sub(src_pos))
        .min(dst.len().saturating_sub(dst_pos));
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    {
        if copy_len >= 8 && is_x86_feature_detected!("avx") {
            unsafe {
                array_copy_float_simd(&mut dst[dst_pos..], &src[src_pos..], copy_len);
            }
        } else {
            dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
        }
    }
    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
    {
        dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
    }
}

/// SIMD-enabled array copy for long[] - copies src to dst using AVX2 (256-bit vectors of 4 i64)
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx2")]
pub unsafe fn array_copy_long_simd(dst: &mut [i64], src: &[i64], len: usize) {
    let chunks = len / 4; // 4 x i64 = 256 bits
    for i in 0..chunks {
        let idx = i * 4;
        let v = _mm256_loadu_si256(src.as_ptr().add(idx) as *const __m256i);
        _mm256_storeu_si256(dst.as_mut_ptr().add(idx) as *mut __m256i, v);
    }
    for i in (chunks * 4)..len {
        dst[i] = src[i];
    }
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub fn array_copy_long_simd(dst: &mut [i64], src: &[i64], len: usize) {
    let end = len.min(dst.len()).min(src.len());
    dst[..end].copy_from_slice(&src[..end]);
}

/// Copy within HeapArray long[] using SIMD when available
pub fn heap_array_copy_long(
    dst: &mut [i64],
    src: &[i64],
    src_pos: usize,
    dst_pos: usize,
    len: usize,
) {
    let copy_len = len
        .min(src.len().saturating_sub(src_pos))
        .min(dst.len().saturating_sub(dst_pos));
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    {
        if copy_len >= 4 && is_x86_feature_detected!("avx2") {
            unsafe {
                array_copy_long_simd(&mut dst[dst_pos..], &src[src_pos..], copy_len);
            }
        } else {
            dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
        }
    }
    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
    {
        dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
    }
}

/// SIMD-enabled array copy for double[] - copies src to dst using AVX (256-bit vectors of 4 f64)
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx")]
pub unsafe fn array_copy_double_simd(dst: &mut [f64], src: &[f64], len: usize) {
    let chunks = len / 4; // 4 x f64 = 256 bits
    for i in 0..chunks {
        let idx = i * 4;
        let v = _mm256_loadu_pd(src.as_ptr().add(idx));
        _mm256_storeu_pd(dst.as_mut_ptr().add(idx), v);
    }
    for i in (chunks * 4)..len {
        dst[i] = src[i];
    }
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub fn array_copy_double_simd(dst: &mut [f64], src: &[f64], len: usize) {
    let end = len.min(dst.len()).min(src.len());
    dst[..end].copy_from_slice(&src[..end]);
}

/// Copy within HeapArray double[] using SIMD when available
pub fn heap_array_copy_double(
    dst: &mut [f64],
    src: &[f64],
    src_pos: usize,
    dst_pos: usize,
    len: usize,
) {
    let copy_len = len
        .min(src.len().saturating_sub(src_pos))
        .min(dst.len().saturating_sub(dst_pos));
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    {
        if copy_len >= 4 && is_x86_feature_detected!("avx") {
            unsafe {
                array_copy_double_simd(&mut dst[dst_pos..], &src[src_pos..], copy_len);
            }
        } else {
            dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
        }
    }
    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
    {
        dst[dst_pos..dst_pos + copy_len].copy_from_slice(&src[src_pos..src_pos + copy_len]);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_heap_array_copy_int() {
        let src = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let mut dst = vec![0; 10];
        heap_array_copy_int(&mut dst, &src, 0, 0, 10);
        assert_eq!(dst, src);
    }

    #[test]
    fn test_heap_array_copy_int_partial() {
        let src = vec![10, 20, 30, 40, 50];
        let mut dst = vec![0; 5];
        heap_array_copy_int(&mut dst, &src, 1, 0, 3);
        assert_eq!(dst, vec![20, 30, 40, 0, 0]);
    }

    #[test]
    fn test_heap_array_copy_float() {
        let src = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
        let mut dst = vec![0.0; 10];
        heap_array_copy_float(&mut dst, &src, 0, 0, 10);
        assert_eq!(dst, src);
    }

    #[test]
    fn test_heap_array_copy_long() {
        let src = vec![1i64, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let mut dst = vec![0i64; 10];
        heap_array_copy_long(&mut dst, &src, 0, 0, 10);
        assert_eq!(dst, src);
    }

    #[test]
    fn test_heap_array_copy_double() {
        let src = vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
        let mut dst = vec![0.0f64; 10];
        heap_array_copy_double(&mut dst, &src, 0, 0, 10);
        assert_eq!(dst, src);
    }

    #[test]
    fn test_heap_array_copy_bounds() {
        let src = vec![1, 2, 3];
        let mut dst = vec![0; 2];
        heap_array_copy_int(&mut dst, &src, 0, 0, 10);
        assert_eq!(dst, vec![1, 2]);
    }
}
