

# modified from
# https://github.com/mewwts/addict

# The MIT License (MIT)
#
# Copyright (c) 2014 <NAME>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.



import copy


class adict(dict):

	def __init__(__self, *args, **kwargs):
		object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
		object.__setattr__(__self, '__key', kwargs.pop('__key', None))
		for arg in args:
			if not arg:
				continue
			elif isinstance(arg, dict):
				for key, val in arg.items():
					__self[key] = __self._hook(val)
			elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
				__self[arg[0]] = __self._hook(arg[1])
			else:
				for key, val in iter(arg):
					__self[key] = __self._hook(val)

		for key, val in kwargs.items():
			__self[key] = __self._hook(val)

	def __setattr__(self, name, value):
		if hasattr(self.__class__, name):
			raise AttributeError("'adict' object attribute "
								 "'{0}' is read-only".format(name))
		else:
			self[name] = value

	def __setitem__(self, name, value):
		super().__setitem__(name, value)
		try:
			p = object.__getattribute__(self, '__parent')
			key = object.__getattribute__(self, '__key')
		except AttributeError:
			p = None
			key = None
		if p is not None:
			p[key] = self
			object.__delattr__(self, '__parent')
			object.__delattr__(self, '__key')

	def __add__(self, other):
		if not self.keys():
			return other
		else:
			self_type = type(self).__name__
			other_type = type(other).__name__
			msg = "unsupported operand type(s) for +: '{}' and '{}'"
			raise TypeError(msg.format(self_type, other_type))

	@classmethod
	def _hook(cls, item):
		if isinstance(item, dict):
			return cls(item)
		elif isinstance(item, (list, tuple)):
			return type(item)(cls._hook(elem) for elem in item)
		return item

	def __getattr__(self, item):
		return self.__getitem__(item)

	def __getitem__(self, name):
		if name not in self:
			return self.__class__(__parent=self, __key=name)
		return super().__getitem__(name)

	def __delattr__(self, name):
		del self[name]

	def to_dict(self):
		base = {}
		for key, value in self.items():
			if isinstance(value, type(self)):
				base[key] = value.to_dict()
			elif isinstance(value, (list, tuple)):
				base[key] = type(value)(
					item.to_dict() if isinstance(item, type(self)) else
					item for item in value)
			else:
				base[key] = value
		return base

	def copy(self):
		return copy.copy(self)

	def deepcopy(self):
		return copy.deepcopy(self)

	def __deepcopy__(self, memo):
		other = self.__class__()
		memo[id(self)] = other
		for key, value in self.items():
			other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo)
		return other

	def update(self, *args, **kwargs):
		other = {}
		if args:
			if len(args) > 1:
				raise TypeError()
			other.update(args[0])
		other.update(kwargs)
		for k, v in other.items():
			if ((k not in self) or
				(not isinstance(self[k], dict)) or
				(not isinstance(v, dict))):
				self[k] = v
			else:
				self[k].update(v)

	def __getnewargs__(self):
		return tuple(self.items())

	def __getstate__(self):
		return self

	def __setstate__(self, state):
		self.update(state)

	def setdefault(self, key, default=None):
		if key in self:
			return self[key]
		else:
			self[key] = default
			return default

	def __repr__(self):
		if self.keys():
			m = max(map(len, list(self.keys()))) + 1
			return '\n'.join(['┣'+k.rjust(m) + ': ' + repr(v).replace('\n','\n┃'+' '*(m+2)) for k, v in self.items()])
		else:
			return self.__class__.__name__ + "()"

	def __xml__(self):
		from .xhtml import Elem
		x = Elem('div')
		t = x.put('table')
		for k, v in self.items():
			tr = t.put('tr')
			tr.put('td', text=k, style='font-weight:bold;vertical-align:top;')
			try:
				v_xml = v.__xml__()
			except AttributeError:
				tr.put('td').put('pre', text=repr(v))
			else:
				tr.append(v_xml)
		return x

	def _repr_html_(self):
		return self.__xml__().tostring()



# modified from
# https://github.com/mewwts/addict

