use anyhow::anyhow;
use std::io::Read;
use crate::opcodes;

pub enum Event {
    Noop,
    ClientToServer(Vec<u8>),
    ServerToClient(Vec<u8>),
    Delay(u32),
    ClientToServerEof,
    ServerToClientEof,
}

impl Event {
    pub fn name(&self) -> &'static str {
        match self {
            Event::Noop => "NOOP",
            Event::ClientToServer(_) => "CLIENT_TO_SERVER",
            Event::ServerToClient(_) => "SERVER_TO_CLIENT",
            Event::Delay(_) => "DELAY",
            Event::ClientToServerEof => "CLIENT_TO_SERVER_EOF",
            Event::ServerToClientEof => "SERVER_TO_CLIENT_EOF",
        }
    }

    pub fn role_reverse(self) -> Self {
        match self {
            Event::ClientToServer(data) => Event::ServerToClient(data),
            Event::ServerToClient(data) => Event::ClientToServer(data),
            Event::ClientToServerEof => Event::ServerToClientEof,
            Event::ServerToClientEof => Event::ClientToServerEof,
            other => other,
        }
    }
}

pub struct EventReader<R: Read> {
    reader: R,
}

impl<R: Read> EventReader<R> {
    pub fn new(reader: R) -> Self {
        Self { reader }
    }

    pub fn read_event(&mut self) -> anyhow::Result<Option<Event>> {
        let mut header = [0u8; 4];
        match self.reader.read_exact(&mut header) {
            Ok(()) => {},
            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
            Err(e) => return Err(e.into()),
        }

        let opcode = header[0];
        let len = ((header[1] as u32) << 16) | ((header[2] as u32) << 8) | (header[3] as u32);

        let event = match opcode {
            opcodes::NOOP => {
                if len > 0 {
                    let mut data = vec![0; len as usize];
                    self.reader.read_exact(&mut data)?;
                }
                Event::Noop
            }
            opcodes::CLIENT_TO_SERVER => {
                let mut data = vec![0; len as usize];
                self.reader.read_exact(&mut data)?;
                Event::ClientToServer(data)
            }
            opcodes::SERVER_TO_CLIENT => {
                let mut data = vec![0; len as usize];
                self.reader.read_exact(&mut data)?;
                Event::ServerToClient(data)
            }
            opcodes::DELAY => Event::Delay(len),
            opcodes::CLIENT_TO_SERVER_EOF => Event::ClientToServerEof,
            opcodes::SERVER_TO_CLIENT_EOF => Event::ServerToClientEof,
            _ if opcode & 0x80 != 0 => {
                // Future backward-compatible extension: skip over it
                if len > 0 {
                    let mut data = vec![0; len as usize];
                    self.reader.read_exact(&mut data)?;
                }
                Event::Noop
            }
            _ => return Err(anyhow!("unknown opcode: {}", opcode)),
        };

        Ok(Some(event))
    }
}
