use std::error::Error;
use std::fmt::{Display, Formatter};
use std::io;
use std::io::Read;
use crate::frame::{Frame, Opcode};
use crate::frame::length::{LengthReadable, WireLength};
use crate::frame::mask::mask;
use crate::frame::wire::FrameReadable;

#[derive(Debug)]
pub enum FrameReadError {
    IoError(io::Error),
    InvalidOpcode
}
impl Display for FrameReadError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::IoError(e) => write!(f, "io error: {}", e),
            Self::InvalidOpcode => write!(f, "invalid opcode")
        }
    }
}
impl Error for FrameReadError {}
impl From<io::Error> for FrameReadError {
    fn from(value: io::Error) -> Self {
        Self::IoError(value)
    }
}

impl<R: Read> FrameReadable for R {
    type Error = FrameReadError;

    fn read_frame(&mut self) -> Result<Frame, Self::Error> {
        // read the first two bytes
        trace!("frame read : tinyheader");
        let mut first_two = [0u8; 2];

        self.read_exact(&mut first_two)?;

        trace!("first two: {:x?}", first_two);

        let fin = (first_two[0] & 0b1000_0000) >> 7;
        let rsv1 = (first_two[0] & 0b0100_0000) >> 6;
        let rsv2 = (first_two[0] & 0b0010_0000) >> 5;
        let rsv3 = (first_two[0] & 0b0001_0000) >> 4;
        let opcode_u8 = first_two[0] & 0b0000_1111;

        trace!("Frame:");
        trace!(" FIN: {}", fin);
        trace!(" RSV1: {}", rsv1);
        trace!(" RSV2: {}", rsv2);
        trace!(" RSV3: {}", rsv3);

        let opcode = match opcode_u8 {
            0 => Opcode::Continuation,
            1 => Opcode::Text,
            2 => Opcode::Binary,
            8 => Opcode::ConnectionClose,
            9 => Opcode::Ping,
            10 => Opcode::Pong,
            _ => return Err(FrameReadError::InvalidOpcode)
        };

        trace!(" OP: {} = {:?}", opcode_u8, opcode);

        let do_mask = (first_two[1] & 0b1000_0000) >> 7;
        let length_without_mask = first_two[1] & 0b0111_1111;

        trace!(" Mask: {}", do_mask);
        trace!(" Length: {}", length_without_mask);

        let length: u64 = WireLength::read_length(length_without_mask, self)?.into();

        trace!(" Parsed length: {}", length);

        let mut mask_key = None;

        if do_mask == 1 {
            let mut buf = [0u8; 4];

            trace!("reading mask");

            self.read_exact(&mut buf)?;

            mask_key = Some(buf);
        }

        let mut payload;

        if length != 0 {
            payload = vec![0u8; length as usize];

            self.read_exact(&mut payload)?;

            trace!("payload read {:x?}", payload);

            if let Some(key) = mask_key {
                mask(&mut payload, key);
            }
        } else {
            payload = vec![];
        }

        Ok(Frame {
            fin: fin == 1,
            rsv1: rsv1 == 1,
            rsv2: rsv2 == 1,
            rsv3: rsv3 == 1,
            opcode,
            mask: do_mask == 1,
            payload_len: length,
            masking_key: mask_key,
            payload_data: payload,
        })
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, Write};
    use std::sync::Once;
    use crate::frame::{Frame, Opcode};
    use crate::frame::wire::{FrameReadable, FrameWritable};
    use crate::tests::setup_logger;

    #[test]
    fn decoding_6455_5_7_a() {
        let mut buf: Cursor<Vec<u8>> = Cursor::new(vec![0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]);

        let frame = buf.read_frame().unwrap();

        assert_eq!(frame, Frame {
            fin: true,
            rsv1: false,
            rsv2: false,
            rsv3: false,
            opcode: Opcode::Text,
            mask: false,
            payload_len: 5,
            masking_key: None,
            payload_data: "Hello".to_string().as_bytes().to_vec(),
        });
    }

    #[test]
    fn decoding_6455_5_7_b() {
        let mut buf: Cursor<Vec<u8>> = Cursor::new(vec![0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58]);

        assert_eq!(buf.read_frame().unwrap(), Frame {
            fin: true,
            rsv1: false,
            rsv2: false,
            rsv3: false,
            opcode: Opcode::Text,
            mask: true,
            payload_len: 5,
            masking_key: Some([0x37, 0xfa, 0x21, 0x3d]),
            payload_data: "Hello".to_string().as_bytes().to_vec(),
        });
    }

    #[test]
    fn decoding_autobahn_1_1_1_close_frame() {
        setup_logger();

        let mut buf: Cursor<Vec<u8>> = Cursor::new(vec![0x88, 0x0c, 0x03, 0xe9, 0x47, 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x77, 0x61, 0x79]);

        assert_eq!(buf.read_frame().unwrap(), Frame {
            fin: true,
            rsv1: false,
            rsv2: false,
            rsv3: false,
            opcode: Opcode::ConnectionClose,
            mask: false,
            payload_len: 12,
            masking_key: None,
            payload_data: [3, 233, 71, 111, 105, 110, 103, 32, 65, 119, 97, 121].to_vec(),
        });
    }

    // 5.7(e) and 5.7(f) are skipped because they are impractical to write
}