use crate::{
    Error,
    payload::{Decode, Encode},
};
use std::io::{Read, Seek, SeekFrom};
use crate::Payload;

#[derive(Debug, Clone, PartialEq)]
pub struct TextPayload(pub String);

impl<T: Into<String>> From<T> for TextPayload {
    fn from(text: T) -> Self {
        Self(text.into())
    }
}

impl AsRef<str> for TextPayload {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Decode for TextPayload {
    fn decode<R: Read + Seek>(mut reader: R, _chunk_len: usize) -> Result<Self, crate::Error> {
        let mut text = Vec::with_capacity(32);

        let mut buf = [0; 32];
        let mut read: usize;
        loop {
            read = reader.read(&mut buf).map_err(Error::from)?;
            // end of stream
            if read == 0 {
                break;
            }

            let start = buf[..read].iter().position(|&b| b == Payload::START_BYTE);
            if let Some(start) = start {
                // 5 4 3 5 4 5 = 6 bytes read
                //     ^ - start byte at idx 2
                // seek backwards until start byte is next to read: read - idx = 4 bytes
                let offset = (read - start) as i64;
                reader.seek(SeekFrom::Current(-offset)).map_err(Error::from)?;

                // tell the rest of the loop that we only read up to the start byte
                read = start as usize;
            }

            for &byte in &buf[..read] {
                text.push(byte);
            }

            // we encountered a real payload, so break now
            if start.is_some() {
                break;
            }
        }

        let text = if text.len() > 0 {
            String::from_utf8(text).map_err(Error::from)?
        } else {
            String::new()
        };

        Ok(Self(text))
    }
}

impl Encode for TextPayload {
    fn encode(&self) -> Vec<u8> {
        self.0.as_bytes().to_vec()
    }
}
