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

use arrayvec::ArrayVec;
use num_traits::PrimInt;

use crate::_7bit_code::{
    _7BitDecode,
    _7BitEncode,
    DecodeState,
};

pub trait Write7bc: Write {
    fn write_7bc(&mut self, value: impl _7BitEncode) -> io::Result<()> {
        let bytes: ArrayVec<_, 16> = value.into_7bit_codes().collect();
        self.write_all(&bytes)?;
        Ok(())
    }
}

impl<T: Write> Write7bc for T {}

#[derive(Debug, derive_more::From, derive_more::Display, derive_more::Error)]
pub enum ReadError {
    Io(io::Error),
    #[from(skip)]
    DecodeOverflow,
}

pub trait Read7bc: Read {
    fn read_7bc<U: _7BitDecode + PrimInt>(&mut self) -> Result<U, ReadError> {
        let mut b = [0; 1];
        let mut builder = U::build_from_7bit_codes();
        loop {
            self.read_exact(&mut b)?;
            match builder(b[0]) {
                DecodeState::Done(res) => break Ok(res),
                DecodeState::Overflow(_) => Err(ReadError::DecodeOverflow)?,
                _ => {}
            }
        }
    }
}

impl<T: Read> Read7bc for T {}
