/// SLM shared memory producer (POSIX, Linux/macOS only).
///
/// Creates a named SHM region and semaphore that the OBS plugin can attach to
/// when running on the same machine.
///
/// Region layout (version 2):
///   [SlmShmHdr @ offset 0, padded to 128 bytes]
///   [SLM_SHM_SLOTS x (SlmShmFrameHdr + max_frame_bytes)]
///
/// The producer writes to slots[write_seq % SLOTS], atomically increments
/// write_seq, then sem_post(). The consumer wakes on the semaphore, drains
/// all new slots, and pushes them to OBS.
///
/// Heartbeat: updated on every frame write and from idle-loop ticks every
/// ~500 ms. OBS treats a heartbeat older than 5 s as a dead producer.
///
/// Cleanup: atexit handler unlinks all SHM regions and semaphores created
/// in this process. On startup a stale region (heartbeat > 5 s old) is
/// unlinked and recreated so a crashed prior run never blocks a new session.

#[cfg(unix)]
mod inner {
    use libc::{
        c_void,
        sem_t, sem_open, sem_close, sem_post, sem_unlink,
        shm_open, shm_unlink, ftruncate, mmap, munmap,
        O_CREAT, O_RDWR,
        PROT_READ, PROT_WRITE, MAP_SHARED,
        S_IRUSR, S_IWUSR,
    };
    use std::ffi::CString;
    use std::sync::{Mutex, atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}};
    use std::ptr;

    const SLM_SHM_MAGIC:    u32   = 0x534C4D02;
    const SLM_SHM_VERSION:  u32   = 2;
    const SLM_SHM_SLOTS:    usize = 16;
    const SLM_SHM_HDR_SIZE: usize = 128;
    const STALE_NS:         u64   = 5_000_000_000;

    /// Matches slm_shm_hdr_t in slm_shm.h -- must stay in sync.
    #[repr(C)]
    struct ShmHdr {
        magic:             u32,
        version:           u32,
        width:             u16,
        height:            u16,
        fps_num:           u32,
        fps_den:           u32,
        pixel_fmt:         u8,
        source_flags:      u8,
        audio_sample_rate: u16,
        audio_channels:    u8,
        caps_valid:        u8,
        disconnect:        u8,
        _pad:              u8,
        max_frame_bytes:   u32,
        write_seq:         u32,
        _pad2:             u32,
        heartbeat_ns:      u64,
        sender_name:       [u8; 64],
        _reserved:         [u8; 16],
    }

    const _: () = assert!(std::mem::size_of::<ShmHdr>() == SLM_SHM_HDR_SIZE);

    /// Matches slm_shm_frame_hdr_t in slm_shm.h -- must stay in sync.
    #[repr(C)]
    struct FrameHdr {
        timestamp_ns: u64,
        seq:          u32,
        size:         u32,
        is_video:     u8,
        _pad:         [u8; 3],
        _pad2:        [u8; 4],
        sender_ts_ns: u64,
    }

    const _: () = assert!(std::mem::size_of::<FrameHdr>() == 32);

    // -- Global atexit cleanup registry ----------------------------------------

    static ATEXIT_REGISTERED: AtomicBool = AtomicBool::new(false);
    static CLEANUP_LIST: Mutex<Vec<(String, String)>> = Mutex::new(Vec::new());

    extern "C" fn atexit_cleanup() {
        if let Ok(list) = CLEANUP_LIST.lock() {
            for (shm, sem) in list.iter() {
                if let Ok(cs) = CString::new(shm.as_str()) {
                    unsafe { shm_unlink(cs.as_ptr()); }
                }
                if let Ok(cs) = CString::new(sem.as_str()) {
                    unsafe { sem_unlink(cs.as_ptr()); }
                }
            }
        }
    }

    fn register_cleanup(shm_base: &str, sem_str: &str) {
        if !ATEXIT_REGISTERED.swap(true, Ordering::SeqCst) {
            unsafe { libc::atexit(atexit_cleanup); }
        }
        if let Ok(mut list) = CLEANUP_LIST.lock() {
            if !list.iter().any(|(s, _)| s == shm_base) {
                list.push((shm_base.to_owned(), sem_str.to_owned()));
            }
        }
    }

    fn region_name(name: &str) -> String {
        format!("/slm-{}", super::shm_token(name))
    }

    /// UNIX-epoch nanoseconds -- comparable with CLOCK_REALTIME on the OBS side.
    fn unix_ns() -> u64 {
        use std::time::{SystemTime, UNIX_EPOCH};
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_nanos() as u64)
            .unwrap_or(1)
    }

    // -- SlmShm ----------------------------------------------------------------

    pub(crate) struct SlmShm {
        map:             *mut u8,
        map_size:        usize,
        max_frame_bytes: usize,
        sem:             *mut sem_t,
        shm_name:        CString,
        sem_name:        CString,
        write_seq:       u32,
        frame_seq:       u32,
    }

    // SAFETY: SlmShm is only accessed under the Mutex<Option<SlmShm>> in SlmInner.
    unsafe impl Send for SlmShm {}

    impl SlmShm {
        pub(crate) fn create(
            sender_name: &str,
            width:        u32,
            height:       u32,
            fps_num:      u32,
            fps_den:      u32,
            pixel_fmt:    u8,
            source_flags: u8,
            audio_sr:     u32,
            audio_ch:     u8,
        ) -> Option<Self> {
            let shm_base = region_name(sender_name);
            let sem_str  = format!("{shm_base}-sem");

            let shm_name = CString::new(shm_base.as_str()).ok()?;
            let sem_name = CString::new(sem_str.as_str()).ok()?;

            let max_frame_bytes = (width as usize) * (height as usize) * 3 / 2;
            let slot_stride     = std::mem::size_of::<FrameHdr>() + max_frame_bytes;
            let map_size        = SLM_SHM_HDR_SIZE + SLM_SHM_SLOTS * slot_stride;

            register_cleanup(&shm_base, &sem_str);

            unsafe {
                // Stale-SHM check: unlink any region left by a previously crashed process.
                let peek_fd = shm_open(shm_name.as_ptr(), O_RDWR, 0);
                if peek_fd >= 0 {
                    let mut tmp = std::mem::zeroed::<ShmHdr>();
                    let nr = libc::read(peek_fd, &mut tmp as *mut _ as *mut libc::c_void,
                                        std::mem::size_of::<ShmHdr>() as libc::size_t);
                    libc::close(peek_fd);

                    let stale = if nr < std::mem::size_of::<ShmHdr>() as libc::ssize_t {
                        true
                    } else if tmp.magic != SLM_SHM_MAGIC || tmp.version != SLM_SHM_VERSION {
                        true
                    } else if tmp.heartbeat_ns != 0 {
                        unix_ns().saturating_sub(tmp.heartbeat_ns) > STALE_NS
                    } else {
                        false
                    };

                    if stale {
                        eprintln!("[SLM-SHM] removing stale region '{shm_base}'");
                        shm_unlink(shm_name.as_ptr());
                        sem_unlink(sem_name.as_ptr());
                    }
                }

                let fd = shm_open(shm_name.as_ptr(), O_CREAT | O_RDWR,
                                  (S_IRUSR | S_IWUSR) as libc::mode_t);
                if fd < 0 {
                    eprintln!("[SLM-SHM] shm_open failed: {}", *libc::__errno_location());
                    return None;
                }

                if ftruncate(fd, map_size as libc::off_t) < 0 {
                    eprintln!("[SLM-SHM] ftruncate failed");
                    libc::close(fd);
                    return None;
                }

                let map = mmap(ptr::null_mut(), map_size,
                               PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
                libc::close(fd);
                if map == libc::MAP_FAILED {
                    eprintln!("[SLM-SHM] mmap failed");
                    return None;
                }

                let sem = sem_open(sem_name.as_ptr(), O_CREAT,
                                   (S_IRUSR | S_IWUSR) as libc::mode_t, 0u32);
                if sem == libc::SEM_FAILED as *mut sem_t {
                    eprintln!("[SLM-SHM] sem_open failed");
                    munmap(map, map_size);
                    return None;
                }

                let hdr = &mut *(map as *mut ShmHdr);
                hdr.magic             = SLM_SHM_MAGIC;
                hdr.version           = SLM_SHM_VERSION;
                hdr.width             = width as u16;
                hdr.height            = height as u16;
                hdr.fps_num           = fps_num;
                hdr.fps_den           = fps_den;
                hdr.pixel_fmt         = pixel_fmt;
                hdr.source_flags      = source_flags;
                hdr.audio_sample_rate = audio_sr as u16;
                hdr.audio_channels    = audio_ch;
                hdr.max_frame_bytes   = max_frame_bytes as u32;
                hdr.write_seq         = 0;
                hdr._pad2             = 0;
                hdr.disconnect        = 0;

                hdr.sender_name = [0u8; 64];
                let nb = sender_name.as_bytes();
                let n  = nb.len().min(63);
                hdr.sender_name[..n].copy_from_slice(&nb[..n]);

                hdr.heartbeat_ns = unix_ns();

                std::sync::atomic::fence(Ordering::Release);
                hdr.caps_valid = 1;

                eprintln!("[SLM-SHM] opened '{shm_base}', {map_size} bytes, \
                           {SLM_SHM_SLOTS} slots x {max_frame_bytes} bytes");

                Some(Self {
                    map: map as *mut u8,
                    map_size,
                    max_frame_bytes,
                    sem,
                    shm_name,
                    sem_name,
                    write_seq: 0,
                    frame_seq: 0,
                })
            }
        }

        fn slot_ptr(&self, seq: u32) -> *mut u8 {
            let slot_idx = (seq as usize) % SLM_SHM_SLOTS;
            let stride   = std::mem::size_of::<FrameHdr>() + self.max_frame_bytes;
            unsafe { self.map.add(SLM_SHM_HDR_SIZE + slot_idx * stride) }
        }

        fn hdr_ptr(&self) -> *mut ShmHdr { self.map as *mut ShmHdr }

        pub(crate) fn update_heartbeat(&mut self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                let atomic_hb = &*((&hdr.heartbeat_ns) as *const u64 as *const AtomicU64);
                atomic_hb.store(unix_ns(), Ordering::Relaxed);
            }
        }

        fn write_slot(&mut self, data: &[u8], timestamp_ns: u64, is_video: bool) {
            if data.len() > self.max_frame_bytes {
                eprintln!("[SLM-SHM] frame too large: {} > {}", data.len(), self.max_frame_bytes);
                return;
            }
            unsafe {
                let slot = self.slot_ptr(self.write_seq);
                let fhdr = slot as *mut FrameHdr;
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).timestamp_ns), timestamp_ns);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).seq),          self.frame_seq);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).size),         data.len() as u32);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).is_video),     u8::from(is_video));
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr)._pad),         [0u8; 3]);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr)._pad2),        [0u8; 4]);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).sender_ts_ns), unix_ns());

                let payload = slot.add(std::mem::size_of::<FrameHdr>());
                std::ptr::copy_nonoverlapping(data.as_ptr(), payload, data.len());

                let atomic_hb = &*((&(*self.hdr_ptr()).heartbeat_ns) as *const u64
                                    as *const AtomicU64);
                atomic_hb.store(unix_ns(), Ordering::Relaxed);

                let atomic_seq = &*((&(*self.hdr_ptr()).write_seq) as *const u32
                                    as *const AtomicU32);
                self.write_seq = self.write_seq.wrapping_add(1);
                atomic_seq.store(self.write_seq, Ordering::Release);
                sem_post(self.sem);
            }
            self.frame_seq = self.frame_seq.wrapping_add(1);
        }

        pub(crate) fn write_video(&mut self, data: &[u8], timestamp_ns: u64) {
            self.write_slot(data, timestamp_ns, true);
        }

        pub(crate) fn write_audio(&mut self, samples: &[f32], timestamp_ns: u64) {
            let bytes = unsafe {
                std::slice::from_raw_parts(samples.as_ptr() as *const u8, samples.len() * 4)
            };
            self.write_slot(bytes, timestamp_ns, false);
        }

        pub(crate) fn send_disconnect(&self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                std::sync::atomic::fence(Ordering::Release);
                hdr.disconnect = 1;
                sem_post(self.sem);
            }
        }

        pub(crate) fn clear_disconnect(&self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                hdr.disconnect = 0;
                std::sync::atomic::fence(Ordering::Release);
            }
        }
    }

    impl Drop for SlmShm {
        fn drop(&mut self) {
            unsafe {
                // Signal any attached consumer before unmapping so it detects
                // shutdown immediately rather than waiting for heartbeat stale.
                let hdr = &mut *self.hdr_ptr();
                hdr.disconnect   = 1;
                hdr.heartbeat_ns = 1; // appears stale immediately (epoch = 1970)
                std::sync::atomic::fence(Ordering::Release);
                sem_post(self.sem); // wake consumer out of sem_timedwait

                sem_close(self.sem);
                sem_unlink(self.sem_name.as_ptr());
                munmap(self.map as *mut c_void, self.map_size);
                shm_unlink(self.shm_name.as_ptr());
            }
        }
    }
}

