# Copyright (c) 2010-2022 Belledonne Communications SARL.
#
# This file is part of Liblinphone 
# (see https://gitlab.linphone.org/BC/public/liblinphone).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.



import re


class Name:
	camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*')
	lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)')
	
	def __init__(self):
		self.words = []
		self.prev = None
	
	def __eq__(self, other):
		return (other is not None and self.words == other.words) and (self.prev == other.prev)
	
	def __lt__(self, other):
		return self.to_camel_case() < other.to_camel_case()
	
	def copy(self):
		nameType = type(self)
		name = nameType()
		name.words = list(self.words)
		name.prev = None if self.prev is None else self.prev.copy()
		return name
	
	def delete_prefix(self, prefix):
		it = self
		_next = None
		while it is not None and it.words != prefix.words:
			_next = it
			it = it.prev
		
		if it is None or it != prefix:
			raise Error('no common prefix')
		elif _next is not None:
			_next.prev = None
	
	def _set_namespace(self, namespace):
		self.prev = namespace
		if self.prev is not None:
			prefix = namespace.to_word_list()
			i = 0
			while i<len(self.words) and i<len(prefix) and self.words[i] == prefix[i]:
				i += 1
			if i == len(self.words):
				raise Error('name equal to namespace \'{0}\'', self.words)
			else:
				self.words = self.words[i:]
	
	def _lower_all_words(self):
		i = 0
		while i<len(self.words):
			self.words[i] = self.words[i].lower()
			i += 1
	
	def from_snake_case(self, name, namespace=None):
		self.words = name.split('_')
		Name._set_namespace(self, namespace)
	
	def from_camel_case(self, name, islowercased=False, namespace=None):
		if not islowercased:
			self.words = Name.camelCaseParsingRegex.findall(name)
		else:
			match = Name.lowerCamelCaseSplitingRegex.match(name)
			self.words = [match.group(1)]
			self.words += Name.camelCaseParsingRegex.findall(match.group(2))
		
		Name._lower_all_words(self)
		Name._set_namespace(self, namespace)
	
	def to_snake_case(self, fullName=False, upper=False):
		if self.prev is None or not fullName:
			res = '_'.join(self.words)
			if upper:
				res = res.upper()
			return res
		else:
			return Name.to_snake_case(self.prev, fullName=True, upper=upper) + '_' + Name.to_snake_case(self, upper=upper)
	
	def to_camel_case(self, lower=False, fullName=False):
		if self.prev is None or not fullName:
			res = ''
			for elem in self.words:
				if elem is self.words[0] and lower:
					res += elem
				else:
					res += elem.title()
			return res
		else:
			return self.prev.to_camel_case(fullName=True, lower=lower) + self.to_camel_case()
	
	def concatenate(self, upper=False, fullName=False):
		if self.prev is None or not fullName:
			res = ''
			for elem in self.words:
				if upper:
					res += elem.upper()
				else:
					res += elem
			return res
		else:
			return Name.concatenate(self.prev, upper=upper, fullName=True) + Name.concatenate(self, upper=upper)
	
	def to_word_list(self):
		if self.prev is None:
			return list(self.words)
		else:
			return Name.to_word_list(self.prev) + self.words
	
	def is_prefix_of(self, other):
		node = other
		while node is not None and node != self:
			node = node.prev
		return (node is not None)
	
	@staticmethod
	def find_common_parent(name1, name2):
		if name1.prev is None or name2.prev is None:
			return None
		elif name1.prev is name2.prev:
			return name1.prev
		else:
			commonParent = Name.find_common_parent(name1.prev, name2)
			if commonParent is not None:
				return commonParent
			else:
				return Name.find_common_parent(name1, name2.prev)


class ClassName(Name):
	def to_c(self, addBrackets=False):
		return self.to_camel_case(fullName=True)

	def from_c(self, name, **kargs):
		self.from_camel_case(name, **kargs)

	def translate(self, translator, **params):
		return translator.translate_class_name(self, **params)


class InterfaceName(ClassName):
	def translate(self, translator, **params):
		return translator.translate_interface_name(self, **params)


class EnumName(ClassName):
	def translate(self, translator, **params):
		return translator.translate_enum_name(self, **params)


class EnumeratorName(ClassName):
	def translate(self, translator, **params):
		return translator.translate_enumerator_name(self, **params)


class MethodName(Name):
	regex = re.compile('^\d+$')
	
	def __init__(self):
		self.overloadRef = 0
	
	def from_snake_case(self, name, namespace=None):
		Name.from_snake_case(self, name, namespace=namespace)
		if len(self.words) > 0:
			suffix = self.words[-1]
			if MethodName.regex.match(suffix) is not None:
				self.overloadRef = int(suffix)
				del self.words[-1]
	
	def to_c(self, addBrackets=False):
		suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else ''
		cName = self.to_snake_case(fullName=True) + suffix
		if addBrackets:
			cName += '()'
		return cName

	def from_c(self, **kargs):
		self.from_snake_case(**kargs)
	
	def translate(self, translator, **params):
		return translator.translate_method_name(self, **params)


class ArgName(Name):
	def to_c(self, addBrackets=False):
		return self.to_snake_case()
	
	def from_c(self, **kargs):
		self.from_snake_case(**kargs)

	def translate(self, translator, **params):
		return translator.translate_argument_name(self, **params)


class PropertyName(ArgName):
	def translate(self, translator, **params):
		return translator.translate_property_name(self, **params)


class NamespaceName(Name):
	def __init__(self, *params):
		Name.__init__(self)
		if len(params) > 0:
			self.words = params[0]
	
	def translate(self, translator, **params):
		return translator.translate_namespace_name(self, **params)


