use crate::arq::Result;
use crate::errors::ArqError;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Read;

impl<T: Read + ?Sized> ReadExt for T {}

pub trait ReadExt: Read {
    // 00 = false
    // 01 = true
    // All other values are considered an error
    fn read_bool(&mut self) -> Result<bool> {
        let data = self.read_u8()?;
        match data {
            0 => Ok(false),
            1 => Ok(true),
            v => Err(ArqError::InvalidBoolean(v)),
        }
    }

    // An Arq string can be empty/null, indicating by a 1-byte flag.
    // If a string is not null, it is -always- an UTF-8 string.
    fn read_string(&mut self) -> Result<Option<String>> {
        // 00 or 01 = isNotNull flag
        let is_not_null = self.read_u8()?;
        match is_not_null {
            0 => return Ok(None),
            1 => {}
            v => return Err(ArqError::InvalidStringFlag(v)),
        }

        let length = self.read_u64::<BigEndian>()?;
        let mut data = vec![0u8; length as usize];
        self.read_exact(&mut data)?;

        Ok(Some(String::from_utf8(data)?))
    }

    fn read_string_default(&mut self) -> Result<String> {
        if let Some(res) = self.read_string()? {
            Ok(res)
        } else {
            Ok(String::from(""))
        }
    }
}

pub fn to_hex_string(array: &[u8]) -> String {
    array.iter().map(|a| format!("{:02x}", a)).collect()
}

pub fn bytes_to_mb(bytes: u64) -> f32 {
    (bytes as f32) / 1024.0 / 1024.0
}
