import re
from zope.interface import implementer
from twisted.web import template
from . import iformal


_IDENTIFIER_REGEX = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')


def titleFromName(name):

    def _():

        it = iter(name)
        last = None

        while 1:
            try:
                ch = next(it)
            except StopIteration:
                return
            if ch == '_':
                if last != '_':
                    yield ' '
            elif last in (None,'_'):
                yield ch.upper()
            elif ch.isupper() and not last.isupper():
                yield ' '
                yield ch.upper()
            else:
                yield ch
            last = ch

    return ''.join(_())

def keytocssid(fieldKey, *extras):
    return render_cssid(fieldKey, *extras)

def render_cssid(fieldKey, *extras):
    """
    Render the CSS id for the form field's key.
    """
    l = [template.slot('formName'), '-', '-'.join(fieldKey.split('.'))]
    for extra in extras:
        l.append('-')
        l.append(str(extra))
    return l



def validIdentifier(name):
    """
    Test that name is a valid Python identifier.
    """
    return _IDENTIFIER_REGEX.match(name) is not None

@implementer( iformal.IKey, iformal.ILabel )
class SequenceKeyLabelAdapter(object):

    def __init__(self, original):
        self.original = original

    def key(self):
        return str(self.original[0])

    def label(self):
        return str(self.original[1])


@implementer(iformal.ILabel)
class StringableLabelAdapter(object):

    def __init__(self, original):
        self.original = str(original)

    def label(self):
        return self.original


# This is a duplication of gavo.utils.misctricks; I don't want either
# of the two depend on each other.
class CaseSemisensitiveDict(dict):
	"""A dictionary allowing case-insensitive access to its content.

	This is used for DAL renderers which, unfortunately, are supposed
	to be case insensitive.  Since case insensitivity is at least undesirable
	for service-specific keys, we go a semi-insenstitve approach here:
	First, we try literal matches, if that does not work, we try matching
	against an all-uppercase version.

	Name clashes resulting from different names being mapped to the
	same normalized version are handled in some random way.  Don't do this.
	And don't rely on case normalization if at all possible.

	Only strings are allowed as keys here.  This class is not concerned
	with the values.
	>>> d = CaseSemisensitiveDict({"a": 1, "A": 2, "b": 3})
	>>> d["a"], d["A"], d["b"], d["B"]
	(1, 2, 3, 3)
	>>> d["B"] = 9; d["b"], d["B"]
	(3, 9)
	>>> del d["b"]; d["b"], d["B"]
	(9, 9)
	>>> "B" in d, "b" in d, "u" in d
	(True, True, False)
	>>> d.pop("a"), list(d.keys())
	(1, ['A', 'B'])
	"""
	def __init__(self, *args, **kwargs):
		dict.__init__(self, *args, **kwargs)
		self._normCasedCache = None

	def __getitem__(self, key):
		try:
			return dict.__getitem__(self, key)
		except KeyError:
			pass # try again with normalized case.
		return self._normCased[key.upper()]

	def __setitem__(self, key, value):
		self._normCasedCache = None
		dict.__setitem__(self, key, value)

	def __contains__(self, key):
		return dict.__contains__(self, key) or key.upper() in self._normCased

	def get(self, key, default=None):
		try:
			return self[key]
		except KeyError:
			return default

	def pop(self, key, default=KeyError):
		try:
			return dict.pop(self, key)
		except KeyError:
			pass # try again with normalized case.
		try:
			return self._normCased.pop(key.upper())
		except KeyError:
			if default is not KeyError:
				return default
			raise
		
	@property
	def _normCased(self):
		if self._normCasedCache is None:
			self._normCasedCache = dict((k.upper(), v)
				for k, v in self.items())
		return self._normCasedCache

	@classmethod
	def fromDict(cls, aDict):
		if isinstance(aDict, CaseSemisensitiveDict):
			return aDict
		else:
			return cls(aDict)