class Translator:
	instances = {}
	
	@staticmethod
	def get(langCode):
		try:
			if langCode == '':
				raise ValueError('Empty language code')
			if langCode not in Translator.instances:
				className = langCode + 'Translator'
				_class = globals()[className]
				Translator.instances[langCode] = _class()
			
			return Translator.instances[langCode]
		except KeyError:
			raise ValueError("Invalid language code: '{0}'".format(langCode))


class CTranslator(Translator):
	def translate_class_name(self, name, **params):
		return name.to_c()
	
	def translate_interface_name(self, name, **params):
		return name.to_c()
	
	def translate_enum_name(self, name, **params):
		return name.to_c()
	
	def translate_enumerator_name(self, name, **params):
		return name.to_c()
	
	def translate_method_name(self, name, **params):
		return name.to_c()
	
	def translate_namespace_name(self, name, **params):
		return None
	
	def translate_argument_name(self, name, **params):
		return name.to_c()
	
	def translate_property_name(self, name, **params):
		return name.to_c()


class JavaTranslator(Translator):
	def __init__(self):
		self.nsSep = '.'
		self._classMemberSep = None # None meams equal to self.nsSep
		self.keyWordEscapes = {}
		self.lowerMethodNames = True
		self.lowerNamespaceNames = True
		self.callbackInterfaceSuffix = 'Listener'

	@property
	def classMemberSep(self):
		return self._classMemberSep if self._classMemberSep is not None else self.nsSep;

	@classMemberSep.setter
	def classMemberSep(self, sep):
		self._classMemberSep = sep
	
	def translate_class_name(self, name, recursive=False, topAncestor=None):
		if name.prev is None or not recursive or name.prev is topAncestor:
			return name.to_camel_case()
		else:
			params = {'recursive': recursive, 'topAncestor': topAncestor}
			return name.prev.translate(self, **params) + self._get_separator(name.prev) + name.to_camel_case()
	
	def translate_interface_name(self, name, **params):
		translatedName = self.translate_class_name(name, **params)
		if translatedName.endswith('Cbs'):
			translatedName = translatedName[:-len('Cbs')] + self.callbackInterfaceSuffix
		return translatedName
	
	def translate_enum_name(self, name, **params):
		return self.translate_class_name(name, **params)
	
	def translate_enumerator_name(self, name, **params):
		return self.translate_class_name(name, **params)
	
	def translate_method_name(self, name, recursive=False, topAncestor=None):
		translatedName = name.to_camel_case(lower=self.lowerMethodNames)
		translatedName = self._escape_keyword(translatedName)
		
		if translatedName.endswith('Callbacks'):
			translatedName = translatedName[:-len('Callbacks')] + self.callbackInterfaceSuffix
		if name.prev is None or not recursive or name.prev is topAncestor:
			return translatedName
		else:
			params = {'recursive': recursive, 'topAncestor': topAncestor}
			return name.prev.translate(self, **params) + self._get_separator(name.prev) + translatedName
	
	def translate_namespace_name(self, name, recursive=False, topAncestor=None):
		translatedName = name.concatenate() if self.lowerNamespaceNames else name.to_camel_case()
		if name.prev is None or not recursive or name.prev is topAncestor:
			return translatedName
		else:
			params = {'recursive': recursive, 'topAncestor': topAncestor}
			return name.prev.translate(self, **params) + self._get_separator(name.prev) + translatedName

	def translate_argument_name(self, name):
		argname = name.to_camel_case(lower=True)
		return self._escape_keyword(argname)

	def translate_property_name(self, name):
		return self.translate_argument_name(name)

	def _escape_keyword(self, keyword):
		try:
			return self.keyWordEscapes[keyword]
		except KeyError:
			return keyword

	def _get_separator(self, prefix):
		if prefix is None:
			return ''
		elif type(prefix) in (ClassName, EnumName):
			return self.classMemberSep
		else:
			return self.nsSep


class SwiftTranslator(JavaTranslator):
	def __init__(self):
		JavaTranslator.__init__(self)
		self.nsSep = '.'
		self.keyWordEscapes = {'protocol' : 'proto'}
		self.callbackInterfaceSuffix = 'Delegate'

	def translate_enum_name(self, name, recursive=False, topAncestor=None):
		camelCaseName = name.to_camel_case()
		# Type is a reserved keyword within classes.
		if camelCaseName == 'Type':
			camelCaseName = 'Kind'
		
		if name.prev is None or not recursive or name.prev is topAncestor:
			enumName = camelCaseName
		else:
			params = {'recursive': recursive, 'topAncestor': topAncestor}
			enumName = name.prev.translate(self, **params) + self.nsSep + camelCaseName
		return enumName

	def translate_class_name(self, name, recursive=False, topAncestor=None):
		return name.to_camel_case()


class CppTranslator(JavaTranslator):
	def __init__(self):
		JavaTranslator.__init__(self)
		self.nsSep = '::'
		self.keyWordEscapes = {'new' : '_new'}


class CSharpTranslator(JavaTranslator):
	def __init__(self):
		JavaTranslator.__init__(self)
		self.keyWordEscapes = {
			'params' : 'parameters',
			'event'  : 'ev',
			'ref'    : 'reference',
			'value'  : 'val',
			'new'    : '_new'
		}
		self.lowerMethodNames = False
		self.lowerNamespaceNames = False
	
	def translate_method_name(self, name, recursive=False, topAncestor=None):
		translatedName = JavaTranslator.translate_method_name(self, name, recursive, topAncestor)
		subnResult = re.subn('(\.AddCallbacks|\.RemoveCallbacks)', '.SetListener' , translatedName)
		if subnResult[1] > 0:
			return subnResult[0]
		return translatedName
		
	def translate_property_name(self, name):
		return name.to_camel_case()
