use super::*;
use crate::proto::EDemoCommands;
use crate::reader::{BitsReader, MessageReader};
use std::io::{Seek, Write};

pub(crate) struct RawDemoMessage {
    pub(crate) msg_type: EDemoCommands,
    pub(crate) tick: u32,
    pub(crate) raw_payload: Vec<u8>,
    pub(crate) compressed: bool,
}

impl<'a, R, W> DemoWriter<'a, R, W>
where
    R: BitsReader + MessageReader,
    W: Write + Seek,
{
    pub(crate) fn read_next_raw_message(&mut self) -> Result<Option<RawDemoMessage>, ParserError> {
        self.parser.reader.refill();
        if self.parser.reader.remaining_bytes() == 0 {
            return Ok(None);
        }

        let cmd = self.parser.reader.read_var_u32() as i32;
        let tick = self.parser.reader.read_var_u32();
        let size = self.parser.reader.read_var_u32();
        let compressed = cmd & EDemoCommands::DemIsCompressed as i32 != 0;
        let msg_type = EDemoCommands::try_from(cmd & !(EDemoCommands::DemIsCompressed as i32))?;
        let raw_payload = self.parser.reader.read_bytes(size);

        Ok(Some(RawDemoMessage {
            msg_type,
            tick,
            raw_payload,
            compressed,
        }))
    }

    pub(crate) fn decode_raw_payload(message: &RawDemoMessage) -> Result<Vec<u8>, ParserError> {
        if message.compressed {
            let mut decoder = snap::raw::Decoder::new();
            Ok(decoder.decompress_vec(message.raw_payload.as_slice())?)
        } else {
            Ok(message.raw_payload.clone())
        }
    }

    pub(crate) fn materialize_payload(
        payload: &mut Option<Vec<u8>>,
        message: &RawDemoMessage,
    ) -> Result<Vec<u8>, ParserError> {
        payload
            .take()
            .map(Ok)
            .unwrap_or_else(|| Self::decode_raw_payload(message))
    }
}
