__all__ = ['BaseResolver', 'Resolver']

from .error import MarkedError
from .nodes import *  # NOQA

import re


class ResolverError(MarkedError):
	pass


class BaseResolver:
	DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str'
	DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq'
	DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map'

	yaml_implicit_resolvers = {}
	yaml_path_resolvers = {}

	def __init__(self):
		self.resolver_exact_paths = []
		self.resolver_prefix_paths = []

	@classmethod
	def add_implicit_resolver(cls, tag, regexp, first):
		if not 'yaml_implicit_resolvers' in cls.__dict__:
			cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy()
		if first is None:
			first = [None]
		for ch in first:
			cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))

	def descend_resolver(self, current_node, current_index):
		if not self.yaml_path_resolvers:
			return
		exact_paths = {}
		prefix_paths = []
		if current_node:
			depth = len(self.resolver_prefix_paths)
			for path, kind in self.resolver_prefix_paths[-1]:
				if self.check_resolver_prefix(depth, path, kind,
						current_node, current_index):
					if len(path) > depth:
						prefix_paths.append((path, kind))
					else:
						exact_paths[kind] = self.yaml_path_resolvers[path, kind]
		else:
			for path, kind in self.yaml_path_resolvers:
				if not path:
					exact_paths[kind] = self.yaml_path_resolvers[path, kind]
				else:
					prefix_paths.append((path, kind))
		self.resolver_exact_paths.append(exact_paths)
		self.resolver_prefix_paths.append(prefix_paths)

	def ascend_resolver(self):
		if not self.yaml_path_resolvers:
			return
		self.resolver_exact_paths.pop()
		self.resolver_prefix_paths.pop()

	def check_resolver_prefix(self, depth, path, kind,
			current_node, current_index):
		node_check, index_check = path[depth - 1]
		if isinstance(node_check, str):
			if current_node.tag != node_check:
				return
		elif node_check is not None:
			if not isinstance(current_node, node_check):
				return
		if index_check is True and current_index is not None:
			return
		if (index_check is False or index_check is None)	\
				and current_index is None:
			return
		if isinstance(index_check, str):
			if not (isinstance(current_index, ScalarNode)
					and index_check == current_index.value):
				return
		elif isinstance(index_check, int) and not isinstance(index_check, bool):
			if index_check != current_index:
				return
		return True

	def resolve(self, kind, value, implicit, mark=None):
		if kind is ScalarNode and implicit[0]:
			if value == '':
				resolvers = self.yaml_implicit_resolvers.get('', [])
			else:
				resolvers = self.yaml_implicit_resolvers.get(value[0], [])
			resolvers += self.yaml_implicit_resolvers.get(None, [])
			for tag, regexp in resolvers:
				if regexp.match(value):
					return tag
			else:
				self.echoerr('While resolving plain scalar', None,
						'expected floating-point value, integer, null or boolean, but got %r' % value,
						mark)
				return self.DEFAULT_SCALAR_TAG
		if kind is ScalarNode:
			return self.DEFAULT_SCALAR_TAG
		elif kind is SequenceNode:
			return self.DEFAULT_SEQUENCE_TAG
		elif kind is MappingNode:
			return self.DEFAULT_MAPPING_TAG


class Resolver(BaseResolver):
	pass


Resolver.add_implicit_resolver(
		'tag:yaml.org,2002:bool',
		re.compile(r'''^(?:true|false)$''', re.X),
		list('yYnNtTfFoO'))

Resolver.add_implicit_resolver(
		'tag:yaml.org,2002:float',
		re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X),
		list('-0123456789'))

Resolver.add_implicit_resolver(
		'tag:yaml.org,2002:int',
		re.compile(r'^(?:0|-?[1-9]\d*)$', re.X),
		list('-0123456789'))

Resolver.add_implicit_resolver(
		'tag:yaml.org,2002:null',
		re.compile(r'^null$', re.X),
		['n'])
