"""
Common code for DaCHS's base package.
"""

#c Copyright 2008-2022, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.

import warnings

from gavo.utils.excs import *  #noflake: really want those names
from gavo.utils import excs # make life a bit easier for pyflakes.


class NotGivenType(type):
	def __str__(self):
		raise excs.StructureError(
			"%s cannot be stringified"%self.__class__.__name__)

	def __repr__(self):
		return "<Not given/empty>"

	def __bool__(self):
		return False


class NotGiven(object, metaclass=NotGivenType):
	"""A sentinel class for defaultless values that can remain undefined.
	"""


class Ignore(excs.ExecutiveAction):
	"""An executive action causing an element to be not adopted by its
	parent.

	Raise this in -- typically -- onElementComplete if the element just
	built goes somewhere else but into its parent structure (or is
	somehow benignly unusable).  Classic use case: Active Tags.
	"""


class Replace(excs.ExecutiveAction):
	"""An executive action replacing the current child with the Exception's
	argument.

	Use this sparingly.  I'd like to get rid of it.
	"""
	def __init__(self, newOb, newName=None):
		self.newOb, self.newName = newOb, newName


class Parser(object):
	"""is an object that routes events.

	It is constructed with up to three functions for handling start,
	value, and end events; these would override methods ``start_``, ``end_``,
	or ``value_``.  Thus, you can simply implement when inheriting from
	Parser.  In that case, no call the the constructor is necessary
	(i.e., Parser works as a mixin as well).
	"""
	def __init__(self, start=None, value=None, end=None):
		self.start, self.value, self.end = start, value, end
	
	def feedEvent(self, ctx, type, name, value):
		if type=="start":
			return self.start_(ctx, name, value)
		elif type=="value":
			return self.value_(ctx, name, value)
		elif type=="end":
			return self.end_(ctx, name, value)
		else:
			raise excs.StructureError(
				"Illegal event type while building: '%s'"%type)


class StructParseDebugMixin(object):
	"""put this before Parser in the parent class list of a struct,
	and you'll see the events coming in to your parser.
	"""
	def feedEvent(self, ctx, type, name, value):
		print(type, name, value, self)
		return Parser.feedEvent(self, ctx, type, name, value)


class StructCallbacks:
	"""A class terminating super() calls for the structure callbacks.

	Structs, when finished, call completeElement(ctx), validate(),
	and onElementComplete() in sequence.  All these need to up-call.
	This mixin provides non-upcalling implementations of these methods,
	and it needs to be present in mixins for structure.

	This will only work properly if self actually is a struct, as we rely
	on the presence of name_ attributes in case of prematurely terminated
	method resolution.
	"""
	def __warnTermination(self, methname):
		if hasattr(super(), methname):
			args = (self.name_, " ".join(c.__name__ for c in self.__class__.__mro__))
			warnings.warn("Discarding validate method while calling up"
				" from {}, mro {} ".format(*args))

	def validate(self):
		self.__warnTermination("validate")
	
	def completeElement(self, ctx):
		self.__warnTermination("completeElement")

	def onElementComplete(self):
		self.__warnTermination("onElementComplete")
	
	def setParent(self, parent):
		self.__warnTermination("setParent")
