#ifndef SIMD_OPS_H
#define SIMD_OPS_H

#include <cstdint>
#include <cstddef>
#include <vector>

namespace simd
{

// Basic arithmetic operations
// 8-bit operations
void add_u8(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8(uint8_t* a, float factor, size_t n);

// 16-bit operations
void add_u16(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16(uint16_t* a, float factor, size_t n);

// New pixel statistics operations
float mean_u8(const uint8_t* data, size_t n);
float mean_u16(const uint16_t* data, size_t n);
float variance_u8(const uint8_t* data, size_t n);
float variance_u16(const uint16_t* data, size_t n);
uint8_t median_u8(uint8_t* data, size_t n);
uint16_t median_u16(uint16_t* data, size_t n);

// New image processing operations
void min_max_u8(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins = 256);
void histogram_u16(const uint16_t* data, size_t n, uint32_t* histogram, size_t bins = 65536);
void threshold_u8(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);

// New filtering operations
void gaussian_blur_u8(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
void median_filter_u8(uint8_t* dst, const uint8_t* src, int width, int height, int channels, int kernel_size);

// Generic implementations
void add_u8_generic(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8_generic(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8_generic(uint8_t* a, float factor, size_t n);
void add_u16_generic(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16_generic(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16_generic(uint16_t* a, float factor, size_t n);
float mean_u8_generic(const uint8_t* data, size_t n);
float mean_u16_generic(const uint16_t* data, size_t n);
float variance_u8_generic(const uint8_t* data, size_t n);
float variance_u16_generic(const uint16_t* data, size_t n);
uint8_t median_u8_generic(uint8_t* data, size_t n);
uint16_t median_u16_generic(uint16_t* data, size_t n);
void min_max_u8_generic(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16_generic(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8_generic(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins);
void histogram_u16_generic(const uint16_t* data, size_t n, uint32_t* histogram, size_t bins);
void threshold_u8_generic(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16_generic(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);
void gaussian_blur_u8_generic(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
void median_filter_u8_generic(uint8_t* dst, const uint8_t* src, int width, int height, int channels, int kernel_size);

#ifdef HAVE_SSE
// SSE implementations for x86_64
void add_u8_sse(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8_sse(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8_sse(uint8_t* a, float factor, size_t n);
void add_u16_sse(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16_sse(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16_sse(uint16_t* a, float factor, size_t n);
float mean_u8_sse(const uint8_t* data, size_t n);
float mean_u16_sse(const uint16_t* data, size_t n);
float variance_u8_sse(const uint8_t* data, size_t n);
float variance_u16_sse(const uint16_t* data, size_t n);
uint8_t median_u8_sse(uint8_t* data, size_t n);
uint16_t median_u16_sse(uint16_t* data, size_t n);
void min_max_u8_sse(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16_sse(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8_sse(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins);
void threshold_u8_sse(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16_sse(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);
void gaussian_blur_u8_sse(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
void median_filter_u8_sse(uint8_t* dst, const uint8_t* src, int width, int height, int channels, int kernel_size);
#endif

#ifdef HAVE_AVX
// AVX implementations for x86_64
void add_u8_avx(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8_avx(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8_avx(uint8_t* a, float factor, size_t n);
void add_u16_avx(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16_avx(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16_avx(uint16_t* a, float factor, size_t n);
float mean_u8_avx(const uint8_t* data, size_t n);
float mean_u16_avx(const uint16_t* data, size_t n);
float variance_u8_avx(const uint8_t* data, size_t n);
float variance_u16_avx(const uint16_t* data, size_t n);
void min_max_u8_avx(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16_avx(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8_avx(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins);
void threshold_u8_avx(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16_avx(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);
void gaussian_blur_u8_avx(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
#endif

#ifdef HAVE_AVX2
// AVX2 implementations for x86_64
void add_u8_avx2(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8_avx2(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8_avx2(uint8_t* a, float factor, size_t n);
void add_u16_avx2(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16_avx2(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16_avx2(uint16_t* a, float factor, size_t n);
float mean_u8_avx2(const uint8_t* data, size_t n);
float mean_u16_avx2(const uint16_t* data, size_t n);
float variance_u8_avx2(const uint8_t* data, size_t n);
float variance_u16_avx2(const uint16_t* data, size_t n);
void min_max_u8_avx2(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16_avx2(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8_avx2(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins);
void threshold_u8_avx2(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16_avx2(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);
void gaussian_blur_u8_avx2(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
void median_filter_u8_avx2(uint8_t* dst, const uint8_t* src, int width, int height, int channels, int kernel_size);
#endif

#ifdef HAVE_NEON
// NEON implementations for ARM64
void add_u8_neon(uint8_t* a, const uint8_t* b, size_t n);
void subtract_u8_neon(uint8_t* a, const uint8_t* b, size_t n);
void multiply_u8_neon(uint8_t* a, float factor, size_t n);
void add_u16_neon(uint16_t* a, const uint16_t* b, size_t n);
void subtract_u16_neon(uint16_t* a, const uint16_t* b, size_t n);
void multiply_u16_neon(uint16_t* a, float factor, size_t n);
float mean_u8_neon(const uint8_t* data, size_t n);
float mean_u16_neon(const uint16_t* data, size_t n);
float variance_u8_neon(const uint8_t* data, size_t n);
float variance_u16_neon(const uint16_t* data, size_t n);
uint8_t median_u8_neon(uint8_t* data, size_t n);
uint16_t median_u16_neon(uint16_t* data, size_t n);
void min_max_u8_neon(const uint8_t* data, size_t n, uint8_t &min_val, uint8_t &max_val);
void min_max_u16_neon(const uint16_t* data, size_t n, uint16_t &min_val, uint16_t &max_val);
void histogram_u8_neon(const uint8_t* data, size_t n, uint32_t* histogram, size_t bins);
void threshold_u8_neon(uint8_t* data, size_t n, uint8_t threshold, uint8_t low_value, uint8_t high_value);
void threshold_u16_neon(uint16_t* data, size_t n, uint16_t threshold, uint16_t low_value, uint16_t high_value);
void gaussian_blur_u8_neon(uint8_t* dst, const uint8_t* src, int width, int height, int channels, float sigma);
void median_filter_u8_neon(uint8_t* dst, const uint8_t* src, int width, int height, int channels, int kernel_size);
#endif

// Runtime CPU feature detection
enum class SimdLevel
{
    None,
    SSE41,
    AVX,
    AVX2,
    NEON
};

// Get the highest SIMD level supported by the current CPU
SimdLevel get_simd_level();

// Enable or disable SIMD acceleration
void enable_simd(bool enable);

// Check if SIMD acceleration is enabled
bool is_simd_enabled();

} // namespace simd

#endif // SIMD_OPS_H
