from fs import Path
from func_prototypes import typed
from delnone import delnone

class SnapshotItem:
  #TODO remove ref
  #TODO remove splitter
  #TODO remove reverser
  #TODO is snap requred?
  def __init__(self, path, type, ref=None, csum=None, splitter=None, reverser=None, snap=None):
    assert type in ["link", "dir"], type
    assert isinstance(path, basestring)
    assert (ref is None) or isinstance(ref, basestring)
    assert (snap is None) or isinstance(snap, basestring)
    if type == "link":
      if ref is not None and csum is not None:
        raise ValueError("Either ref or csum should be specified for links")
      elif ref:
        csum = reverser(ref) #TODO remove
      elif csum:
        ref = splitter(csum) #TODO remove
      else:
        raise ValueError("Either ref or csum are required for links")
    self._path = path
    self._type = type
    self._ref = ref
    self._csum = csum
    self._snap = snap

  def __cmp__(self, other):
    assert other is None or isinstance(other, SnapshotItem)
    if other is None:
      return -1
    self_path = Path(self._path)
    other_path = Path(other._path)
    return cmp(self_path, other_path)

  def get_tuple(self):
    if self._ref:
      ref = self._ref
    else:
      ref = None
    return (self._path, self._type, ref)

  def get_dict(self):
    return delnone(dict(path=self._path,
            type=self._type,
            csum=self._csum,
            snap=self._snap))

  def is_dir(self):
    return self._type == "dir"

  def is_link(self):
    return self._type == "link"

  def csum(self):
    assert self._type == "link", "Encountered unexpected type %s in SnapshotItem for path %s" % (self._type, self._path)
    return self._csum

  def __unicode__(self):
    return u'<%s %s %s>' % (self._type, self._path, self._ref)

  def __str__(self):
    return unicode(self).encode('utf-8')

class Snapshot:
  pass

class TreeSnapshot(Snapshot):
  def __init__(self, root, udd, exclude, reverser):
    assert isinstance(root, Path)
    self.root = root
    self.udd = udd
    self.exclude = exclude
    self.reverser = reverser

  def __iter__(self):
    root = self.root
    udd = self.udd
    exclude = self.exclude
    def tree_snap_iterator():
      last_path = None # Note: last_path is just used to debug snapshot order issues. Remove once we have confidence.
      for path, type_ in root.entries(exclude):
        if last_path:
          assert last_path < path, "Order error: %s < %s" % (last_path, Path)
        last_path = path
        if type_ == "link":
          ud_str = self.reverser(path.readlink().relative_to(udd))
        elif type_ == "dir":
          ud_str = None
        elif type_ == "file":
          continue
        else:
          raise ValueError("Encounted unexpected type %s for path %s" % (type_, entry))
        yield SnapshotItem(path.relative_to(root), type_, ud_str)
    return tree_snap_iterator()

class KeySnapshot(Snapshot):
  def __init__(self, data, name, splitter=None, reverser=None):
    self.data = data
    self._splitter = splitter
    self._reverser = reverser
    self._name = name

  def __iter__(self):
    def key_snap_iterator():
      for item in self.data:
        if isinstance(item, list):
          assert len(item) == 3
          (path_str, type_, ref) = item
          parsed = SnapshotItem(path_str, type_, self._reverser(ref))
        elif isinstance(item, dict):
          parsed = SnapshotItem(**item)
        yield parsed
    return iter(sorted(key_snap_iterator()))

class SnapDelta:
  REMOVED='removed'
  DIR='dir'
  LINK='link'
  _modes = [REMOVED, DIR, LINK]
  def __init__(self, path, mode, csum):
    assert isinstance(path, basestring)
    assert isinstance(mode, basestring) and mode in self._modes
    if mode == self.LINK:
      assert csum is not None and csum.count("/") == 0
    else:
      assert csum is None
    self._path = path
    self._mode = mode
    self._csum = csum

  def __str__(self):
    return "%s %s %s" % (self._mode, self._path, self._csum)

  def __repr__(self):
    return str(self)


@@ -100,27 +100,8 @@
 em:%0A
-  #TODO remove ref%0A
   #T
@@ -204,18 +204,9 @@
 type
-, ref=None
+_
 , cs
@@ -271,16 +271,17 @@
 ert type
+_
  in %5B%22li
@@ -293,24 +293,25 @@
 %22dir%22%5D, type
+_
 %0A    assert 
@@ -343,64 +343,8 @@
 ng)%0A
-    assert (ref is None) or isinstance(ref, basestring)%0A
     
@@ -404,24 +404,25 @@
 %0A    if type
+_
  == %22link%22:%0A
@@ -434,36 +434,16 @@
  if 
-ref is not None and 
 csum is 
 not 
@@ -434,28 +434,24 @@
  if csum is 
-not 
 None:%0A      
@@ -466,39 +466,29 @@
 ValueError(%22
-Either ref or c
+check
 sum should b
@@ -515,207 +515,8 @@
 s%22)%0A
-      elif ref:%0A        csum = reverser(ref) #TODO remove%0A      elif csum:%0A        ref = splitter(csum) #TODO remove%0A      else:%0A        raise ValueError(%22Either ref or csum are required for links%22)%0A
     
@@ -558,28 +558,9 @@
 type
-%0A    self._ref = ref
+_
 %0A   
@@ -861,75 +861,8 @@
 f):%0A
-    if self._ref:%0A      ref = self._ref%0A    else:%0A      ref = None%0A
     
@@ -893,19 +893,20 @@
 ._type, 
-ref
+csum
 )%0A%0A  def
@@ -1403,19 +1403,20 @@
 , self._
-ref
+csum
 )%0A%0A  def

Remove ref.