"""problema:

cuando se renombra un archivo que esta siendo observado, las rutas que estan en el mapa de observacion rutas:wd y wd:rutas quedan obsoletas.

solucion:

1. hacer una cuestion que me permita mapear wds:elementosdeunarbol.  cuando se renombre un archivo, tomar el elemento y renombrarlo (lo cual deberia renombrar el key en el padre si es que lo tiene).  estos elementos deben tener un get_path para obtener la ruta certera al archivo.
debe haber una funcion de comodidad que me permita hacer:
elemento = get_path("/home/rudd-o/niseque/nisecomo") y solito los coloque en el arbol.

2. watchear todos los directorios padre (de forma shadow, que esos eventos no se permeen al usuario), para poder observar cuando alguno se renombro.

en el modulo inotify

def watch(path,mask):

	element = tree.get(path)
	element.watch(mask)
	# this should request shadowing on all parent nodes
	
def ignore(absolute_path):
	element = tree.get(path)
	element.ignore()
	# this should actually ignore the element only if no children are being watched, or else mark the element as shadowed
	# but this is going to be expensive in terms of reference counting and shit
	
def dispatch_event(event):

	wd = event.wd
	element = tree.lookup_wd(wd)
	
	if element.is_being_watched():
		if event is moved_from: moved_from = event.filename
		if event is moved_to:
			moved_to = event.filename
			element[moved_from].set_name(moved_to)
			destination_element = tree.get(
		if event is deleted:
			element.remove_child(event.filename)
		dispatch event as appropriate
		
	if element.is_being_shadowed():
		if moved_to: rename element child that is involved
		if ignored: element.stop_watching()

"""

class TreeNode:

	def __init__(self):
		self.parent = None
		self.children = []

	def get_root(self):
		if self.parent is None: return self
		return self.parent.get_root()

class NamedNodeList(dict):

	def __init__(self,owner):
		self.owner = owner
	
	def append(self,node):
		self[node.name] = node
		
	def __setitem__(self,key,value):
		if self.has_key(value.name):
			raise Exception, "A node with name %s already exists"%value.name
		value._reparent(new_parent=self.owner,being_added=True)
		dict.__setitem__(self,key,value)

	def __contains__(self,node):
		return self.has_key(node.name)
		
	def remove(self,node):
		del self[node.name]
	
	def __delitem__(self,key):
		self[key]._reparent(new_parent=None,being_deleted=True)
		dict.__delitem__(self,key)

	def __iter__(self): return self.itervalues()
		
	def __str__(self):
		listt = [str(a) for a in self]
		return "[" + ",".join(listt)  + "]"
		
	def _change_name(self,old_name,new_name):
		if self.has_key(new_name):
			raise Exception, "A node with name %s already exists"%new_name
		value = self[old_name]
		dict.__delitem__(self,old_name)
		dict.__setitem__(self,new_name,value)


class NamedTreeNode(TreeNode):
	
	def __str__(self):
		return "Node %s"%repr(self.name)
	
	def __init__(self,name):
		if not type(name) is str or len(name) == 0: raise ValueError,name
		TreeNode.__init__(self)
		self.children = NamedNodeList(self)
		self.name = name

	def _child_node_renamed(self,old_name,new_name):
		self.children._change_name(old_name,new_name)

	def rename(self,new_name):
		if not type(new_name) is str or len(new_name) == 0: raise ValueError,new_name
		old_name = self.name
		if self.parent is not None:
			self.parent._child_node_renamed(old_name,new_name)
		self.name = new_name

	def _reparent(self,new_parent,being_deleted=False,being_added=False):
		if self.parent is new_parent: return
		old_parent = self.parent
		
		if old_parent and not being_deleted: old_parent.children.remove(self)
		self.parent = new_parent
		if new_parent and not being_added: new_parent.children.append(self)

	def reparent(self,new_parent):
		self._reparent(new_parent)
	
	def __repr__(self):
		hexid = "%#x"%id(self)
		return "<"+str(self.__class__)+" %s instance at "%self.name + hexid  + ">"
		
	def get_ancestors(self):
		start_node = self.parent
		ancestors = []
		while start_node is not None:
			ancestors.append(start_node)
			start_node = start_node.parent
		return ancestors

	def get_descendents(self):
		descendents = []
		for a in self.children:
			descendents.append(a)
			descendents.extend(a.get_descendents())
		return descendents

class FSTreeNode(NamedTreeNode):
	
	def __init__(self,name):
		if "/" in name: raise ValueError, name
		NamedTreeNode.__init__(self,name)
	
	def rename(self,new_name):
		if "/" in new_name: raise ValueError, new_name
		NamedTreeNode.rename(self,new_name)

	def get_path(self):
		if self.parent:
			parentpath = self.parent.get_path()
			if parentpath.endswith("/"): slashd = ""
			else: slashd = "/"
			return parentpath + slashd + self.name
		else: return self.name
		
	def __str__(self):
		return "Node %s"%repr(self.get_path())
		
	def __repr__(self):
		hexid = "%#x"%id(self)
		return "<"+str(self.__class__)+" %s instance at "%self.get_path() + hexid + ">"
	
	def dump_tree(self):
		print self.get_path(),
		if hasattr(self,"wd"): print self.wd
		else: print ""
		for a in self.children: a.dump_tree()
	
	def lookup(self,path,autocreate=False):
		assert type(path) is str
		#for convenience, we'll return self when "" is passed as path
		if path == "": return self
		try:
			if path[0] == "/": return self.get_root().lookup(path[1:],autocreate)
			if "/" not in path:
				try: child = self.children[path]
				except KeyError:
					if not autocreate: raise
					child = FSTreeNode(path)
					self.children.append(child)
				return child
			first,rest = path.split("/",1)
			try: child = self.children[first]
			except KeyError:
				if not autocreate: raise
				child = FSTreeNode(first)
				self.children.append(child)
			return child.lookup(rest,autocreate)
		except KeyError: raise LookupError,path


class FSTreeRoot(FSTreeNode):
	def __init__(self):
		FSTreeNode.__init__(self,"dummyname")
		self.name = ""

	def get_path(self):
		return "/"
		
	def rename(self,new_name):
		raise NotImplementedError, "Roots cannot be renamed"

# b = FSTreeNode("hola")
# b.children.append(FSTreeNode("Manuel"))
# b.children.append(FSTreeNode("Que"))
# b.children.append(FSTreeNode("Fue"))
# b.children.append(FSTreeNode("loco"))
# 
# b.dump_tree()
# 
# f = b.get_by_path("Manuel")
# print f 

def build_test_tree():
	import os
	root_node = FSTreeRoot()
	rootdir = "/home/rudd-o/tmp"
	for root,dirs,files in os.walk(rootdir):
		thisroot = root[len(rootdir):]
		if thisroot.startswith("/"): thisroot = thisroot[1:]
		for name in dirs: root_node.lookup(thisroot+"/"+name,autocreate=True)
	return root_node