# The MIT License (MIT)
#
# Copyright (c) 2014 <NAME>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.



import copy


class adict(dict):

	def __init__(__self, *args, **kwargs):
		object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
		object.__setattr__(__self, '__key', kwargs.pop('__key', None))
		for arg in args:
			if not arg:
				continue
			elif isinstance(arg, dict):
				for key, val in arg.items():
					__self[key] = __self._hook(val)
			elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
				__self[arg[0]] = __self._hook(arg[1])
			else:
				for key, val in iter(arg):
					__self[key] = __self._hook(val)

		for key, val in kwargs.items():
			__self[key] = __self._hook(val)

	def __setattr__(self, name, value):
		if hasattr(self.__class__, name):
			raise AttributeError("'adict' object attribute "
								 "'{0}' is read-only".format(name))
		else:
			self[name] = value

	def __setitem__(self, name, value):
		super().__setitem__(name, value)
		try:
			p = object.__getattribute__(self, '__parent')
			key = object.__getattribute__(self, '__key')
		except AttributeError:
			p = None
			key = None
		if p is not None:
			p[key] = self
			object.__delattr__(self, '__parent')
			object.__delattr__(self, '__key')

	def __add__(self, other):
		if not self.keys():
			return other
		else:
			self_type = type(self).__name__
			other_type = type(other).__name__
			msg = "unsupported operand type(s) for +: '{}' and '{}'"
			raise TypeError(msg.format(self_type, other_type))

	@classmethod
	def _hook(cls, item):
		if isinstance(item, dict):
			return cls(item)
		elif isinstance(item, (list, tuple)):
			return type(item)(cls._hook(elem) for elem in item)
		return item

	def __getattr__(self, item):
		return self.__getitem__(item)

	def __getitem__(self, name):
		if name not in self:
			return self.__class__(__parent=self, __key=name)
		return super().__getitem__(name)

	def __delattr__(self, name):
		del self[name]

	def to_dict(self):
		base = {}
		for key, value in self.items():
			if isinstance(value, type(self)):
				base[key] = value.to_dict()
			elif isinstance(value, (list, tuple)):
				base[key] = type(value)(
					item.to_dict() if isinstance(item, type(self)) else
					item for item in value)
			else:
				base[key] = value
		return base

	def copy(self):
		return copy.copy(self)

	def deepcopy(self):
		return copy.deepcopy(self)

	def __deepcopy__(self, memo):
		other = self.__class__()
		memo[id(self)] = other
		for key, value in self.items():
			other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo)
		return other

	def update(self, *args, **kwargs):
		other = {}
		if args:
			if len(args) > 1:
				raise TypeError()
			other.update(args[0])
		other.update(kwargs)
		for k, v in other.items():
			if ((k not in self) or
				(not isinstance(self[k], dict)) or
				(not isinstance(v, dict))):
				self[k] = v
			else:
				self[k].update(v)

	def __getnewargs__(self):
		return tuple(self.items())

	def __getstate__(self):
		return self

	def __setstate__(self, state):
		self.update(state)

	def setdefault(self, key, default=None):
		if key in self:
			return self[key]
		else:
			self[key] = default
			return default

	def __repr__(self):
		if self.keys():
			m = max(map(len, list(self.keys()))) + 1
			return '\n'.join(['┣'+k.rjust(m) + ': ' + repr(v).replace('\n','\n┃'+' '*(m+2)) for k, v in self.items()])
		else:
			return self.__class__.__name__ + "()"

	def __xml__(self):
		from .xhtml import Elem
		x = Elem('div')
		t = x.put('table')
		for k, v in self.items():
			tr = t.put('tr')
			tr.put('td', text=k, style='font-weight:bold;vertical-align:top;')
			try:
				v_xml = v.__xml__()
			except AttributeError:
				tr.put('td').put('pre', text=repr(v))
			else:
				tr.append(v_xml)
		return x

	def _repr_html_(self):
		return self.__xml__().tostring()

# modified from # https://github.com/mewwts/addict # The MIT License (MIT) # # Copyright (c) 2014 <NAME> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE.