use crate::ffi;
use crate::error::Error;
use std::ffi::CString;

pub fn base58_encode(src: &[u8]) -> String {
    let destlen = unsafe { ffi::nwep_base58_encode_len(src.len()) };
    let mut buf = vec![0u8; destlen];
    let n = unsafe {
        ffi::nwep_base58_encode(buf.as_mut_ptr().cast(), destlen, src.as_ptr(), src.len())
    };
    String::from_utf8_lossy(&buf[..n]).into_owned()
}

pub fn base58_decode(src: &str) -> Result<Vec<u8>, Error> {
    let destlen = unsafe { ffi::nwep_base58_decode_len(src.len()) };
    let mut buf = vec![0u8; destlen];
    let c = CString::new(src).map_err(|_| Error::from_code(crate::error::ERR_INTERNAL_INVALID_ARG))?;
    let n = unsafe { ffi::nwep_base58_decode(buf.as_mut_ptr(), destlen, c.as_ptr()) };
    if n == 0 {
        Err(Error::from_code(crate::error::ERR_IDENTITY_INVALID_ADDR))
    } else {
        buf.truncate(n);
        Ok(buf)
    }
}

pub fn base64_encode(src: &[u8]) -> String {
    let destlen = unsafe { ffi::nwep_base64_encode_len(src.len()) };
    let mut buf = vec![0u8; destlen];
    let n = unsafe {
        ffi::nwep_base64_encode(buf.as_mut_ptr().cast(), destlen, src.as_ptr(), src.len())
    };
    String::from_utf8_lossy(&buf[..n]).into_owned()
}

pub fn base64_decode(src: &str) -> Result<Vec<u8>, Error> {
    let destlen = unsafe { ffi::nwep_base64_decode_len(src.len()) };
    let mut buf = vec![0u8; destlen];
    let c = CString::new(src).map_err(|_| Error::from_code(crate::error::ERR_INTERNAL_INVALID_ARG))?;
    let n = unsafe { ffi::nwep_base64_decode(buf.as_mut_ptr(), destlen, c.as_ptr()) };
    if n == 0 {
        Err(Error::from_code(crate::error::ERR_INTERNAL_INVALID_ARG))
    } else {
        buf.truncate(n);
        Ok(buf)
    }
}

pub fn base64_decode_n(src: &[u8]) -> Result<Vec<u8>, Error> {
    let destlen = unsafe { ffi::nwep_base64_decode_len(src.len()) };
    let mut buf = vec![0u8; destlen];
    let n = unsafe {
        ffi::nwep_base64_decode_n(buf.as_mut_ptr(), destlen, src.as_ptr().cast(), src.len())
    };
    if n == 0 {
        Err(Error::from_code(crate::error::ERR_INTERNAL_INVALID_ARG))
    } else {
        buf.truncate(n);
        Ok(buf)
    }
}
