use crate::ffi::{c_char, CStr, OsString};
use crate::fmt;
use crate::os::unix::ffi::OsStringExt;
use crate::ptr;
use crate::sync::atomic::{
    AtomicIsize, AtomicPtr,
    Ordering::{Acquire, Relaxed, Release},
};
use crate::vec;

static ARGC: AtomicIsize = AtomicIsize::new(0);
static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());

/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) {
    ARGC.store(argc, Relaxed);
    // Use release ordering here to broadcast writes by the OS.
    ARGV.store(argv as *mut *const u8, Release);
}

/// Returns the command line arguments
pub fn args() -> Args {
    // Synchronize with the store above.
    let argv = ARGV.load(Acquire);
    // If argv has not been initialized yet, do not return any arguments.
    let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) };
    let args: Vec<OsString> = (0..argc)
        .map(|i| unsafe {
            let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char);
            OsStringExt::from_vec(cstr.to_bytes().to_vec())
        })
        .collect();

    Args { iter: args.into_iter() }
}

pub struct Args {
    iter: vec::IntoIter<OsString>,
}

impl fmt::Debug for Args {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.iter.as_slice().fmt(f)
    }
}

impl !Send for Args {}
impl !Sync for Args {}

impl Iterator for Args {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        self.iter.next()
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl ExactSizeIterator for Args {
    fn len(&self) -> usize {
        self.iter.len()
    }
}

impl DoubleEndedIterator for Args {
    fn next_back(&mut self) -> Option<OsString> {
        self.iter.next_back()
    }
}