#[cfg(unix)]
pub(crate) use inner::SlmShm;

#[cfg(windows)]
pub(crate) use win_inner::SlmShm;

/// SHM region name token: FNV-1a (64-bit) of the sender name, as 16 lowercase
/// hex chars. The fixed length keeps the POSIX `shm_open` name within macOS's
/// 31-char limit (`/slm-<token>-sem` = 25 chars). It MUST stay byte-for-byte
/// identical to `slm_shm_token()` in slm-wire-for-obs/src/slm_shm.h -- the
/// producer and the OBS consumer derive the name independently and only
/// rendezvous if they agree. Hash the UTF-8 *bytes* (not chars) so non-ASCII
/// sender names match the C side's `(unsigned char)` iteration.
fn shm_token(name: &str) -> String {
    let mut h: u64 = 0xcbf2_9ce4_8422_2325;
    for b in name.bytes() {
        h ^= b as u64;
        h = h.wrapping_mul(0x100000001b3);
    }
    format!("{h:016x}")
}

#[cfg(test)]
mod tests {
    /// Cross-language invariant: this value MUST match `slm_shm_token()` in
    /// slm-wire-for-obs/src/slm_shm.h. If it ever changes, change the C side
    /// in lockstep or local SHM transport silently falls back to UDP.
    #[test]
    fn shm_token_vector() {
        assert_eq!(super::shm_token("SLAY Media"), "8189292c8643bf1c");
    }
}

