use std::{env, os::unix::ffi::OsStrExt};

use crate::BString;

fn w_u32(out: &mut Vec<u8>, x: u32) {
    for b in x.to_le_bytes() {
        out.push(b);
    }
}

fn w_u8s(out: &mut Vec<u8>, b: &[u8]) {
    w_u32(out, b.len() as u32);
    out.extend_from_slice(b);
}

fn w_u8ss(out: &mut Vec<u8>, b: &[&[u8]]) {
    w_u32(out, b.len() as u32);
    for c in b {
        w_u8s(out, c);
    }
}

pub enum Error {
    Overrun,
}

pub type Result<T> = std::result::Result<T, Error>;

fn r_u32(buf: &mut &[u8]) -> Result<u32> {
    if buf.len() < 4 {
        Err(Error::Overrun)?;
    }
    let x = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
    *buf = &buf[4..];
    Ok(x)
}

fn r_u8s(buf: &mut &[u8]) -> Result<Vec<u8>> {
    let len = r_u32(buf)? as usize;
    if len > buf.len() {
        Err(Error::Overrun)?;
    }
    let x = buf[..len].to_vec();
    *buf = &buf[len..];
    Ok(x)
}

fn r_u8ss(buf: &mut &[u8]) -> Result<Vec<Vec<u8>>> {
    let len = r_u32(buf)? as usize;
    let mut out = Vec::with_capacity(len);
    for _ in 0..len {
        out.push(r_u8s(buf)?);
    }
    Ok(out)
}

pub fn serialize_cli_args() -> Vec<u8> {
    let args: Vec<Vec<u8>> = env::args_os()
        .map(|a| a.as_os_str().as_bytes().to_vec())
        .collect();
    let args: Vec<&[u8]> = args.iter().map(|v| v.as_ref()).collect();
    let mut out = Vec::new();
    w_u8ss(&mut out, &args);
    out
}

pub fn deserialize_cli_args(mut buf: &[u8]) -> Result<Vec<BString>> {
    r_u8ss(&mut buf)
}
