use super::ReadError;

macro_rules! eat_if_matches {
    ($parser: expr, $p: pat) => {
        eat_if_matches!($parser, $p => {})
    };
    ($parser: expr, $p: pat => $body: expr) => {
        match $parser.pop().map_err(ParseError::Read)? {
            Some($p) => Some($body),
            Some(item) => {
                $parser.push(item);
                None
            }
            None => None,
        }
    };
}

#[derive(Debug, Clone)]
pub(crate) struct Parser<I, T> {
    iter: I,
    next_item: Option<T>,
}

impl<I, T> Parser<I, T> {
    pub(crate) fn new<J, E>(iter: J) -> Self
    where
        J: IntoIterator<IntoIter = I, Item = Result<T, E>>,
        I: Iterator<Item = Result<T, E>>,
    {
        Parser {
            iter: iter.into_iter(),
            next_item: None,
        }
    }
}

impl<I, T> Parser<I, T>
where
    I: Iterator<Item = Result<T, ReadError>>,
{
    pub(crate) fn push(&mut self, item: T) {
        assert!(self.next_item.is_none());
        self.next_item = Some(item);
    }

    pub(crate) fn pop(&mut self) -> Result<Option<T>, ReadError> {
        if let Some(next) = self.next_item.take() {
            return Ok(Some(next));
        }
        if let Some(item) = self.iter.next() {
            item.map(Some)
        } else {
            Ok(None)
        }
    }

    pub(crate) fn peek(&mut self) -> Result<Option<&T>, ReadError> {
        if let Some(ref next) = self.next_item {
            return Ok(Some(next));
        }
        self.next_item = if let Some(item) = self.iter.next() {
            Some(item?)
        } else {
            None
        };
        Ok(self.next_item.as_ref())
    }
}
