use std::io::{Result, Write};

macro_rules! impl_write {
    ($($type:ty),*) => {
        $(
            paste::paste! {
                #[inline]
                fn [<write_ $type _ne>](&mut self, value: $type) -> usize {
                    self.write(&value.to_ne_bytes()).expect("Write failed")
                }

                #[inline]
                fn [<write_ $type _ne_checked>](&mut self, value: $type) -> Result<usize> {
                    self.write(&value.to_ne_bytes())
                }

                #[inline]
                fn [<write_ $type _be>](&mut self, value: $type) -> usize {
                    self.write(&value.to_be_bytes()).expect("Write failed")
                }

                #[inline]
                fn [<write_ $type _be_checked>](&mut self, value: $type) -> Result<usize> {
                    self.write(&value.to_be_bytes())
                }

                #[inline]
                fn [<write_ $type _le>](&mut self, value: $type) -> usize {
                    self.write(&value.to_le_bytes()).expect("Write failed")
                }

                #[inline]
                fn [<write_ $type _le_checked>](&mut self, value: $type) -> Result<usize> {
                    self.write(&value.to_le_bytes())
                }
            }
        )*
    };
}

/// # Write extension
/// All provided methods interprets input value with specified engianess and then writes them to the writer.
/// # Panics
/// If [`std::io::Write::write`] will return an error, **ALL** methods that doesn't end with `_checked` will panic.
/// All `_checked` methods will return an error in that case.
/// Provided methods:
/// * `write_*_ne` - Native-endian
/// * `write_*_be` - Big-endian
/// * `write_*_le` - Little-endian
/// # Example
/// ```
/// fn main() {
///     use io_ext::WriteExt;
///     
///     let mut buf = Box::new([0u8; 4]);
///     let mut slice = &mut buf[..];
///     // Note: &[u8] implements Read trait
/// 
///     slice.write_u16_be(0x40);
///     assert!(slice.write_i16_le_checked(13).is_ok());
/// 
///     assert_eq!(&buf[..], b"\x00\x40\x0D\x00");
/// }
/// ```
pub trait WriteExt: Write {
    #[inline]
    fn write_u8(&mut self, value: u8) -> usize {
        self.write(std::slice::from_ref(&value)).expect("Write failed")
    }

    #[inline]
    fn write_u8_checked(&mut self, value: u8) -> Result<usize> {
        self.write(std::slice::from_ref(&value))
    }

    impl_write!(f32 , f64, u16, i16, u32, i32, u64, i64, u128, i128);
}

impl<W> WriteExt for W
where
    W: Write 
{ }
