from ...abc import Expression

from ..value.eventexpr import EVENT
from ..value.eventexpr import KWARGS
from ..value.eventexpr import ARG
from ..value.valueexpr import VALUE
from ..utility.context import CONTEXT


class ITEM(Expression):
	"""
Get the item from a dictionary.

There are two forms:

1) Mapping form

!ITEM
with: !EVENT
item: foo
default: 0

2) Scalar form

!ITEM EVENT potatoes

Scalar form has some limitations (e.g no default value) but it is more compact
	"""

	Attributes = {
		"With": ["*"],  # TODO: This ...
		"Item": ["*"],  # TODO: This ...
		"Default": ["*"],  # TODO: This ...
	}

	def __init__(self, app, *, arg_with=None, arg_item=None, arg_default=None, value=None):
		super().__init__(app)

		self.OutletType = '^'  # Inlet type is to be set based on the parent advice

		if value is not None:
			# Scalar value provided
			with_, item = value.split(' ', 2)

			with_ = with_.upper()
			if with_ == 'EVENT':
				self.With = EVENT(app, value='')
			elif with_ == 'CONTEXT':
				self.With = CONTEXT(app, value='')
			elif with_ == 'KWARGS':
				self.With = KWARGS(app, value='')
			elif with_ == 'ARG':
				self.With = ARG(app, value='')
			else:
				raise RuntimeError("Invalid item argument '{}' - must be EVENT, CONTEXT, KWARGS, ARG", format(with_))

			self.Item = VALUE(app, value=item)
			self.Default = VALUE(app, value=None)

		else:
			if isinstance(arg_with, Expression):
				self.With = arg_with
			else:
				self.With = VALUE(app, value=arg_with)

			if isinstance(arg_item, Expression):
				self.Item = arg_item
			else:
				self.Item = VALUE(app, value=arg_item)

			if isinstance(arg_default, Expression):
				self.Default = arg_default
			else:
				self.Default = VALUE(app, value=arg_default)


	def optimize(self):

		if isinstance(self.With, EVENT) and isinstance(self.Item, VALUE):
			return ITEM_optimized_EVENT_VALUE(self)

		elif isinstance(self.With, CONTEXT) and isinstance(self.Item, VALUE):
			if "." in self.Item.Value:
				return ITEM_optimized_CONTEXT_VALUE_NESTED(self)
			else:
				return ITEM_optimized_CONTEXT_VALUE(self)

		return None


	def __call__(self, context, event, *args, **kwargs):
		wth = self.With(context, event, *args, **kwargs)
		item = self.Item(context, event, *args, **kwargs)

		try:
			return wth[item]

		except (KeyError, IndexError):
			if self.Default is None:
				return None
			return self.Default(context, event, *args, **kwargs)


class ITEM_optimized_EVENT_VALUE(ITEM):

	def __init__(self, orig):
		super().__init__(orig.App)

		self.With = orig.With
		self.Item = orig.Item
		self.Default = orig.Default

		if self.Default is None:
			self.DefaultValue = None
		elif isinstance(self.Default, VALUE):
			self.DefaultValue = self.Default(None, None)
		else:
			raise NotImplementedError("Default: {}".format(self.Default))

		self.Key = self.Item.Value
		self.OutletType = orig.OutletType

	def get_outlet_type(self):
		return self.OutletType

	def set_outlet_type(self, outlet_type):
		self.OutletType = outlet_type

	def optimize(self):
		# This is to prevent re-optimising the class
		return None

	def __call__(self, context, event, *args, **kwargs):
		return event.get(self.Key, self.DefaultValue)


class ITEM_optimized_CONTEXT_VALUE(ITEM):

	def __init__(self, orig):
		super().__init__(orig.App)

		self.With = orig.With
		self.Item = orig.Item
		self.Default = orig.Default

		self.Key = self.Item.Value

		if self.Default is None:
			self.DefaultValue = None
		elif isinstance(self.Default, VALUE):
			self.DefaultValue = self.Default(None, None)
		else:
			raise NotImplementedError("Default: {}".format(self.Default))

		self.OutletType = orig.OutletType

	def get_outlet_type(self):
		return self.OutletType

	def set_outlet_type(self, outlet_type):
		self.OutletType = outlet_type

	def optimize(self):
		# This is to prevent re-optimising the class
		return None

	def __call__(self, context, event, *args, **kwargs):
		return context.get(self.Key, self.Default)


class ITEM_optimized_CONTEXT_VALUE_NESTED(ITEM):

	def __init__(self, orig):
		super().__init__(orig.App)

		self.With = orig.With
		self.Item = orig.Item
		self.Default = orig.Default

		if self.Default is None:
			self.DefaultValue = None
		elif isinstance(self.Default, VALUE):
			self.DefaultValue = self.Default(None, None)
		else:
			raise NotImplementedError("Default: {}".format(self.Default))

		# TODO: Replace with JSON pointer path
		self.KeyList = self.Item.Value.split('.')

		self.OutletType = orig.OutletType

	def get_outlet_type(self):
		return self.OutletType

	def set_outlet_type(self, outlet_type):
		self.OutletType = outlet_type

	def optimize(self):
		# This is to prevent re-optimising the class
		return None

	def __call__(self, context, event, *args, **kwargs):
		value = context
		try:
			for key in self.KeyList:
				if isinstance(value, list):
					value = value[int(key)]
				else:
					value = value[key]
		except (TypeError, KeyError, IndexError):
			return self.DefaultValue

		return value