/// Windows Named File Mapping producer - page-file-backed equivalent of the
/// POSIX shm_open path. Layout is byte-for-byte identical; only the OS
/// primitives differ.
///
/// Name mapping (must match slm_shm_win.c on the consumer side):
///   POSIX /slm-<token>      ->  Win32  Local\slm-<token>
///   POSIX /slm-<token>-sem  ->  Win32  Local\slm-<token>-sem
/// where <token> is shm_token(sender_name) -- see its doc above.
///
/// Cleanup: Windows automatically destroys page-file-backed mappings and named
/// semaphores when all handles are closed, so no atexit unlink is needed.
#[cfg(windows)]
mod win_inner {
    use std::ptr;
    use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
    use windows_sys::Win32::Foundation::{
        CloseHandle, GetLastError, HANDLE, INVALID_HANDLE_VALUE,
        ERROR_ALREADY_EXISTS,
    };
    use windows_sys::Win32::System::Memory::{
        CreateFileMappingW, MapViewOfFile, UnmapViewOfFile,
        FILE_MAP_ALL_ACCESS, PAGE_READWRITE, MEMORY_MAPPED_VIEW_ADDRESS,
    };
    use windows_sys::Win32::System::Threading::{
        CreateSemaphoreW, ReleaseSemaphore,
    };

