def startDrag(self,event):
         self.dragRect = DragBox(event.pos)
class Viewport(object):  #SHOULD PROBABLY INHERIT FROM DRAWABLE OBJECT
    """
    Acts as a "window" into the world it contains. Includes deadzone dimensions
    so that it can be scrolled with the mouse.
    
    @param world: the world the viewport can see
    @param scrollLoc: (x,y) #World coordinates of the corner of the viewport
    @param loc: (x,y) #Corner of the viewport in screen coordinates
    @param size: (width,height) #Dimensions in screen coordinates
    @param mouse: the mouse controlling it
    @param deadZoneRect:
    @param selectedEntities:
    @param selector: a selection rectangle that is activated and drawn when necessary #FIXME: NOT YET IMPLEMENTED
    
    @param clientID: identifying string or number given by the server
    when a client is created
    """
    
    def __init__(self,world,manager,scrollLoc,screenPos,size,clientID):
        self.world = world
        self.manager = manager
        self.scrollLoc = scrollLoc
        self.cartScrollLoc = specialMath.isoToCart(scrollLoc)
        self.loc = screenPos
        self.size = size
        self.rect = pygame.Rect(screenPos,size)
        self.hud=None
        self.clientID = clientID
        
        if world is not None:
            self.minimap = MiniMap(self.world,clientID=clientID)
            self.worldSize=self.world.grid.getCartGridDimensions()
        else:
            self.minimap = None
        
        #FIXME - SHOULD COME FROM A CONFIG FILE
        self.scrollSensitivity=.001
        
        self.initDeadZoneBasedOnSize()
        self.surface = pygame.Surface(size)
        self.surface.set_clip(((0,0),size))
        self.scrollSpeed = [0,0]

        self.selectedEntities = []
                
        self.viewportEntities = []
        self.myViewportEntities = []
        
        self.quickSelect = {}
        for i in xrange(pygame.K_0,pygame.K_9+1):
            self.quickSelect[i] = pygame.sprite.Group()
        
        self.dragRect = None
        
        self.currentMenu = None
        
        from ContextualMenu import getCGDcontextualMenu
        self.contextualMenu = getCGDcontextualMenu()
        
        self._selectedEntitiesChanged = False
        
        self.gameOver = False
    
    def setClientID(self,clientID):
        self.clientID = clientID
        self.minimap.setClientID(clientID)
    
    def setGameOver(self):
        self.gameOver = True
        
    def initDeadZoneBasedOnSize(self):
        #CURRENT IMPLEMENTATION IS FAKE
        offset = int(0.3*float(self.size[0]))
        deadZoneSize = (self.size[0]-offset,self.size[1]-offset)
        self.deadZoneRect = pygame.Rect((0, 0), deadZoneSize)
        self.deadZoneRect.center = (self.size[0]/2.0,\
                                    self.size[1]/2.0)

    def setScrollSpeed(self,mousePos):
        deadZoneHeight,deadZoneWidth = self.deadZoneRect.size
        relMousePos = (mousePos[0]-self.loc[0],mousePos[1]-self.loc[1])
        if self.rect.collidepoint(mousePos) and \
            not self.deadZoneRect.collidepoint(relMousePos) and \
            not self.minimap.hasFocus(mousePos):
            dx = (mousePos[0]-self.deadZoneRect.center[0]-self.loc[0])
            dy = (mousePos[1]-self.deadZoneRect.center[1]-self.loc[1])

            self.scrollSpeed[0]=dx*self.scrollSensitivity
            self.scrollSpeed[1]=dy*self.scrollSensitivity
        else:
            self.scrollSpeed = [0,0]
    
    def initiateActionEvent(self,event):
        pos = event.pos

        if self.minimap.rect.collidepoint(pos):
            mapClickPoint = self.minimap.clickToGridPos(pos)
            if mapClickPoint is not None:
                return 
            else:            
                cartPos = specialMath.isoToCart(pos)
                destCart = self.cartScrollLoc[0] + cartPos[0], \
                            self.cartScrollLoc[1] + cartPos[1]
        else:
            cartPos = specialMath.isoToCart(pos)
            destCart = self.cartScrollLoc[0] + cartPos[0], \
                        self.cartScrollLoc[1] + cartPos[1]
        
        clicked = specialMath.closestEntity(self.viewportEntities,pos)
        
        if clicked:
            drawRect = clicked.rect.move(clicked.drawOffset)
            drawRect.center = specialMath.cartToIso(drawRect.center)
        
            if not drawRect.collidepoint(pos):
                clicked = None
        
        if clicked is not None:
            self.currentMenu = self.contextualMenu.getMenu(self.selectedEntities,clicked)
        else:
            self.currentMenu = self.contextualMenu.getMenu(self.selectedEntities,WayPoint(*destCart))
            
        if self.currentMenu is not None:
            self.currentMenu.open(event.pos)
        
    def completeActionEvent(self,event):
        attacking = False
        pos = event.pos
        
        # Performs action indicated by menu if menu is visible
        # and exists.  Otherwise, the menu reference is destroyed.
        if self.currentMenu is not None and self.currentMenu.visible:
            self.selectMenu(pos)
            return # Do not do anything else if the menu is selected
        else:
            self.currentMenu = None
        
        # Sets destination in cartesian coordinates
        # Handles minimap clicks
        if self.minimap.rect.collidepoint(pos):
            mapClickPoint = self.minimap.clickToGridPos(pos)
            if mapClickPoint is not None:
                destCart = mapClickPoint
            else:
                cartPos = specialMath.isoToCart(pos)
                destCart = (self.cartScrollLoc[0] + cartPos[0])%self.worldSize[0], \
                            (self.cartScrollLoc[1] + cartPos[1])%self.worldSize[1]
        else:
            cartPos = specialMath.isoToCart(pos)
            destCart = (self.cartScrollLoc[0] + cartPos[0])%self.worldSize[0], \
                        (self.cartScrollLoc[1] + cartPos[1])%self.worldSize[1]
        
        # Determines closest entity to a click
        clicked = specialMath.closestEntity(self.viewportEntities,pos)
        
        if clicked:
            drawRect = clicked.rect.move(clicked.drawOffset)
            drawRect.center = specialMath.cartToIso(drawRect.center)
        
            if not drawRect.collidepoint(pos):
                clicked = None
        
        # clicked is now either None or the closest Entity to the click
        if clicked:
            for selected in self.selectedEntities:
                attacking=True
                if isinstance(selected,Unit):
                    selected.initAction(clicked)
       
        if not attacking:
            eCenter = specialMath.centerOfEntityList(self.selectedEntities, self.worldSize)
            for entity in self.selectedEntities:
                if not entity.status==Locals.MOVING:
                    entity.dest=entity.realCenter
                if entity.movable: entity.status=Locals.MOVING
                dx = entity.rect.center[0] - eCenter[0]
                dy = entity.rect.center[1] - eCenter[1]
                newLoc = (dx+destCart[0],dy+destCart[1])
                entity.addToPath(newLoc)
  
    def startDrag(self,event):
            self.dragRect = DragBox(event.pos)
        
    def continueDrag(self,event):
        if self.dragRect is not None:
            self.dragRect.update(event.curr)
            if self.dragRect.visible:
                self.drawDragRect()
    
    def completeDrag(self,event):
        if self.dragRect is not None:
            self.dragRect.update(event.curr)
            if self.dragRect.visible:
                self.drawDragRect()
        self.dragSelect(event)
        self.dragRect = None
    
    def drawMiniMap(self):
        if self.minimap is not None:
            self.minimap.update(self.cartPointTupleOfScreen())
            self.minimap.draw(self.surface)
    
    def drawDragRect(self):   
        """
        Draws the drag rectangle from a mouse drag, if not None.
        """
        if not self.dragRect == None:
            self.dragRect.draw(self.surface)
    
    def dragSelect(self,event):
        """
        Fill this in.
        """
        if self.dragRect is not None:
            start = event.start
            end = event.curr
            
            if isinstance(event,Event.DragCompletedEvent):
                for e in self.selectedEntities:
                    e.deselect()
                    self._selectedEntitiesChanged = True
                self.selectedEntities = []
            #else: pass # if it is an Event.AddDragCompletedEvent, do
            # # not deselect
            
            #if self.dragRect.isOffScreen(self.size):
            #    searchList = self.world.allEntities.values()
            #else:
            #    searchList = self.viewportEntities
            searchList = self.myViewportEntities
            
            for entity in searchList:
            
                drawRect = entity.rect.move(entity.drawOffset)
                drawRect.center = specialMath.cartToIso(drawRect.center)
                
                selectRect=entity.getSelectionRect(drawRect)
            
                if selectRect.colliderect(MakeBoundingBox(start,end)):

                    if isinstance(event,Event.DragCompletedEvent):
                        entity.select()
                        self.selectedEntities.append(entity)
                        self._selectedEntitiesChanged = True
                    else: # Add drag completed event
                        if entity not in self.selectedEntities:
                            entity.select()
                            self.selectedEntities.append(entity)
                            self._selectedEntitiesChanged = True
    
    def ownsEntity(self,entity):
        """
        Returns boolean indicating whether or not this client owns the
        provided entity.
        """
        return self.clientID == entity.owner
    
    def clickEvent(self,event):
        """
        What works:
        single - clicking on units
        click on ground to deselect all (without a modifier)
        click a unit while holding a modifier to add to the selection
        click a selected unit while holding a modifier to remove from the selection
        """
        
        pos = event.pos
        
        if self.minimap.rect.collidepoint(pos):
            mapClickPoint = self.minimap.clickToGridPos(pos)
            if mapClickPoint is not None:
                self._setCartScrollLocation(mapClickPoint)
                return
        cartPos = specialMath.isoToCart(pos)
        destCart = (self.cartScrollLoc[0] + cartPos[0])/self.worldSize[0], \
                       (self.cartScrollLoc[1] + cartPos[1])/self.worldSize[1]
        
        # MAY BREAK THINGS - CHECK
        #clicked = specialMath.closestEntity(self.viewportEntities,pos)
        clicked = specialMath.closestEntity(self.myViewportEntities,pos)
        
        if clicked:
            drawRect = clicked.rect.move(clicked.drawOffset)
            drawRect.center = specialMath.cartToIso(drawRect.center)
            
            selectRect=clicked.getSelectionRect(drawRect)
            
            if not selectRect.collidepoint(pos):
                clicked = None
        
        if isinstance(event,Event.SelectionEvent):
            for e in self.selectedEntities:
                e.deselect()

            self.selectedEntities = []
            self._selectedEntitiesChanged = True

        if clicked and self.ownsEntity(clicked):
            # Determines if the closest entity is already selected.
            # If it is, it makes it no longer selected.
            if clicked.selected:
                clicked.deselect()
                self.selectedEntities.remove(clicked)
            else:
                clicked.select()
                self.selectedEntities.append(clicked)
                #print clicked.healthStr()
                #if isinstance(clicked, Unit): print '\n' + str(clicked.inventory)
            self._selectedEntitiesChanged = True
            
    def updateMenu(self,eventPos):
        if self.currentMenu is not None:
            self.currentMenu.update(eventPos)
    
    def selectMenu(self,eventPos):
        if self.currentMenu is not None:
            self.currentMenu.select(eventPos)
        self.currentMenu = None
    
    def drawMenu(self):
        if self.currentMenu is not None:
            self.currentMenu.draw(self.surface)
    
    def mouseMoved(self,event):
        self.setScrollSpeed(event.pos)
        self.updateMenu(event.pos)
    
    def _setCartScrollLocation(self,newCartLoc):
        self.cartScrollLoc = tuple(newCartLoc)
        self.scrollLoc = specialMath.cartToIso(self.cartScrollLoc)
    
    def scrollBasedOnElapsedTime(self,elapsedTime):
        
        if not self.world == None and self.currentMenu == None:# FIXME and self.dragRect == None:
            newScrollLoc = list(self.scrollLoc)
            scrollAddX = self.scrollSpeed[0]*elapsedTime
            scrollAddY = self.scrollSpeed[1]*elapsedTime
            if not self.dragRect == None:
                self.dragRect.scroll((scrollAddX,scrollAddY)) 
            newScrollLoc[0] = (newScrollLoc[0]+scrollAddX)
            newScrollLoc[1] = (newScrollLoc[1]+scrollAddY)
            
            # used to calculate corner of scroll location in cartesian grid
            self.cartScrollLoc = self.isoToWrappedCart(newScrollLoc)
            
            newScrollLoc = specialMath.cartToIso(self.cartScrollLoc)
            self.scrollLoc = tuple(newScrollLoc)
    
    def cartWrap(self,cartCoord):
        gridSizeX,gridSizeY = self.world.gridDim
        return cartCoord[0]%gridSizeX,cartCoord[1]%gridSizeY
    
    def isoToWrappedCart(self,isoCoord):
        """
        Returns a wrapped cartesian coordinate from an isometric
        coordinate.
        """
        return self.cartWrap(specialMath.isoToCart(isoCoord))
    
    def drawContainedEntities(self):
        """
        Draws all elements contained in the current viewport to
        self.surface.
        """
        for e in self.viewportEntities:
            e.draw(self.surface,self.scrollLoc)
  
    def setFocusedEntities(self):
        rawMouseLoc = pygame.mouse.get_pos()
        viewportMouseLoc = rawMouseLoc[0]-self.loc[0],rawMouseLoc[1]-self.loc[1]
        
        for e in self.viewportEntities:
            
            drawRect = e.rect.move(e.drawOffset)
            drawRect.center = specialMath.cartToIso(drawRect.center)
            
            selectRect = e.getSelectionRect(drawRect)
            
            if selectRect.collidepoint(viewportMouseLoc):
                e.focus()
  
    def draw(self,displaySurface):
        """
        Draws the map and all entities for the current world location.
        displaySurface is provided by the screen.
        """

        if not self.world == None:
            
            if not self.gameOver:
                self.setFocusedEntities()
                self.world.grid.draw(self.surface, self.scrollLoc, self.size)
                self.drawContainedEntities()
                self.drawDragRect()
                self.drawMiniMap()
                #self.drawDebugFrames() #Add this in to see the scroll boxes
                self.drawMenu()
                displaySurface.blit(self.surface, (self.loc,self.size))
            else:
                 font=pygame.font.Font(pygame.font.get_default_font(),120)
                 txt=font.render('YOU LOSE.',False,(0,0,0))
                 self.surface.blit(txt,(0,0))

    def processUpdateEvent(self,event):
        timeElapsed = event.elapsedTimeSinceLastFrame
        
        self.setViewportEntities()
        self.postNotification()
        
        # FIXME - NOT EFFICIENT
        for entity in self.world.getDeadEntities():
            if entity in self.selectedEntities:
                try:
                    self.selectedEntities.remove(entity)
                    self._selectedEntitiesChanged = True
                except ValueError: # thrown if entity not in selectedEntity list
                    pass
        
        if self._selectedEntitiesChanged:
            self.manager.post(Event.SelectedEntityEvent(self.selectedEntities))
            self._selectedEntitiesChanged = False
        
        if self.currentMenu is not None and not self.currentMenu.visible:
            self.currentMenu._delayedOpen(timeElapsed)
        
        self.scrollBasedOnElapsedTime(timeElapsed)
        self.world.elapsedTimeSinceLastFrame = timeElapsed
    
    def rectToCartWrappedRects(self,rect):
        isoLeftTop = rect.topleft
        cartLeftTop = self.isoToWrappedCart(isoLeftTop)
    
    def cartPointTupleOfScreen(self):
        """
        Returns a tuple of the Cartesian points of the current view
        into the world.  Order: topleft,topright,bottomright,bottomleft
        """
        l,t = self.cartScrollLoc
            
        w,h = self.size
        cartWidthVector=specialMath.isoToCart((w,0))
        cartHeightVector=specialMath.isoToCart((0,h))
        
        cartTopLeft=l,t
        cartTopRight=l+cartWidthVector[0],t+cartWidthVector[1]
        cartBottomRight=l+cartWidthVector[0]+cartHeightVector[0],t+cartWidthVector[1]+cartHeightVector[1]
        cartBottomLeft=l+cartHeightVector[0],t+cartHeightVector[1]
        
        return cartTopLeft,cartTopRight,cartBottomRight,cartBottomLeft
        
    def setViewportEntities(self):
        if not self.world == None:
            """FIXME to work with new coordinates."""
            
            cartTopLeft,cartTopRight,cartBottomRight,cartBottomLeft = \
                self.cartPointTupleOfScreen()
            
            worldWidth,worldHeight = self.world.grid.getCartGridDimensions()
            screen=[]
            
            #determine which screens to check
            xRange = [0]
            yRange = [0]
            
            if cartBottomLeft[0] >= 0:
                xRange.append(-1)
            if cartTopRight[0] <= worldWidth:
                xRange.append(1)
            
            if cartTopLeft[1] >= 0:
                yRange.append(-1)
            if cartBottomRight[1] <= worldHeight:
                yRange.append(1)
                
            for i in xRange:
                for j in yRange:
                    TL = cartTopLeft[0]+i*worldWidth,cartTopLeft[1]+j*worldHeight
                    TR = cartTopRight[0]+i*worldWidth,cartTopRight[1]+j*worldHeight
                    BR = cartBottomRight[0]+i*worldWidth,cartBottomRight[1]+j*worldHeight
                    BL = cartBottomLeft[0]+i*worldWidth,cartBottomLeft[1]+j*worldHeight
                    
                    screen.append((TL,TR,BR,BL))
            
            self.viewportEntities = self.world.getScreenEntities(screen)
            
            self.myViewportEntities = []
            for entity in self.viewportEntities:
                if self.ownsEntity(entity):
                    self.myViewportEntities.append(entity)
    
    def drawDebugFrames(self):  
        """
        Draws frames on viewport which are useful for debugging.
        Defines the scrolling and non-scrolling regions.
        """
        rect = ((0,0),self.size)
        pygame.draw.rect(self.surface, (255,255,0), rect, 3)
        pygame.draw.rect(self.surface, (255,0,255), self.deadZoneRect, 2)
    
    def setQuickSelect(self,event):
        self.quickSelect[event.key].empty()
        for entity in self.selectedEntities:
            self.quickSelect[event.key].add(entity)
        
    def getQuickSelect(self,event):
        for entity in self.quickSelect[event.key].sprites():
            entity.select()
            if entity not in self.selectedEntities:
                self.selectedEntities.append(entity)
                self._selectedEntitiesChanged = True
    
    def postNotification(self):
        """
        If the world has notifications, post the first 
        notification.
        """

        for i in xrange(len(self.world.notifications)):
            self.manager.post(self.world.notifications.pop(0))    
    
    def changeWorld(self,world):
        self.world = world
        self.minimap = MiniMap(self.world)
        self.worldSize=self.world.grid.getCartGridDimensions()