from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
from direct.distributed.GridParent import GridParent
from pandac.PandaModules import EmbeddedValue

class GridChild:
    """
    Any object that expects to be parented to a grid should inherit from this.
    It works with GridParent to manage its grid cell hierarchy in the scenegraph.
    """
    def __init__(self):
        try:
            self.__initiallized
        except AttributeError:
            self._gridParent = None

            self._gridInterestEnabled = False
            self._gridInterests = {}

    def delete(self):
        self.__setGridParent(None)
        self.enableGridInterest(False)

    @report(types = ['args'], dConfigParam = 'smoothnode')
    def setGridCell(self, grid, zoneId):
        if not hasattr(self,'getParent'):
            return
        if grid is None:
            self.__setGridParent(None)
            self.__clearGridInterest()
        else:
            if not self._gridParent:
                self.__setGridParent(GridParent(self))

            # Does the (wrt)ReparentTo() operation
            self._gridParent.setGridCell(grid, zoneId)

            # Moves the grid interest along with this child
            self.updateGridInterest(grid, zoneId)

    def updateGridInterest(self, grid, zoneId):
        # add additional grid interests (sometimes we want more
        # than just one)
        #if self._gridInterestEnabled:
        self.__setGridInterest(grid, zoneId)
        
    def enableGridInterest(self, enabled = True):
        self._gridInterestEnabled = enabled
        if enabled and self.isOnAGrid():
            # enable all grid interests I may have
            for currGridId, interestInfo in self._gridInterests.items():
                currGrid = getBase().getRepository().doId2do.get(currGridId)
                if currGrid:
                    self.__setGridInterest(currGrid, interestInfo[1])
                else:
                    self.notify.warning("unknown grid interest %s"%currGridId)
        else:
            for currGridId, interestInfo in self._gridInterests.items():
                self.cr.removeTaggedInterest(interestInfo[0])
            #self.__clearGridInterest()
            pass

    def isOnAGrid(self):
        return self._gridParent is not None

    def getGrid(self):
        if self._gridParent:
            return self._gridParent.getGrid()
        else:
            return None

    def getGridZone(self):
        if self._gridParent:
            return self._gridParent.getGridZone()
        else:
            return None
        
    def __setGridParent(self, gridParent):
        if self._gridParent and self._gridParent is not gridParent:
            self._gridParent.delete()
        self._gridParent = gridParent

    
    def __setGridInterest(self, grid, zoneId):
        assert not self.cr.noNewInterests()
        if self.cr.noNewInterests():
            self.notify.warning(
                'startProcessVisibility(%s): tried to open a new interest during logout'
                % self.doId)
            return

        gridDoId = grid.getDoId()
        existingInterest = self._gridInterests.get(gridDoId)
        if self._gridInterestEnabled:
            if existingInterest and existingInterest[0]:
                self.cr.alterInterest(existingInterest[0],
                                      grid.getDoId(), zoneId)
                existingInterest[1] = zoneId
            else:
                newInterest = self.cr.addTaggedInterest(gridDoId, zoneId,
                                                        self.cr.ITAG_GAME,
                                                        self.uniqueName('gridvis'))
                self._gridInterests[gridDoId] = [newInterest,zoneId]
        else:
            # indicate we want this grid interest once gridInterestEnabled is True
            if game.process == 'client':
                # we only care about interests on the client
                self._gridInterests[gridDoId] = [None,zoneId]

    def getGridInterestIds(self):
        return self._gridInterests.keys()

    def getGridInterestZoneId(self,gridDoId):
        return self._gridInterests.get(gridDoId,[None,None])[1]

    def __clearGridInterest(self):
        if self._gridInterestEnabled:
            for currGridId, interestInfo in self._gridInterests.items():
                self.cr.removeTaggedInterest(interestInfo[0])
        self._gridInterests = {}






class SmoothGridChild(GridChild):
    """
    SmoothNodes have a special requirement in that they need to send
    their current cell along with their telemetry data stream. This
    allows the distributed receiving objects to update their grid parent
    according to this value, rather than the setLocation() data.

    Use this instead of GridNode when you expect this object to send its
    telemetry data out.
    """
    def __init__(self):
        GridChild.__init__(self)
        assert isinstance(self, DistributedSmoothNodeBase), \
               'All GridChild objects must be instances of DistributedSmoothNodeBase'

    @report(types = ['args'], dConfigParam = 'smoothnode')
    def setGridCell(self, grid, zoneId):
        GridChild.setGridCell(self, grid, zoneId)
        if grid and self.isGenerated(): # we get our cnode in DistributedSmoothNodeBase.generate()
            self.cnode.setEmbeddedVal(zoneId)

    @report(types = ['args'], dConfigParam = 'smoothnode')
    def transformTelemetry(self, x, y, z, h, p, r, e):
        # We don't really need to transform telemetry, but
        # we do update our grid cell such that the new 
        # telemetry is correct now.
        # We do this instead of overriding setSmPosHprE()
        # because we're a mixin class.
        if self.isOnAGrid():
            self.setGridCell(self.getGrid(), e) # causes a wrtReparent() which updates 
                                                # all previous smooth positions
        return x, y, z, h, p, r