    fn to_wstring(s: &str) -> Vec<u16> {
        s.encode_utf16().chain(std::iter::once(0)).collect()
    }

    const SLM_SHM_MAGIC:    u32   = 0x534C4D02;
    const SLM_SHM_VERSION:  u32   = 2;
    const SLM_SHM_SLOTS:    usize = 16;
    const SLM_SHM_HDR_SIZE: usize = 128;
    const STALE_NS:         u64   = 5_000_000_000;

    /// Matches slm_shm_hdr_t in slm_shm.h -- must stay in sync.
    #[repr(C)]
    struct ShmHdr {
        magic:             u32,
        version:           u32,
        width:             u16,
        height:            u16,
        fps_num:           u32,
        fps_den:           u32,
        pixel_fmt:         u8,
        source_flags:      u8,
        audio_sample_rate: u16,
        audio_channels:    u8,
        caps_valid:        u8,
        disconnect:        u8,
        _pad:              u8,
        max_frame_bytes:   u32,
        write_seq:         u32,
        _pad2:             u32,
        heartbeat_ns:      u64,
        sender_name:       [u8; 64],
        _reserved:         [u8; 16],
    }

    const _: () = assert!(std::mem::size_of::<ShmHdr>() == SLM_SHM_HDR_SIZE);

    /// Matches slm_shm_frame_hdr_t in slm_shm.h -- must stay in sync.
    #[repr(C)]
    struct FrameHdr {
        timestamp_ns: u64,
        seq:          u32,
        size:         u32,
        is_video:     u8,
        _pad:         [u8; 3],
        _pad2:        [u8; 4],
        sender_ts_ns: u64,
    }

