use json::{
    json_minifier::JsonMinifier,
    read::byte_to_char::{ByteToChar, CharsError},
};
use std::{
    fmt,
    io::{Error, ErrorKind, Read},
    vec::IntoIter,
};

pub struct JsonRead<P, R> {
    minifier: JsonMinifier,
    read: Option<R>,
    iter: Option<ByteToChar<R>>,
    predicate: P,
    initialized: bool,
    item_iter: Option<IntoIter<u8>>,
    item1: Option<char>,
}

impl<P, R: Read> JsonRead<P, R> {
    #[inline]
    pub fn new(read: R, predicate: P) -> Self {
        JsonRead {
            minifier: JsonMinifier::default(),
            read: Some(read),
            iter: None,
            predicate,
            initialized: false,
            item_iter: None,
            item1: None,
        }
    }

    fn get_next(&mut self) -> Result<Option<char>, CharsError> {
        match self.iter.as_mut().unwrap().next() {
            None => Ok(None),
            Some(item) => match item {
                Ok(item) => Ok(Some(item)),
                Err(err) => Err(err),
            },
        }
    }

    fn add_char_to_buffer(&mut self, buf: &mut [u8], buf_pos: &mut usize) {
        if let Some(ref mut iter) = self.item_iter {
            while *buf_pos < buf.len() {
                if let Some(byte) = iter.next() {
                    buf[*buf_pos] = byte;
                    *buf_pos += 1;
                } else {
                    break;
                }
            }
        }
    }
}

impl<P, R: Read + fmt::Debug> fmt::Debug for JsonRead<P, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Filter")
            .field("iter", &self.iter)
            .field("initialized", &self.initialized)
            .finish()
    }
}

impl<P, R> Read for JsonRead<P, R>
where
    R: Read,
    P: FnMut(&mut JsonMinifier, &char, Option<&char>) -> bool,
{
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        let mut buf_pos: usize = 0;

        if buf.is_empty() {
            return Ok(0);
        }

        if !self.initialized {
            self.iter = Some(ByteToChar::new(self.read.take().unwrap(), buf.len())?);
            self.item1 = self.get_next()?;
            self.initialized = true;
        }

        while let Some(item) = self.item1.take() {
            self.item1 = self.get_next()?;
            if (self.predicate)(&mut self.minifier, &item, self.item1.as_ref()) {
                self.item_iter = Some(item.to_string().into_bytes().into_iter());
                self.add_char_to_buffer(buf, &mut buf_pos);
            }
            if buf_pos >= buf.len() {
                break;
            }
        }
        Ok(buf_pos)
    }
}

impl From<CharsError> for Error {
    fn from(_: CharsError) -> Self {
        Error::from(ErrorKind::InvalidData)
    }
}
