use std::io::{Error, Read, Write};
use crate::frame::AsBit;

pub enum WireLength {
    Small(u8),
    Medium(u16),
    Large(u64)
}

impl From<u64> for WireLength {
    fn from(value: u64) -> Self {
        if value <= 125 {
            Self::Small(value as u8)
        } else if value > 125 && value < 65536 {
            Self::Medium(value as u16)
        } else {
            Self::Large(value)
        }
    }
}

impl Into<u64> for WireLength {
    fn into(self) -> u64 {
        match self {
            Self::Small(v) => v as u64,
            Self::Medium(v) => v as u64,
            Self::Large(v) => v
        }
    }
}

pub trait LengthWritable {
    fn write_length<W: Write>(&self, masked: bool, w: &mut W) -> Result<(), std::io::Error>;
}
impl LengthWritable for WireLength {
    fn write_length<W: Write>(&self, masked: bool, w: &mut W) -> Result<(), Error> {
        match self {
            Self::Small(v) => w.write_all(&[*v | masked.as_bit() << 7]),
            Self::Medium(v) => {
                let bytes = v.to_be_bytes();
                w.write_all(&[126 | masked.as_bit() << 7, bytes[0], bytes[1]])
            },
            Self::Large(v) => {
                let bytes = v.to_be_bytes();
                w.write_all(&[127 | masked.as_bit() << 7, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]])
            },
        }
    }
}

pub trait LengthReadable {
    fn read_length<R: Read>(initial: u8, r: &mut R) -> Result<Self, std::io::Error> where Self: Sized;
}
impl LengthReadable for WireLength {
    fn read_length<R: Read>(initial: u8, r: &mut R) -> Result<Self, Error> where Self: Sized {
        if initial <= 125 {
            return Ok(WireLength::Small(initial));
        } else if initial == 126 {
            let mut buf2 = [0u8; 2];
            r.read_exact(&mut buf2)?;
            return Ok(WireLength::Medium(u16::from_be_bytes(buf2)));
        }

        let mut buf2 = [0u8; 8];
        r.read_exact(&mut buf2)?;
        return Ok(WireLength::Large(u64::from_be_bytes(buf2)));
    }
}