    const _: () = assert!(std::mem::size_of::<FrameHdr>() == 32);

    fn region_name(name: &str) -> String {
        format!("Local\\slm-{}", super::shm_token(name))
    }

    fn unix_ns() -> u64 {
        use std::time::{SystemTime, UNIX_EPOCH};
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_nanos() as u64)
            .unwrap_or(1)
    }

    pub(crate) struct SlmShm {
        map:             *mut u8,
        map_size:        usize,
        max_frame_bytes: usize,
        h_map:           HANDLE,
        h_sem:           HANDLE,
        write_seq:       u32,
        frame_seq:       u32,
    }

    // SAFETY: SlmShm is only accessed under the Mutex<Option<SlmShm>> in SlmInner.
    unsafe impl Send for SlmShm {}

    impl SlmShm {
        pub(crate) fn create(
            sender_name: &str,
            width:        u32,
            height:       u32,
            fps_num:      u32,
            fps_den:      u32,
            pixel_fmt:    u8,
            source_flags: u8,
            audio_sr:     u32,
            audio_ch:     u8,
        ) -> Option<Self> {
            let shm_base = region_name(sender_name);
            let sem_str  = format!("{shm_base}-sem");

            let shm_name = to_wstring(&shm_base);
            let sem_name = to_wstring(&sem_str);

            let max_frame_bytes = (width as usize) * (height as usize) * 3 / 2;
            let slot_stride     = std::mem::size_of::<FrameHdr>() + max_frame_bytes;
            let map_size        = SLM_SHM_HDR_SIZE + SLM_SHM_SLOTS * slot_stride;

            unsafe {
                let h_map = CreateFileMappingW(
                    INVALID_HANDLE_VALUE,
                    ptr::null(),
                    PAGE_READWRITE,
                    (map_size >> 32) as u32,
                    (map_size & 0xFFFF_FFFF) as u32,
                    shm_name.as_ptr(),
                );
                if h_map.is_null() {
                    eprintln!("[SLM-SHM] CreateFileMapping '{}' failed: {}", shm_base, GetLastError());
                    return None;
                }

                let already_exists = GetLastError() == ERROR_ALREADY_EXISTS;

                let map_view = MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
                if map_view.Value.is_null() {
                    eprintln!("[SLM-SHM] MapViewOfFile failed: {}", GetLastError());
                    CloseHandle(h_map);
                    return None;
                }
                let map = map_view.Value as *mut u8;

                if already_exists {
                    let tmp = &*(map as *const ShmHdr);
                    let live = tmp.magic   == SLM_SHM_MAGIC
                        && tmp.version     == SLM_SHM_VERSION
                        && tmp.heartbeat_ns != 0
                        && unix_ns().saturating_sub(tmp.heartbeat_ns) <= STALE_NS;
                    if live {
                        eprintln!("[SLM-SHM] '{}' already has a live producer", shm_base);
                        UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { Value: map as *mut _ });
                        CloseHandle(h_map);
                        return None;
                    }
                    eprintln!("[SLM-SHM] '{}' stale mapping found, reinitializing", shm_base);
                }

                let h_sem = CreateSemaphoreW(ptr::null(), 0, 0x7FFF_FFFF, sem_name.as_ptr());
                if h_sem.is_null() {
                    eprintln!("[SLM-SHM] CreateSemaphore failed: {}", GetLastError());
                    UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { Value: map as *mut _ });
                    CloseHandle(h_map);
                    return None;
                }

                std::ptr::write_bytes(map, 0, SLM_SHM_HDR_SIZE);
                let hdr = &mut *(map as *mut ShmHdr);
                hdr.magic             = SLM_SHM_MAGIC;
                hdr.version           = SLM_SHM_VERSION;
                hdr.width             = width as u16;
                hdr.height            = height as u16;
                hdr.fps_num           = fps_num;
                hdr.fps_den           = fps_den;
                hdr.pixel_fmt         = pixel_fmt;
                hdr.source_flags      = source_flags;
                hdr.audio_sample_rate = audio_sr as u16;
                hdr.audio_channels    = audio_ch;
                hdr.max_frame_bytes   = max_frame_bytes as u32;
                hdr.write_seq         = 0;
                hdr._pad2             = 0;
                hdr.disconnect        = 0;

                hdr.sender_name = [0u8; 64];
                let nb = sender_name.as_bytes();
                let n  = nb.len().min(63);
                hdr.sender_name[..n].copy_from_slice(&nb[..n]);

                hdr.heartbeat_ns = unix_ns();

                std::sync::atomic::fence(Ordering::Release);
                hdr.caps_valid = 1;

                eprintln!("[SLM-SHM] opened '{}', {map_size} bytes, \
                           {SLM_SHM_SLOTS} slots x {max_frame_bytes} bytes",
                          shm_base);

                Some(Self {
                    map,
                    map_size,
                    max_frame_bytes,
                    h_map,
                    h_sem,
                    write_seq: 0,
                    frame_seq: 0,
                })
            }
        }

        fn slot_ptr(&self, seq: u32) -> *mut u8 {
            let slot_idx = (seq as usize) % SLM_SHM_SLOTS;
            let stride   = std::mem::size_of::<FrameHdr>() + self.max_frame_bytes;
            unsafe { self.map.add(SLM_SHM_HDR_SIZE + slot_idx * stride) }
        }

        fn hdr_ptr(&self) -> *mut ShmHdr { self.map as *mut ShmHdr }

        pub(crate) fn update_heartbeat(&mut self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                let atomic_hb = &*((&hdr.heartbeat_ns) as *const u64 as *const AtomicU64);
                atomic_hb.store(unix_ns(), Ordering::Relaxed);
            }
        }

        fn write_slot(&mut self, data: &[u8], timestamp_ns: u64, is_video: bool) {
            if data.len() > self.max_frame_bytes {
                eprintln!("[SLM-SHM] frame too large: {} > {}", data.len(), self.max_frame_bytes);
                return;
            }
            unsafe {
                let slot = self.slot_ptr(self.write_seq);
                let fhdr = slot as *mut FrameHdr;
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).timestamp_ns), timestamp_ns);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).seq),          self.frame_seq);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).size),         data.len() as u32);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).is_video),     u8::from(is_video));
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr)._pad),         [0u8; 3]);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr)._pad2),        [0u8; 4]);
                std::ptr::write(std::ptr::addr_of_mut!((*fhdr).sender_ts_ns), unix_ns());

                let payload = slot.add(std::mem::size_of::<FrameHdr>());
                std::ptr::copy_nonoverlapping(data.as_ptr(), payload, data.len());

                let atomic_hb = &*((&(*self.hdr_ptr()).heartbeat_ns) as *const u64
                                    as *const AtomicU64);
                atomic_hb.store(unix_ns(), Ordering::Relaxed);

                let atomic_seq = &*((&(*self.hdr_ptr()).write_seq) as *const u32
                                    as *const AtomicU32);
                self.write_seq = self.write_seq.wrapping_add(1);
                atomic_seq.store(self.write_seq, Ordering::Release);
                ReleaseSemaphore(self.h_sem, 1, ptr::null_mut());
            }
            self.frame_seq = self.frame_seq.wrapping_add(1);
        }

        pub(crate) fn write_video(&mut self, data: &[u8], timestamp_ns: u64) {
            self.write_slot(data, timestamp_ns, true);
        }

        pub(crate) fn write_audio(&mut self, samples: &[f32], timestamp_ns: u64) {
            let bytes = unsafe {
                std::slice::from_raw_parts(samples.as_ptr() as *const u8, samples.len() * 4)
            };
            self.write_slot(bytes, timestamp_ns, false);
        }

        pub(crate) fn send_disconnect(&self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                std::sync::atomic::fence(Ordering::Release);
                hdr.disconnect = 1;
                ReleaseSemaphore(self.h_sem, 1, ptr::null_mut());
            }
        }

        pub(crate) fn clear_disconnect(&self) {
            unsafe {
                let hdr = &mut *self.hdr_ptr();
                hdr.disconnect = 0;
                std::sync::atomic::fence(Ordering::Release);
            }
        }
    }

    impl Drop for SlmShm {
        fn drop(&mut self) {
            unsafe {
                // Signal any attached consumer before unmapping.
                let hdr = &mut *self.hdr_ptr();
                hdr.disconnect   = 1;
                hdr.heartbeat_ns = 1;
                std::sync::atomic::fence(Ordering::Release);
                ReleaseSemaphore(self.h_sem, 1, ptr::null_mut());

                UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { Value: self.map as *mut _ });
                CloseHandle(self.h_map);
                CloseHandle(self.h_sem);
            }
        }
    }
}

#[cfg(not(any(unix, windows)))]
pub(crate) struct SlmShm;

#[cfg(not(any(unix, windows)))]
impl SlmShm {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn create(_: &str, _: u32, _: u32, _: u32, _: u32,
                         _: u8, _: u8, _: u32, _: u8) -> Option<Self> { None }
    pub(crate) fn write_video(&mut self, _: &[u8], _: u64) {}
    pub(crate) fn write_audio(&mut self, _: &[f32], _: u64) {}
    pub(crate) fn send_disconnect(&self) {}
    pub(crate) fn clear_disconnect(&self) {}
    pub(crate) fn update_heartbeat(&mut self) {}
}
