
collisionDetectionLst = {
    'intersect' :    lambda a,b : (a.x-b.x)**2+(a.y-b.y)**2 <= (a.r+b.r)**2,
    'notIntersect' : lambda a,b : (a.x-b.x)**2+(a.y-b.y)**2 >  (a.r+b.r)**2,
    'inside' :       lambda a,b : (a.x-b.x)**2+(a.y-b.y)**2 <= (b.r-a.r)**2,
    'notInside' :    lambda a,b : (a.x-b.x)**2+(a.y-b.y)**2 >  (b.r-a.r)**2,
    'alwaysFalse' :  lambda a,b : False
}

class Bubble(object):
    def __init__(self,x,y,r,collisionDetection,tag,child):
        self.x=x
        self.y=y
        self.r=r
        self._coll = collisionDetection
        self.tag=tuple(tag)
        self._child=tuple(child)

    def getChild(self):
        for child in self._child:
            yield Bubble(child.x+self.x,\
                         child.y+self.y,\
                         child.r,\
                         child._coll,\
                         child.tag,\
                         child._child)

    def collideWith(self,other):
        coll = collisionDetectionDict[self._coll]
        if coll(self,other):
            for child in self.getChild():
                ret = child.collideWith(self.other)
                if ret:
                    return ret                    
            return self.tag
        return None

def fromStruct(struct):
    return Bubble(struct['x'] if 'x' in struct else 0,\
                  struct['y'] if 'y' in struct else 0,\
                  struct['r'] if 'r' in struct else 1,\
                  struct['coll'] if 'coll' in struct else 'alwaysFalse',\
                  struct['tag'] if 'tag' in struct else [],\
                  [fromStruct(sub) for sub in struct['child']] if 'child' in struct else [],\
                  )

if __name__ == __main__:
    asteroidStruct = {
        'x' : 50, 'y' : 50, 'r' : 25,
        'coll' : 'intersect',
        'tag' : ['gravityWell'],
        'child' : [{
            'x' : 50, 'y' : 50, 'r' : 15,
            'coll' : 'intersect',
            'tag' : ['asteoidItself'],
            'child' : [{
                'x' : 45, 'y' : 50, 'r' : 5,
                'coll' : 'inside',
                'tag' : ['crater']
                }]
            }]
    }
    probeStruct =              { 'x' : 1, 'y' : 1, 'r' : 1}
    probeStructInGravity =     { 'x' : 10, 'y' : 10, 'r' : 1}
    probeStructInAsteroid =    { 'x' : 15, 'y' : 15, 'r' : 1}
    probeStructInCrater =      { 'x' : 45, 'y' : 50, 'r' : 1}
    probeStructLeftOfCrater =  { 'x' : 40, 'y' : 50, 'r' : 1}
    probeStructRightOfCrater = { 'x' : 50, 'y' : 50, 'r' : 1}

    asteroid = fromStruct(asteroidStruct)
    probe = fromStruct(probeStruct)
    inGravity = fromStruct(probeStructInGravity)
    inAsteroid = fromStruct(probeStructInAsteroid)
    inCrater = fromStruct(probeStructInCrater)
    leftOfCrater = fromStruct(probeStructLeftOfCrater)
    rightOfCrater = fromStruct(probeStructRightOfCrater)
