import xxhash

from ..value.valueexpr import VALUE
from ...abc import Expression

###


class HASH(Expression):

	Attributes = {
		"What": ["*"],
		"Seed": [
			'si64', 'si8', 'si16', 'si32',  # Those are not yet supported 'si128', 'si256',
			'ui8', 'ui16', 'ui32', 'ui64',  # Those are not yet supported 'ui128', 'ui256',
		],
		"Type": ["str"],  # And must be evaluated in compile type
	}


	def __init__(self, app, *, arg_what, arg_seed: int = 0, arg_type='xxhash64'):
		super().__init__(app)

		if isinstance(arg_type, Expression):
			self.Type = arg_type
		else:
			self.Type = VALUE(app, value=arg_type)
		arg_type = self.Type(None, None)
		assert(isinstance(arg_type, str))
		assert(arg_type in ('xxhash64'))

		if isinstance(arg_what, Expression):
			self.What = arg_what
		else:
			self.What = VALUE(app, value=arg_what)

		if isinstance(arg_seed, Expression):
			self.Seed = arg_seed
		else:
			self.Seed = VALUE(app, value=arg_seed)


	def initialize(self):
		outlet_type = self.What.get_outlet_type()
		self.Method = get_hash_method(self.Type(None, None), outlet_type)


	def __call__(self, context, event, *args, **kwargs):
		what = self.What(context, event, *args, **kwargs)
		seed = self.Seed(context, event, *args, **kwargs)
		return self.Method(what, seed)


	def get_outlet_type(self):
		return 'ui64'


def get_hash_method(hash_type, input_type):
	if hash_type == 'xxhash64':
		if input_type in (
			'int', 'si64', 'si8', 'si16', 'si32', 'si128', 'si256',
			'ui8', 'ui16', 'ui32', 'ui64', 'ui128', 'ui256'
		):
			# For integer, use the alternative hashing functions
			return hash_xxhash64_int

		elif input_type in ('str'):
			return hash_xxhash64_str

	raise NotImplementedError("Hashing for '{}' and '{}' is not yet implemented".format(hash_type, input_type))


def hash_xxhash64_str(what, seed):
	return xxhash.xxh64(what, seed=seed).intdigest()


def hash_xxhash64_int(what, seed):
	return (what ^ seed) & 0xFF_FF_FF_FF_FF_FF_FF_FF  # Limit to 64bits
