import hashlib

# Sentinal value for nondeterministic dependencies
DISCORDIA = 'discordia'
OMNISCIENCE = 'omniscience'

def get_hash(s):
    if s is None:
        return None
    return hashlib.md5(s).digest()

def get_seq_hash(l):
    return get_hash(u','.join(unicode(i) for i in sorted(l)))

class Dependencies(set):
    def add(self, d):
        assert d in (DISCORDIA, OMNISCIENCE) or len(d) == 3, d
        set.add(self, d)
    
    def addRawDep(self, ename, pname, hsh):
        self.add((ename, pname, hsh))
    def makeUncacheable(self):
        self.add(DISCORDIA)
    def makeRestricted(self):
        self.add(OMNISCIENCE)
    def isRestricted(self):
        return OMNISCIENCE in self
    def addDep(self, propval):
        assert propval is not None
        self.addRawDep(propval.element.ename, propval.propname,
                       get_hash(propval.value))
    # TODO(xavid): rename and make sure this makes sense
    def addExistsDep(self, ename):
        self.addRawDep(ename, '__exists', None)
    def addNoPropvalDep(self, ename, pname):
        self.addRawDep(ename, pname, None)
    def addPropvalDep(self, e, pname):
        pv = e.get_propval(pname)
        if pv is None:
            self.addNoPropvalDep(e.ename, pname)
        else:
            self.addDep(pv)

    def addChildrenDep(self, e):
        self.addRawDep(e.ename, '__children', get_seq_hash(e.get_children()))
    def addParentDep(self, e):
        self.addRawDep(e.ename, '__parent', get_hash(e.get_parent_ename()))
    def addElementPropvalsDep(self, e):
        self.addRawDep(e.ename, '__propvals', get_seq_hash(e.list_props()))

    # A dependency that alwas gets re-evaled if tested, but is still cachable
    # for if the subversion revision hasn't changed.
    # TODO(xavid): It'd be cool to expand this to also pass if the element
    #              hasn't changed, with this defined by what would affect
    #              the yaml file in bazsvn.  Would require some model-specific
    #              version/logic.
    def addFragileDep(self, e):
        self.addRawDep(e.ename, '__fragile', None)
