"""
provides node functionality for the eventable odml types
Document, Section, Property and Value

additionally implements change notifications up to the corresponding section
"""
from .. import event

class RootNode(object):
    @property
    def children(self):
        return self._sections
        
    def from_path(self, path):
        child = self.children[path[0]]
        if len(path) == 1:
            return child
        return child.from_path(path[1:])

    def to_path(self):
        return ()
    
    def path_to(self, child):
        """return the path from this node to its direct child *child*"""
        return (self._sections.index(child),)

class ParentedNode(RootNode):
    def to_path(self):
        return self.parent.to_path() + self.parent.path_to(self)
    
    def successor(self):
        return self.parent.children[self.position + 1]
    
    def next(self):
        """
        returns the next section following in this section's parent's list of sections
        returns None if there is no further element available

        i.e.:
            parent
              sec-a (<- self)
              sec-b

        will return sec-b
        """
        try:
            return self.successor()
        except IndexError:
            return None
    
    @property
    def position(self):
        return self.parent.path_to(self)[-1]
        
    @property
    def parent(self):
        return self._parent

class SectionNode(ParentedNode):
    """
    SectionNodes are special as they wrap two types of sub-nodes:
    
    * SubSections (path = (0, idx))
    * Properties  (path = (1, idx))
    """
    def from_path(self, path):
        assert len(path) > 1
        
        if path[0] == 0: # sections
            return super(SectionNode, self).from_path(path[1:])
        
        # else: properties
        child = self._props[path[1]]

        if len(path) == 2:
            return child
        return child.from_path(path[2:])
        
    
    def path_to(self, child):
        if isinstance(child, event.Property):
            return (1, self._props.index(child))
        return (0, self._sections.index(child))

        
class PropertyNode(ParentedNode):
    @property
    def parent(self):
        """returns the parent section of this Property"""
        return self._section
    
    @property
    def children(self):
        return self._values
    
    def successor(self):
        return self.parent._props[self.position + 1]
    
    def path_to(self, child):
        return (self.values.index(child),)

class ValueNode(ParentedNode):
    @property
    def parent(self):
        return self._property
    
    def path_from(self, path):
        raise TypeError("Value objects have no children")
    
    def path_to(self, child):
        raise TypeError("Value objects have no children")

#TODO? provide this externally?
class Document(event.Document, RootNode): pass
class Value(event.Value, ValueNode): pass
class Property(event.Property, PropertyNode): pass
class Section(event.Section, SectionNode): pass

def on_value_change(value, **kwargs):
    prop = value._property
    if prop is not None:
        prop._Changed(prop, value=value, value_pos=value.position, **kwargs)
    
Value._Changed += on_value_change

def on_property_change(prop, **kwargs):
    sec = prop._section
    if sec is not None:
        sec._Changed(sec, prop=prop, prop_pos=prop.position, **kwargs)
    
Property._Changed += on_property_change

# TODO on_section_change(sec, **kwargs)

@@ -192,24 +192,672 @@
 port event%0A%0A
+def identity_index(obj, val):%0A    %22%22%22%0A    same as obj.index(val) but will only return a position%0A    if the object val is found in the list%0A    %0A    i.e.%0A    %3E%3E%3E identity_index(%5B%221%22%5D, %221%22)%0A    will raise an ValueError because the two strings%0A    are different objects with the same content%0A%0A    %3E%3E%3E a = %221%22%0A    %3E%3E%3E identity_index(%5Ba%5D, a)%0A    0%0A%0A    The difference to obj.index comes apparent here as well:%0A    %3E%3E%3E a=%221%22, b=%221%22%0A    %3E%3E%3E identity_index(%5Ba,b%5D, b)%0A    1%0A    %3E%3E%3E %5Ba,b%5D.index(b)%0A    0%0A    %22%22%22%0A    for i, v in enumerate(obj):%0A        if v is val: return i%0A%0A    raise ValueError(%22%25s does not contain the item %25s%22 %25 (repr(obj), repr(val)))%0A%0A
 class RootNo
@@ -1269,24 +1269,39 @@
      return 
+(identity_index
 (self._secti
@@ -1299,31 +1299,26 @@
 lf._sections
-.index(
+, 
 child),)%0A%0Acl
@@ -1455,32 +1455,134 @@
 uccessor(self):%0A
+        print self, %22position: %22, self.position, identity_index(self.parent, self), %22in%22, self.parent%0A
         return s
@@ -2832,16 +2832,31 @@
 urn (1, 
+identity_index(
 self._pr
@@ -2858,23 +2858,18 @@
 f._props
-.index(
+, 
 child))%0A
@@ -2887,16 +2887,31 @@
 urn (0, 
+identity_index(
 self._se
@@ -2916,23 +2916,18 @@
 sections
-.index(
+, 
 child))%0A
@@ -3297,16 +3297,31 @@
  return 
+(identity_index
 (self.va
@@ -3324,23 +3324,18 @@
 f.values
-.index(
+, 
 child),)

use an identity index to find the position (the default .index() function uses the custom __cmp__ for objects)