use serde::{de, Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum SystemPacket {
    Ping,
    Pong,
    Close { reason: String },
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum SimConnectPacket {
    // ..
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum Packets {
    System(SystemPacket),
    SimConnect(SimConnectPacket),
}

impl Packet for Packets {}

pub trait Packet: Serialize + de::DeserializeOwned + Sized {
    fn encode(&self) -> Result<Vec<u8>, anyhow::Error> {
        use flate2::write::GzEncoder;
        use flate2::Compression;
        use std::io::Write;

        let encoded = bincode::serialize(&self)?;
        let mut compressor = GzEncoder::new(Vec::new(), Compression::best());
        compressor.write_all(&encoded)?;

        Ok(compressor.finish()?)
    }

    fn decode(data: &[u8]) -> Result<Self, anyhow::Error> {
        use flate2::read::GzDecoder;
        use std::io::Read;
        let mut decompressor = GzDecoder::new(data);
        let mut buffer = Vec::new();
        decompressor.read_to_end(&mut buffer)?;

        Ok(bincode::deserialize(&buffer)?)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let msg = Packets::System(SystemPacket::Ping);

        let decoded = Packet::decode(&msg.encode().unwrap()).unwrap();

        assert_eq!(msg, decoded);
    }
}
