use std::fmt::{Display, Formatter};
use parity_scale_codec::Encode;
use crate::sync::data::MAX_SYNC_MESSAGE_SIZE;
const MSG_BYTES_LIMIT: usize = MAX_SYNC_MESSAGE_SIZE as usize;
pub struct Limiter<'a, D: Encode, const LIMIT: usize> {
    msg: &'a [D],
    start_index: usize,
}
pub type MsgLimiter<'a, D> = Limiter<'a, D, MSG_BYTES_LIMIT>;
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
    ItemTooBig,
}
impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::ItemTooBig => write!(f, "Single item takes more than the limit"),
        }
    }
}
impl<'a, D: Encode, const LIMIT: usize> Limiter<'a, D, LIMIT> {
    pub fn new(msg: &'a [D]) -> Self {
        Self {
            msg,
            start_index: 0,
        }
    }
    pub fn next_largest_msg(&mut self) -> Result<Option<&'a [D]>, Error> {
        if self.start_index == self.msg.len() {
            return Ok(None);
        }
        let end_idx = self.find_idx_of_largest_prefix()?;
        let start_index = self.start_index;
        self.start_index = end_idx;
        Ok(Some(&self.msg[start_index..end_idx]))
    }
    fn find_idx_of_largest_prefix(&self) -> Result<usize, Error> {
        let mut idx = self.start_index;
        let mut encoded_sum = 0;
        while idx < self.msg.len() && encoded_sum <= LIMIT {
            encoded_sum += self.msg[idx].encoded_size();
            idx += 1;
        }
        while idx > self.start_index && self.msg[self.start_index..idx].encoded_size() > LIMIT {
            idx -= 1;
        }
        if idx == self.start_index {
            Err(Error::ItemTooBig)
        } else {
            Ok(idx)
        }
    }
}
#[cfg(test)]
mod tests {
    use parity_scale_codec::Encode;
    use crate::sync::message_limiter::{Error, Limiter};
    type TestLimiter<'a, D> = Limiter<'a, D, 10>;
    #[derive(Clone, Debug, Eq, PartialEq)]
    struct EncodeToSize(usize);
    impl Encode for EncodeToSize {
        fn size_hint(&self) -> usize {
            self.0
        }
        fn encode(&self) -> Vec<u8> {
            vec![0; self.0]
        }
    }
    fn sized(size: usize) -> EncodeToSize {
        EncodeToSize(size)
    }
    #[test]
    fn takes_one_that_fit() {
        let v = vec![sized(5), sized(6), sized(7)];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..1])), lim.next_largest_msg())
    }
    #[test]
    fn takes_all() {
        let v = vec![sized(1), sized(2), sized(3)];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..])), lim.next_largest_msg())
    }
    #[test]
    fn takes_all_that_fits_into_limit() {
        let v = vec![sized(1), sized(2), sized(7)];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..2])), lim.next_largest_msg())
    }
    #[test]
    fn works_with_empty_input() {
        let v = vec![];
        let mut lim = TestLimiter::<EncodeToSize>::new(&v);
        assert_eq!(Ok(None), lim.next_largest_msg())
    }
    #[test]
    fn respects_the_limit() {
        let v = vec![sized(10)];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Err(Error::ItemTooBig), lim.next_largest_msg())
    }
    #[test]
    fn iterates_correctly() {
        let v = vec![sized(5), sized(6), sized(7)];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..1])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[1..2])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[2..3])), lim.next_largest_msg());
        assert_eq!(Ok(None), lim.next_largest_msg());
    }
    #[test]
    fn iterates_correctly_2() {
        let v = vec![
            sized(5),
            sized(3),
            sized(2),
            sized(5),
            sized(5),
            sized(6),
            sized(7),
        ];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..2])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[2..4])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[4..5])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[5..6])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[6..7])), lim.next_largest_msg());
        assert_eq!(Ok(None), lim.next_largest_msg());
    }
    #[test]
    fn iterates_correctly_with_oversized_element() {
        let v = vec![
            sized(5),
            sized(3),
            sized(2),
            sized(5),
            sized(5),
            sized(6),
            sized(10), ];
        let mut lim = TestLimiter::new(&v);
        assert_eq!(Ok(Some(&v[..2])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[2..4])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[4..5])), lim.next_largest_msg());
        assert_eq!(Ok(Some(&v[5..6])), lim.next_largest_msg());
        assert_eq!(Err(Error::ItemTooBig), lim.next_largest_msg());
    }
}