use crate::error::XDPError;
use crate::result::XDPResult;
use libc::if_nametoindex;
use std::{
    convert::TryInto,
    ffi::{CStr, CString},
    os::raw::c_char,
};

pub(crate) fn str_to_cstring(s: &str) -> XDPResult<CString> {
    match CString::new(s) {
        Ok(c) => Ok(c),
        Err(e) => {
            fail!("Error creating C string: {:?}", e);
        }
    }
}

pub(crate) fn lookup_interface_by_name(name: &str) -> XDPResult<i32> {
    let index = unsafe { if_nametoindex(str_to_cstring(name)?.as_ptr()) };
    if index == 0 {
        fail!("Error finding interface index for {}", name);
    }

    match (index as i32).try_into() {
        Ok(i) => Ok(i),
        Err(e) => fail!("Error converting interface index to 'i32': {}", e),
    }
}

pub(crate) fn cstring_to_str(char_ptr: *const c_char) -> String {
    let cs = unsafe { CStr::from_ptr(char_ptr) };
    match cs.to_str() {
        Ok(s) => String::from(s),
        Err(_) => String::new(),
    }
}

// Returns the number of possible cpus
pub(crate) fn num_cpus() -> XDPResult<usize> {
    let contents = match std::fs::read_to_string("/sys/devices/system/cpu/possible") {
        Ok(c) => c,
        Err(e) => fail!("Error getting the number of cpus: {:?}", e),
    };

    if contents.trim() == "0" {
        return Ok(1);
    }

    let parts: Vec<&str> = contents.trim().split("-").collect();
    if parts.len() != 2 {
        fail!("Unable to determine number of cpus");
    }

    let lower = parts[0].parse::<u32>().unwrap_or(0);
    let upper = parts[1].parse::<u32>().unwrap_or(0);

    if upper == 0 {
        fail!("Unable to determine number of cpus");
    }

    Ok((upper - lower) as usize + 1 as usize)
}
