use gramatika::{ArcStr, ParseStreamer, SpannedError, Token as _, TokenCtor};
use crate::{
	comment::Comment,
	token::{Lexer, Token},
	TokenKind,
};
pub struct ParseStream {
	inner: gramatika::ParseStream<Token, Lexer>,
	comments: Vec<Comment>,
	parsing_comment: bool,
	pub(crate) errors: Vec<SpannedError>,
}
pub struct ParseResult {
	pub source: ArcStr,
	pub tokens: Vec<Token>,
	pub comments: Vec<Comment>,
	pub errors: Vec<SpannedError>,
}
impl ParseStream {
	pub fn source(&self) -> ArcStr {
		self.inner.source()
	}
	pub fn into_inner(self) -> ParseResult {
		let (source, tokens) = self.inner.into_inner();
		ParseResult {
			source,
			tokens,
			comments: self.comments,
			errors: self.errors,
		}
	}
	pub fn split_next(
		&mut self,
		split_at: usize,
		ctors: (TokenCtor<Token>, TokenCtor<Token>),
	) -> gramatika::Result<Token> {
		self.inner.split_next(split_at, ctors)
	}
	fn did_parse_comment(&mut self) -> bool {
		let Some(token) = self.inner.peek() else {
			return false;
		};
		match token.kind() {
			TokenKind::CommentStart => {
				self.parsing_comment = true;
				match self.parse::<Comment>() {
					Ok(comment) => {
						self.comments.push(comment);
						self.parsing_comment = false;
						true
					}
					Err(error) => {
						self.errors.push(error);
						self.parsing_comment = false;
						true
					}
				}
			}
			_ => false,
		}
	}
}
impl<S> From<S> for ParseStream
where S: Into<ArcStr>
{
	fn from(value: S) -> Self {
		Self {
			inner: gramatika::ParseStream::from(value),
			comments: vec![],
			parsing_comment: false,
			errors: vec![],
		}
	}
}
impl ParseStreamer for ParseStream {
	type Token = Token;
	fn is_empty(&mut self) -> bool {
		self.inner.is_empty()
	}
	fn peek(&mut self) -> Option<&Token> {
		if !self.parsing_comment && self.did_parse_comment() {
			self.peek()
		} else {
			self.inner.peek()
		}
	}
	fn prev(&mut self) -> Option<&Token> {
		self.inner.prev()
	}
	fn check_kind(&mut self, kind: TokenKind) -> bool {
		if !self.parsing_comment && self.did_parse_comment() {
			self.check_kind(kind)
		} else {
			self.inner.check_kind(kind)
		}
	}
	fn check(&mut self, compare: Token) -> bool {
		if !self.parsing_comment && self.did_parse_comment() {
			self.check(compare)
		} else {
			self.inner.check(compare)
		}
	}
	fn consume(&mut self, compare: Token) -> gramatika::Result<Token> {
		if !self.parsing_comment && self.did_parse_comment() {
			self.consume(compare)
		} else {
			self.inner.consume(compare)
		}
	}
	fn consume_kind(&mut self, kind: TokenKind) -> gramatika::Result<Token> {
		if !self.parsing_comment && self.did_parse_comment() {
			self.consume_kind(kind)
		} else {
			self.inner.consume_kind(kind)
		}
	}
	fn consume_as(
		&mut self,
		kind: TokenKind,
		convert: TokenCtor<Token>,
	) -> gramatika::Result<Token> {
		if !self.parsing_comment && self.did_parse_comment() {
			self.consume_as(kind, convert)
		} else {
			self.inner.consume_as(kind, convert)
		}
	}
	fn upgrade_last(
		&mut self,
		kind: TokenKind,
		convert: TokenCtor<Token>,
	) -> gramatika::Result<Token> {
		self.inner.upgrade_last(kind, convert)
	}
	fn upgrade(
		&mut self,
		token: Self::Token,
		convert: TokenCtor<Token>,
	) -> gramatika::Result<Token> {
		self.inner.upgrade(token, convert)
	}
	fn discard(&mut self) {
		self.inner.discard();
	}
}
impl Iterator for ParseStream {
	type Item = Token;
	fn next(&mut self) -> Option<Token> {
		self.inner.next()
	}
}