__all__ = ['Parser', 'ParserError']

from .error import MarkedError
from .tokens import *  # NOQA
from .events import *  # NOQA


class ParserError(MarkedError):
	pass


class Parser:
	def __init__(self):
		self.current_event = None
		self.yaml_version = None
		self.states = []
		self.marks = []
		self.state = self.parse_stream_start

	def dispose(self):
		# Reset the state attributes (to clear self-references)
		self.states = []
		self.state = None

	def check_event(self, *choices):
		# Check the type of the next event.
		if self.current_event is None:
			if self.state:
				self.current_event = self.state()
		if self.current_event is not None:
			if not choices:
				return True
			for choice in choices:
				if isinstance(self.current_event, choice):
					return True
		return False

	def peek_event(self):
		# Get the next event.
		if self.current_event is None:
			if self.state:
				self.current_event = self.state()
		return self.current_event

	def get_event(self):
		# Get the next event and proceed further.
		if self.current_event is None:
			if self.state:
				self.current_event = self.state()
		value = self.current_event
		self.current_event = None
		return value

	# stream	::= STREAM-START implicit_document? explicit_document* STREAM-END
	# implicit_document ::= block_node DOCUMENT-END*
	# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*

	def parse_stream_start(self):
		# Parse the stream start.
		token = self.get_token()
		event = StreamStartEvent(token.start_mark, token.end_mark,
				encoding=token.encoding)

		# Prepare the next state.
		self.state = self.parse_implicit_document_start

		return event

	def parse_implicit_document_start(self):
		# Parse an implicit document.
		if not self.check_token(StreamEndToken):
			token = self.peek_token()
			start_mark = end_mark = token.start_mark
			event = DocumentStartEvent(start_mark, end_mark, explicit=False)

			# Prepare the next state.
			self.states.append(self.parse_document_end)
			self.state = self.parse_node

			return event

		else:
			return self.parse_document_start()

	def parse_document_start(self):
		# Parse an explicit document.
		if not self.check_token(StreamEndToken):
			token = self.peek_token()
			self.echoerr(None, None,
					"expected '<stream end>', but found %r" % token.id,
					token.start_mark)
			return StreamEndEvent(token.start_mark, token.end_mark)
		else:
			# Parse the end of the stream.
			token = self.get_token()
			event = StreamEndEvent(token.start_mark, token.end_mark)
			assert not self.states
			assert not self.marks
			self.state = None
		return event

	def parse_document_end(self):
		# Parse the document end.
		token = self.peek_token()
		start_mark = end_mark = token.start_mark
		explicit = False
		event = DocumentEndEvent(start_mark, end_mark, explicit=explicit)

		# Prepare the next state.
		self.state = self.parse_document_start

		return event

	def parse_document_content(self):
		return self.parse_node()

	def parse_node(self, indentless_sequence=False):
		start_mark = end_mark = None
		if start_mark is None:
			start_mark = end_mark = self.peek_token().start_mark
		event = None
		implicit = True
		if self.check_token(ScalarToken):
			token = self.get_token()
			end_mark = token.end_mark
			if token.plain:
				implicit = (True, False)
			else:
				implicit = (False, True)
			event = ScalarEvent(implicit, token.value,
					start_mark, end_mark, style=token.style)
			self.state = self.states.pop()
		elif self.check_token(FlowSequenceStartToken):
			end_mark = self.peek_token().end_mark
			event = SequenceStartEvent(implicit,
					start_mark, end_mark, flow_style=True)
			self.state = self.parse_flow_sequence_first_entry
		elif self.check_token(FlowMappingStartToken):
			end_mark = self.peek_token().end_mark
			event = MappingStartEvent(implicit,
					start_mark, end_mark, flow_style=True)
			self.state = self.parse_flow_mapping_first_key
		else:
			token = self.peek_token()
			raise ParserError("while parsing a flow node", start_mark,
					"expected the node content, but found %r" % token.id,
					token.start_mark)
		return event

	def parse_flow_sequence_first_entry(self):
		token = self.get_token()
		self.marks.append(token.start_mark)
		return self.parse_flow_sequence_entry(first=True)

	def parse_flow_sequence_entry(self, first=False):
		if not self.check_token(FlowSequenceEndToken):
			if not first:
				if self.check_token(FlowEntryToken):
					self.get_token()
					if self.check_token(FlowSequenceEndToken):
						token = self.peek_token()
						self.echoerr("While parsing a flow sequence", self.marks[-1],
							"expected sequence value, but got %r" % token.id, token.start_mark)
				else:
					token = self.peek_token()
					raise ParserError("while parsing a flow sequence", self.marks[-1],
							"expected ',' or ']', but got %r" % token.id, token.start_mark)

			if not self.check_token(FlowSequenceEndToken):
				self.states.append(self.parse_flow_sequence_entry)
				return self.parse_node()
		token = self.get_token()
		event = SequenceEndEvent(token.start_mark, token.end_mark)
		self.state = self.states.pop()
		self.marks.pop()
		return event

	def parse_flow_sequence_entry_mapping_end(self):
		self.state = self.parse_flow_sequence_entry
		token = self.peek_token()
		return MappingEndEvent(token.start_mark, token.start_mark)

	def parse_flow_mapping_first_key(self):
		token = self.get_token()
		self.marks.append(token.start_mark)
		return self.parse_flow_mapping_key(first=True)

	def parse_flow_mapping_key(self, first=False):
		if not self.check_token(FlowMappingEndToken):
			if not first:
				if self.check_token(FlowEntryToken):
					self.get_token()
					if self.check_token(FlowMappingEndToken):
						token = self.peek_token()
						self.echoerr("While parsing a flow mapping", self.marks[-1],
							"expected mapping key, but got %r" % token.id, token.start_mark)
				else:
					token = self.peek_token()
					raise ParserError("while parsing a flow mapping", self.marks[-1],
							"expected ',' or '}', but got %r" % token.id, token.start_mark)
			if self.check_token(KeyToken):
				token = self.get_token()
				if not self.check_token(ValueToken,
						FlowEntryToken, FlowMappingEndToken):
					self.states.append(self.parse_flow_mapping_value)
					return self.parse_node()
				else:
					token = self.peek_token()
					raise ParserError("while parsing a flow mapping", self.marks[-1],
							"expected value, but got %r" % token.id, token.start_mark)
			elif not self.check_token(FlowMappingEndToken):
				token = self.peek_token()
				expect_key = self.check_token(ValueToken, FlowEntryToken)
				if not expect_key:
					self.get_token()
					expect_key = self.check_token(ValueToken)

				if expect_key:
					raise ParserError("while parsing a flow mapping", self.marks[-1],
							"expected string key, but got %r" % token.id, token.start_mark)
				else:
					token = self.peek_token()
					raise ParserError("while parsing a flow mapping", self.marks[-1],
							"expected ':', but got %r" % token.id, token.start_mark)
		token = self.get_token()
		event = MappingEndEvent(token.start_mark, token.end_mark)
		self.state = self.states.pop()
		self.marks.pop()
		return event

	def parse_flow_mapping_value(self):
		if self.check_token(ValueToken):
			token = self.get_token()
			if not self.check_token(FlowEntryToken, FlowMappingEndToken):
				self.states.append(self.parse_flow_mapping_key)
				return self.parse_node()

		token = self.peek_token()
		raise ParserError("while parsing a flow mapping", self.marks[-1],
				"expected mapping value, but got %r" % token.id, token.start_mark)
