/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Namaste

Copyright (C) 2019, 2021-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2021-2023".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

#![cfg(any(target_os = "linux", target_os = "l4re", target_os = "android"))]

use {
    core::{
        sync::atomic::{AtomicUsize, Ordering},
        time::Duration,
    },
    std::{
        sync::Arc,
        thread,
    },
    namaste::{Result, RwNamaste},
};

const READ_ID: &[u8] = &[
    0x2e, 0xdd, 0xd0, 0x21, 0x3e, 0x90, 0xb6, 0xe7, 0xad, 0x3b, 0x15, 0x5b, 0x74, 0x07, 0xf0, 0x51, 0xa3, 0x98, 0xf7, 0x42, 0xd2, 0xae, 0x31,
    0x11, 0x3e, 0x43, 0x26, 0x52, 0xb6, 0x84, 0x8f, 0x00, 0xb7, 0x88, 0x80, 0xcd, 0xab, 0x55, 0xe1, 0x74, 0x42, 0xc5, 0x5f, 0x05, 0x08, 0x6b,
    0x34, 0x70, 0xd4, 0x20, 0xb6, 0x73, 0x65, 0x3f, 0x5a, 0xe3, 0xff, 0x6a, 0x98, 0x06, 0x6f, 0xc7, 0x09, 0x14,
];

const WRITE_ID: &[u8] = &[
    0x56, 0xcb, 0x08, 0xbb, 0x7b, 0x46, 0x37, 0x14, 0xe6, 0xf4, 0x5e, 0x3b, 0x95, 0xd3, 0xd6, 0xc7, 0x55, 0x6e, 0xee, 0xdb, 0x65, 0x93, 0x8e,
    0x8b, 0x6f, 0xe8, 0xdf, 0xe0, 0x4b, 0xfd, 0x9d, 0x4e, 0xaa, 0xd5, 0x42, 0x07, 0xce, 0x98, 0x78, 0x97, 0x1e, 0xec, 0x11, 0xd6, 0xad, 0x96,
    0xb6, 0x60, 0xaa, 0x2f, 0x69, 0x9e, 0x44, 0xe1, 0x8c, 0x57, 0xbc, 0x1f, 0x31, 0x60, 0x1f, 0x48, 0x73, 0x64,
];

const ORDERING: Ordering = Ordering::Relaxed;

#[test]
fn tests() -> Result<()> {
    const THREAD_COUNT: usize = 9;

    let mut rw_namaste = RwNamaste::make(READ_ID, WRITE_ID, Duration::from_secs(1))?;
    rw_namaste.read()?;

    let read_count = Arc::new(AtomicUsize::new(1));

    let threads = (usize::MIN..THREAD_COUNT).fold(Vec::with_capacity(THREAD_COUNT), |mut result, _| {
        let read_count = read_count.clone();
        let mut rw_namaste = rw_namaste.clone();
        result.push(thread::spawn(move || {
            rw_namaste.read()?;
            read_count.fetch_add(1, ORDERING);
            Result::Ok(())
        }));
        result
    });

    assert!(rw_namaste.write().is_err());

    threads.into_iter().try_fold((), |(), next| next.join().unwrap())?;

    assert!(rw_namaste.write().is_err());

    rw_namaste.drop_read();

    thread::sleep(Duration::from_millis(10));
    (0..9).try_fold((), |(), _| rw_namaste.write())?;

    rw_namaste.drop_write();
    thread::sleep(Duration::from_millis(10));
    rw_namaste.write()?;

    assert_eq!(read_count.load(ORDERING), THREAD_COUNT.checked_add(1).unwrap());

    Ok(())
}
