'''
Accessible Access Control 1.0
2012-2104 Michigan Technological University
Supported in part by NSF grants: DUE-1140512, DUE-1245310 and IIS-1319363
Developer: Man Wang
Advisors:Dr. Steve Carr, Dr. Jean Mayo, Dr. Ching-Kuang Shene and Dr. Chaoli Wang
'''
'''
Created on Aug 28, 2013

@author: mandy
'''
from PyQt4.QtGui import QGraphicsScene, QColor, QMessageBox, QMenu
from PyQt4.QtCore import QRectF, Qt
from EdgeItem import EdgeItem
from ClearanceNode import ClearanceNode
import math, copy

class WholeGraphScene(QGraphicsScene):
    NODELEVELLIMIT = 5
    LABELHEIGHT = 0
    NODE_MAXRADIUS = 15
    LINE_MAXWIDTH = 5
    def __init__(self, parent, scene):
        QGraphicsScene.__init__(self, parent)    
        self.parent = parent
        self.scene = scene
        self.viewW = 400
        self.viewH = 350
        self.resetAll()
    
    def resetAll(self):
        self.clear()
        self.clrNum = 0
        self.cateNum = 0
        self.clearances = self.scene.clearances
        self.categories = self.scene.categories
        self.ndlvl = {}
        self.visited = []
        self.goal = set()
        self.currentClickedNode = None
        self.firstNodeWhole = None
        self.secondNodeWhole = None
        self.wholeV = []
        self.wholeE = []
        self.mergedNdlvl = []
        self.numNdsLevel = 0
        self.prevClickedIsGrouping = False
        self.addedRealNodesLevel = -1
        self.firstTime = True
        self.newClrCateReset = False
        self.replacenode = False
        self.groupNodes = []
    
    def addNewClrCateReset(self):
        self.ndlvl = {}
        self.clrNum = 0
        self.cateNum = 0
        self.visited = []
        self.goal = set()   
        self.replacenode = False     
        self.clearances = self.scene.clearances
        self.categories = self.scene.categories
        
        self.currentClickedNode = None
        self.firstNodeWhole = None
        self.secondNodeWhole = None
        self.mergedNdlvl = []
        self.numNdsLevel = 0
        self.prevClickedIsGrouping = False
        self.addedRealNodesLevel = -1
        for n in self.items():
            if isinstance(n, ClearanceNode):
                n.setVisible(False)
                if n.groupingNode:
                    self.wholeV.remove(n)
                    self.removeItem(n)
                    #n.groupingNode = False
            elif isinstance(n, EdgeItem):
                self.removeItem(n)
                self.wholeE.remove(n)


    def deleteNode(self, node):
         if node in self.wholeV:
            self.wholeV.remove(node)
         for e in self.wholeE:
             if e.startItem == node:
                 self.wholeE.remove(e)
             elif e.endItem == node:
                 self.wholeE.remove(e)
         if node in self.items():
             self.removeItem(node)
             
#    def createFirstNodeForWholeGraph(self):
#        name = self.scene.clearances[len(self.scene.clearances)-1]+',set(['
#        for i in xrange(len(self.scene.categories)):
#            name+="u'"+self.scene.categories[i]+"'"
#            if i < len(self.scene.categories)-1:
#                name+=', '
#        name+='])'
#        node = ClearanceNode(self.scene.clearances[len(self.scene.clearances)-1], self.scene.categories, self)
#        node.name = name
#        node.rootNode = True
#        return node
#    
#    def createSecondNodeForWholeGraph(self):
#        name = self.scene.clearances[0]+',set([])'
#        node = ClearanceNode(self.scene.clearances[0], [''], self)
#        node.name = name
#        node.leafNode = True
#        return node

    def createFirstNodeForWholeGraph(self):
        name = self.scene.clearances[len(self.scene.clearances)-1]+',set(['
        for i in xrange(len(self.scene.categories)):
            name+="u'"+self.scene.categories[i]+"'"
            if i < len(self.scene.categories)-1:
                name+=', '
        name+='])'
        if self.firstNodeWhole == None or self.firstNodeWhole.name != name:
            if self.firstNodeWhole != None:
                self.firstNodeWhole.rootNode = False
            node = ClearanceNode(self.scene.clearances[len(self.scene.clearances)-1], self.scene.categories, self)
            node.name = name
            node.rootNode = True
        else:
            node = self.firstNodeWhole
        return node
    
    def createSecondNodeForWholeGraph(self):
        name = self.scene.clearances[0]+',set([])'
        if self.secondNodeWhole == None or self.secondNodeWhole.name != name:
            if self.secondNodeWhole != None:
                self.secondNodeWhole.leafNode = False
            node = ClearanceNode(self.scene.clearances[0], [''], self)
            node.name = name
            node.leafNode = True
        else:
            node = self.secondNodeWhole
        return node
    

    def addOneNode2Scene(self, node):
        if node not in self.items():
            self.addItem(node)
        if node not in self.wholeV:
            self.wholeV.append(node)
        node.setVisible(True)
    
    def addOneEdge2Scene(self, type, firstNode, secNode, highlightFlg, count):
        edge = EdgeItem(type, secNode, firstNode)
        for e in self.items():
            if isinstance(e, EdgeItem):
                #if e.startItem == firstNode and e.endItem == secNode:
                if e.startItem == secNode and e.endItem == firstNode:
                    edge = e
                    break
        if edge not in self.items():
            self.addItem(edge)
        if edge not in self.wholeE:
            self.wholeE.append(edge)
        edge.lineWidth = 1
        edge.setVisible(True)
        return edge
        
    def removeTransEdge(self):
        for e in self.scene.wholelatticeNodes:
            e.visited = False
            if e.parent == []:
                self.first = e
            elif e.children == []:
                self.last = e
          
        item = self.first
        self.ndlvl = {0:[item]}
        self.numNdsLevel+=1
        
        prevlevel = 0
        i, j = 0, 0
        var =[]
        while item != self.last:
            tempChildren = []
            for e in item.children:
                tempChildren.append(e)
            for c in item.children:
                for cc in c.children:
                    for e in item.children:
                        if e == cc: 
                            tempChildren.remove(e)
                item.children = tempChildren
            if i < len(self.ndlvl[prevlevel]):
                for ele in item.children:
                    if ele.visited == False:
                        var.append(ele)
                        ele.visited = True
                i+=1
            if i == len(self.ndlvl[prevlevel]):
                self.ndlvl.update({self.numNdsLevel:var})
                self.numNdsLevel+=1
                prevlevel +=1
                var = []
                j = 0
                i = 0
            if self.ndlvl[self.numNdsLevel-1][j] != None:
                item = self.ndlvl[self.numNdsLevel-1][j]
                j += 1
        #get the break up level
        for i in xrange(0, len(self.ndlvl.keys())-1):
            current = len(self.ndlvl[i])
            next = len(self.ndlvl[i+1])
            if current > next:
                self.scene.brkLevel = i+1
                break
            current = next
        
    def getEdgeItem(self):
        self.removeTransEdge()
        for i in xrange(len(self.ndlvl)):
            for endItem in self.ndlvl[i]:
                for startItem in endItem.children:
                    e = EdgeItem(EdgeItem.CLR_CONN, startItem, endItem)
                    startItem.edgeList.append(e)
                    endItem.edgeList.append(e)
                    
    def layoutInitialization(self):
        self.numLevels = len(self.ndlvl)
        self.longer, self.radius = self.viewW, self.viewH
        if self.longer < self.viewH:
            self.longer = self.viewH
            self.radius = self.viewW
        self.radius *= 0.5
        self.wIntvl = 0.9*self.longer / self.numLevels
        self.curNumChildren = len(self.currentClickedNode.children)
        if self.curNumChildren !=0:
            self.aIntvl = math.pi/(self.curNumChildren+1)
            
    def getWholeLevels(self):
        for i in xrange(len(self.scene.NdsLevels)):
            nodes = []
            for n in self.scene.NdsLevels[i]:
                for e in self.scene.wholelatticeNodes:
                    if n.name == e.name:
                        nodes.append(e)
            self.ndlvl.update({i:nodes})
        
    def initialWholeNodes(self, firstNode):
        self.halfViewW = (int)(0.5*self.viewW)
        self.halfViewH = (int)(0.5*self.viewH)
        self.getWholeLevels()
        if firstNode != None:
            for elist in self.ndlvl.values():
                for e in elist:
                    if e.clearance == firstNode.clearance and set(e.category) == set(firstNode.category):
                        firstNode = e
            self.currentClickedNode = firstNode
            self.layoutInitialization()
            self.addOneNode2Scene(firstNode)
            firstNode.setVisible(True)
            self.path = {0:firstNode}
            self.curlevel = 0
        return firstNode
    
    def DFSAllConstruct(self, firstNode, goal, visited):
        if firstNode in goal:
            return True
        if firstNode in visited:
            return False
        visited.append(firstNode)
        reached  = False
        for e in firstNode.children:
            btemp = self.DFSAllConstruct(e, goal, visited)
            if btemp:
                reached = True
        if reached:
            goal.add(firstNode) 
        return reached
    
    def checkVisibilityGroupNode(self, node):
        for k in self.groupNodes:
            if node in k:
                return True
        return False
                
    def DFSAll(self, firstNode, goal, visited):
        btemp = False
        if firstNode in goal:
            return True
        if firstNode in visited:
            return False
        visited.append(firstNode)
        reached  = False
        for e in firstNode.children:
            visibilityS = self.checkVisibilityGroupNode(e)
            if len(e.groupingParentPath)>0:
                visibilityG = self.checkVisibilityGroupNode(e.groupingParentPath[0])
                if visibilityS or visibilityG:
                    btemp = self.DFSAll(e, goal, visited)
            else:
                if visibilityS:
                    btemp = self.DFSAll(e, goal, visited)
            if btemp:
                reached = True
        if reached:
            goal.add(firstNode) 
        return reached
    

    def savePath(self, firstNode, secNode):
        self.ndlvlPath = []
        temp = []
        nodeOnelevel = [firstNode]
        while nodeOnelevel != []:
            nodeOnelevel.sort(key =lambda x: x.flgClr, reverse = True)
            self.ndlvlPath.append(nodeOnelevel)
            temp = nodeOnelevel
            nodeOnelevel = []
            for node in temp:
                for n in node.children:
                    if n in self.goal and n not in nodeOnelevel:
                        nodeOnelevel.append(n)
                
        #save the children on path for each node
        self.ndlvlPath[len(self.ndlvlPath)-1][0].childrenPath = []
        for i in xrange(len(self.ndlvlPath)-1):
            for node in self.ndlvlPath[i]:
                node.childrenPath = []
                for c in node.children:
                    if c in self.ndlvlPath[i+1]:
                        node.childrenPath.append(c)
        
    def equalWidthIntvlLayout(self, firstNode, nodelevel):
        realHeight = self.viewH-2*self.LABELHEIGHT
        self.wIntvl = self.viewW / len(nodelevel)
        for i in xrange(0, len(nodelevel)):
            if i < len(nodelevel):
                self.hIntvl = realHeight/len(nodelevel[i])
                count = 0
                height = 0.5*(self.viewH-self.hIntvl)-self.LABELHEIGHT
            for n in nodelevel[i]:
                if i == 0:
                    n.relativeX, n.relativeY = 0.5*(-self.viewW+self.wIntvl), 0
                    self.addOneNode2Scene(n)
                if i < len(nodelevel):
                    n.relativeX = -0.5*self.viewW + (0.5+i)*self.wIntvl
                    n.relativeY = height
                    self.addOneNode2Scene(n)
                    height -= self.hIntvl
                    count+=1
                    if len(self.ndlvlPath)<=self.NODELEVELLIMIT:
                        for p in nodelevel[i-1]:
                            if n in p.children:
                                self.addOneEdge2Scene(EdgeItem.CLR_CONN, p, n, False, 1)
                if i == len(nodelevel)-1:
                    n.relativeX, n.relativeY = 0.5*(self.viewW-self.wIntvl), 0
                    self.addOneNode2Scene(n)
        self.mapToScene()
        self.update()
            
    def equalWidthIntvlOneNodeLayoutNorthSouth(self, nodelevel):
        realWidhth = self.viewW
        self.hIntvl = self.viewH / len(nodelevel)
        for i in xrange(0, len(nodelevel)):
            if i < len(nodelevel):
                self.wIntvl = realWidhth/len(nodelevel[i])
                width = 0.5*(-self.viewW+self.wIntvl)
                count = 0
            for n in nodelevel[i]:
                if i == 0:
                    n.relativeX, n.relativeY = 0, 0.5*(self.viewH-self.hIntvl)
                    self.addOneNode2Scene(n)
                elif i < len(nodelevel):
                    n.relativeX = width
                    n.relativeY = 0.5*self.viewH - (0.5+i)*self.hIntvl
                    self.addOneNode2Scene(n)
                    width += self.wIntvl
                    count+=1
                    if len(self.ndlvlPath)<=self.NODELEVELLIMIT:
                        for p in nodelevel[i-1]:
                            if n in p.children:
                                self.addOneEdge2Scene(EdgeItem.CLR_CONN, p, n, False, 1)
                elif i == len(nodelevel)-1:
                    n.relativeX, n.relativeY = 0, 0.5*(-self.viewH+self.hIntvl)
                    self.addOneNode2Scene(n)
        self.mapToScene()
        self.update()
                
    def mapToScene(self):
        for item in self.wholeV:
                item.setPos(item.relativeX+self.viewW/2.0, -item.relativeY+self.viewH/2.0)
        for edge in self.wholeE:
                edge.updatePosition()
                
    def clearScene(self):
        for item in self.items():
            item.setVisible(False)     
            
    def restoreInitialGraph(self):
        if len(self.ndlvlPath) > self.NODELEVELLIMIT:
            for n in self.wholeV:
                if not (n.groupingNode or n.rootNode or n.leafNode):
                    self.movingItem = n
                    self.revert2Groupnode()
                
    def searchWholePath(self):
        if not self.scene.main.hasInfo:
            return
        if self.clrNum < len(self.scene.clearances) or self.cateNum < len(self.scene.categories):
            self.clrNum = len(self.scene.clearances)
            self.cateNum = len(self.scene.categories)
        if self.firstTime:
            self.resetAll()
            self.firstTime = False
            self.constructGraph()
        elif self.newClrCateReset:
            self.addNewClrCateReset()
            self.constructGraph()
            self.newClrCateReset = False
            
    def constructGraph(self):
        self.firstNodeWhole = self.createFirstNodeForWholeGraph()
        self.secondNodeWhole = self.createSecondNodeForWholeGraph()
        self.firstNodeWhole = self.initialWholeNodes(self.firstNodeWhole)
        self.onelevelPath = [[self.firstNodeWhole]]
        self.currentClickedNode = self.firstNodeWhole
        self.onelevelExtend = [[self.firstNodeWhole]]
        for elist in self.ndlvl.values():
            for e in elist:
                if e.clearance == self.secondNodeWhole.clearance and set(e.category) == set(self.secondNodeWhole.category):
                    self.secondNodeWhole = e
        self.addOneNode2Scene(self.secondNodeWhole)
        
        self.goal.add(self.secondNodeWhole)
        self.DFSAllConstruct(self.firstNodeWhole, self.goal, self.visited)
        self.getEdgeItem()
        self.savePath(self.firstNodeWhole, self.secondNodeWhole)
        if len(self.ndlvlPath) <= self.NODELEVELLIMIT:
            self.equalWidthIntvlOneNodeLayoutNorthSouth(self.ndlvlPath)
        else:
            self.saveGroupingPath(self.firstNodeWhole, self.secondNodeWhole)
            self.adjustableWIntvlLayout(self.firstNodeWhole, self.groupNodes, self.ndlvlPath)

            
    def adjustableWIntvlLayout(self, firstNode, nodelevel, mergedNdlevel):
        bound = len(nodelevel)/2
        self.node = nodelevel[bound][0]
        mergedNdlevel = nodelevel
        if self.onelevelExtend != [[firstNode]]:
            mergedNdlevel = self.mergeTwoLists(nodelevel, self.onelevelExtend)
        self.drawAllEdges(mergedNdlevel)
        self.equalWidthIntvlOneNodeLayoutNorthSouth(mergedNdlevel)
        
    def saveGroupingPath(self, firstNode, secNode):
        self.groupNodes = [[firstNode]]
        firstNode.groupingChildrenPath = [firstNode]
        self.ndlvlGroupPath = []
        finalList = []
        onelevel = []
        onelevelGroupNodes = []
        sameClrOnelevel = []
        for i in xrange(1, len(self.ndlvlPath)-1):
            prev = self.ndlvlPath[i][0]
            for n in self.ndlvlPath[i]:
                if n.flgClr == prev.flgClr:
                    sameClrOnelevel.append(n)
                else:
                    onelevel.append(sameClrOnelevel)
                    self.addGroupNodes(sameClrOnelevel, onelevelGroupNodes)
                    sameClrOnelevel = [n]
                    prev = n
            onelevel.append(sameClrOnelevel)
            self.addGroupNodes(sameClrOnelevel, onelevelGroupNodes)
            sameClrOnelevel = []
            self.groupNodes.append(onelevelGroupNodes)
            onelevelGroupNodes = []
            finalList.append(onelevel)
            onelevel = []
            
        self.groupNodes.append([secNode])
        
        self.ndlvlGroupPath = finalList
        self.getGroupNodesEdges()
    
    def addGroupNodes(self, originalNodes, onelevelGroupNodes):
        numNodeSameClr = len(originalNodes)
        halfsize = 5*numNodeSameClr
        if numNodeSameClr == 1:
            n = originalNodes[0]
        else:
            n = ClearanceNode(originalNodes[0].clearance, [], self)
            n.name = originalNodes[0].clearance
            n.groupingNode = True
            if halfsize < self.NODE_MAXRADIUS:
                n.setRect(-halfsize,-halfsize,2*halfsize,2*halfsize)
            else:
                n.setRect(-self.NODE_MAXRADIUS,-self.NODE_MAXRADIUS,2*self.NODE_MAXRADIUS,2*self.NODE_MAXRADIUS)
        onelevelGroupNodes.append(n)
        for c in originalNodes:
            n.groupingChildrenPath.append(c)
            n.groupingOriChildrenPath.append(c)
            if n.groupingNode:
                c.groupingParentPath = [n]
                
    def getGroupNodesEdges(self):
        '''
        get the children grouping nodes for edges
        '''
        for i in range(len(self.groupNodes)-1):
            for n in self.groupNodes[i]:
                if n.groupingNode:
                    currentNodes = n.groupingChildrenPath
                else:
                    currentNodes = [n]
                for nextlevel in self.groupNodes[i+1]:
                    count = 0
                    if nextlevel.groupingNode:
                        childrenNodes = nextlevel.groupingChildrenPath
                    else:
                        childrenNodes = [nextlevel]
                    for c in currentNodes:
                        for cc in c.children:
                            if cc in childrenNodes:
                                count+=1
                    if count > 0:
                        self.addOneEdge2Scene(EdgeItem.CLR_CONN, n, nextlevel, False, count)
                        
    def drawAllEdges(self, nodelevel):
        for i in xrange(len(nodelevel)-1):
            if self.addedRealNodesLevel != -1 and i!=self.addedRealNodesLevel+1:
                for n in nodelevel[i]:
                    for c in n.children:
                        if c in nodelevel[i+1]:
                            self.addOneEdge2Scene(EdgeItem.CLR_CONN, n, c, False, 1.0)
                    
    def layoutAnima(self, nodelevel, mergedNodeLevel):
        numLevel = len(mergedNodeLevel)
        normals = self.NODELEVELLIMIT-1
        leftNormal = int(normals/2.0+0.5)
        rightNormal = normals - leftNormal

        for i in xrange(len(mergedNodeLevel)):
            if self.node in mergedNodeLevel[i]:
                bound = i
                
        if self.addedRealNodesLevel != -1:
            bound = self.addedRealNodesLevel+1
            
        leftRatio, rightRatio = 0,0
        if bound-leftNormal > 0:
            leftRatio = 0.8/(bound-leftNormal)
        if numLevel-bound-rightNormal-1>0:
            rightRatio = 0.8/(numLevel-bound-rightNormal-1)
        
        idealWidthIntvl = self.NODELEVELLIMIT-1
        for i in xrange(bound-leftNormal):
            idealWidthIntvl+=leftRatio*(i+1)
        for i in xrange(numLevel-bound-rightNormal-1):
            idealWidthIntvl+=rightRatio*(i+1)
        self.wIntvl = self.viewW / len(nodelevel)
        sideWidth = (self.viewW-self.wIntvl)/idealWidthIntvl
        self.groupingLevels(nodelevel, bound, leftNormal, rightNormal, leftRatio, rightRatio, sideWidth)
            
    def groupingLevels(self, nodelevel, bound, leftNormal, rightNormal, leftRatio, rightRatio, sideWidth):
        numLevel = len(nodelevel)
        ratio = leftRatio
        realHeight = self.viewH-2*self.LABELHEIGHT
        nodelevel[0][0].relativeX = 0.5*(-self.viewW+self.wIntvl)
        nodelevel[0][0].relativeY = 0
        for i in range(1, numLevel-1):
            self.hIntvl = realHeight/len(nodelevel[i])
            height = 0.5*(self.viewH-self.hIntvl)-self.LABELHEIGHT
            if i == bound+rightNormal:
                ratio = rightRatio
            for n in nodelevel[i]:
                if bound == 0 and i ==1:
                    intvl = nodelevel[i-1][0].relativeX + (self.NODELEVELLIMIT-1)*sideWidth
                elif i<=bound-leftNormal:
                    intvl = (nodelevel[i-1][0].relativeX + ratio*(i)*sideWidth)#-(n.pos().x()-self.halfViewW)#(prev.relativeX + ratio*(i)*sideWidth)#
                elif (bound-i<leftNormal and bound-i>=0) or (i - bound<=rightNormal and i - bound>=0):
                    intvl= nodelevel[i-1][0].relativeX + sideWidth#-(n.pos().x()-self.halfViewW)
                elif i>bound+rightNormal:
                    intvl = nodelevel[i-1][0].relativeX + ratio*(numLevel-i)*sideWidth#-(n.pos().x()-self.halfViewW)
                n.relativeX = intvl#/self.ANIMMOVECNT
                n.relativeX = self.boundaryCheck(n.relativeX)
                n.relativeY = height
                height -= self.hIntvl
                self.addOneNode2Scene(n) 
        nodelevel[numLevel-1][0].relativeX = 0.5*(self.viewW-self.wIntvl)
        nodelevel[numLevel-1][0].relativeY = 0
        
    def boundaryCheck(self, x):
        if x > self.halfViewW:
            x=self.halfViewW-10
        elif x<-self.halfViewW:
            x=-self.halfViewW+10
        return x
    
    def resizeEvent(self, evt):
        self.views()[0].resize(evt.size())
        self.viewW = self.views()[0].viewport().width()
        self.viewH = self.views()[0].viewport().height()
        self.halfViewW = 0.5*self.viewW
        self.halfViewH = 0.5*self.viewH
        self.setSceneRect(QRectF(0,0,self.viewW, self.viewH))
        if self.parent.hasInfo:
            if len(self.ndlvlPath) <= self.NODELEVELLIMIT:
                self.equalWidthIntvlOneNodeLayoutNorthSouth(self.ndlvlPath)
            else:
                self.adjustableWIntvlLayout(self.firstNodeWhole, self.groupNodes, self.ndlvlPath)    
    
    def findWriteEdges4Highlight(self, goalList, node):
        for edge in self.items():
            if isinstance(edge, EdgeItem) and edge.type != EdgeItem.GRUP_CONN:
                startReached, endReached = False, False
                if edge.endItem.groupingNode:
                    for n in edge.endItem.groupingChildrenPath:
                        if n in goalList:
                            endReached = True
                elif edge.endItem in goalList:
                    endReached = True
                if edge.startItem.groupingNode:
                    for n in edge.startItem.groupingChildrenPath:
                        if n in goalList:
                            startReached = True
                elif edge.startItem in goalList:
                    startReached = True     
                if startReached and endReached:
                    edge.highlightedWrite = True
                    
    def findReadEdges4Highlight(self, goalList, node):
        for edge in self.items():
            if isinstance(edge, EdgeItem) and edge.type != EdgeItem.GRUP_CONN:
                startReached, endReached = False, False
                if edge.endItem.groupingNode:
                    for n in edge.endItem.groupingChildrenPath:
                        if n in goalList:
                            endReached = True
                elif edge.endItem in goalList:
                    endReached = True
                if edge.startItem.groupingNode:
                    for n in edge.startItem.groupingChildrenPath:
                        if n in goalList:
                            startReached = True
                elif edge.startItem in goalList:
                    startReached = True     
                if startReached and endReached:
                    edge.highlighted = True
            
    def getWritableNodes(self, node):
        visited = []
        goal = set()
        if node.groupingNode:
            for n in node.groupingChildrenPath:
                visited = []
                goal=set()
                goal.add(n)
                self.DFSAll(self.firstNodeWhole, goal, visited)
                self.findWriteEdges4Highlight(goal, n)
        else:
            goal.add(node)
            self.DFSAll(self.firstNodeWhole, goal, visited)
            self.findWriteEdges4Highlight(goal, node)
        self.update()
        
    def getReadableNodes(self, node):
        visited = []
        goal = set()
        goal.add(self.secondNodeWhole)
        if node.groupingNode:
            for n in node.groupingChildrenPath:
                if n.isVisible():
                    reached = False
                    visited = []
                    goal = set()
                    goal.add(self.secondNodeWhole)
                    self.DFSAll(n, goal, visited)
                    self.findReadEdges4Highlight(goal, n)
        else:
            self.DFSAll(node, goal, visited)
            self.findReadEdges4Highlight(goal, node)
        self.update()
        
    def highlightPathThrNode(self, node):
        self.getWritableNodes(node)
        self.getReadableNodes(node)
    
    def setAllEdgesNothighlighted(self):
        for edge in self.items():
            if isinstance(edge, EdgeItem):
                edge.highlighted = False
                edge.highlightedWrite = False
                
    def mouseMoveEvent(self, evt):
        if self.groupNodes != []:
            self.hoverItem = self.itemAt(evt.scenePos())
            if self.hoverItem != None and isinstance(self.hoverItem, ClearanceNode):
                if not self.hoverItem.groupingNode:
                    self.hoverItem.setToolTip('('+str(self.hoverItem.clearance)+', ['+','.join(self.hoverItem.category)+'])')
                else:
                    self.hoverItem.setToolTip(str(self.hoverItem.clearance))
                self.highlightPathThrNode(self.hoverItem)
            else:
                self.setAllEdgesNothighlighted()
        QGraphicsScene.mouseMoveEvent(self, evt)
        self.update()
            
    def removeUnusedRealNodesAndEdges(self):
        self.removeGroup2RealNodesEdges()
        self.groupNodes.pop(self.addedRealNodesLevel+1)
        
    def removeGroup2RealNodesEdges(self):
        for n in self.items():
            if isinstance(n, EdgeItem):
                if n.type == EdgeItem.GRUP_CONN:
                    self.removeItem(n)
                    self.removeItem(n.startItem)
                   
    def selectNodeWithLabelPrinted(self, nodeList):
        numEle = len(nodeList)
        if numEle> 30:
            for i  in xrange(numEle):
                if (i+1)%5 == 0:
                    nodeList[i].displayLabel = True
                else:
                    nodeList[i].displayLabel = False
        
    def showRealNodes(self, node, nodelevel):
        if node.groupingNode:
            for i in xrange(len(self.groupNodes)):
                if node in self.groupNodes[i]:
                    self.addedRealNodesLevel = i
                    break
            self.groupNodes.insert(self.addedRealNodesLevel+1, node.groupingChildrenPath)
            self.selectNodeWithLabelPrinted(node.groupingChildrenPath)
            self.getGroup2RealNodesEdges()
        if len(self.groupNodes) >2:
            self.equalWidthIntvlOneNodeLayoutNorthSouth(self.groupNodes)
             
    def getGroup2RealNodesEdges(self):
        node = self.movingItem
        for c in node.groupingChildrenPath:
            edge = self.addOneEdge2Scene(EdgeItem.GRUP_CONN, node, c, False, 4)
            edge.color = QColor(255,215,0)
            
    def replaceGroupNode(self, node, nodelevel):
        i = 0
        for n in self.groupNodes[self.addedRealNodesLevel]:
            if node in n.groupingChildrenPath:
                self.groupNodes[self.addedRealNodesLevel][i] = node
                node.groupingChildrenPath = [node]
                self.removeEdgesIncludeNode(n)
                self.removeItem(n)
            i+=1
        self.updateGroupNodeSet(self.addedRealNodesLevel, self.groupNodes)
        if len(self.groupNodes) >2:
            self.equalWidthIntvlOneNodeLayoutNorthSouth(self.groupNodes)
            
    def removeEdgesIncludeNode(self, node):
        for edge in self.items():
            if isinstance(edge, EdgeItem):
                if edge.startItem == node or edge.endItem == node:
                    self.removeItem(edge)
                    
    def getGroupNodelevel(self, nodelevel, tempList):
        '''
        to combine path lists starting from diff nodes on the same level to the second node
        '''
        if tempList == []:
            tempList = nodelevel
        else:
            for i in xrange(len(nodelevel)):
                for n in nodelevel[i]:
                    if n not in tempList[i]:
                        tempList[i].append(n)
        return tempList
    
    def updateGroupNodeSet(self, startIndex, nodelevel):
        for i in range(len(self.groupNodes)-1):
            oneLevel = []
            for n in self.groupNodes[i]:
                if n.groupingNode:
                    for realnodes in n.groupingChildrenPath:
                        for c in realnodes.children:
                            if c not in oneLevel:
                                oneLevel.append(c)
                else:
                    for c in n.children:
                        if c not in oneLevel:
                            oneLevel.append(c)
            for e in self.groupNodes[i+1]:
                self.getGroupNodesEdges()
                
    def revert2Groupnode(self):
        if self.movingItem.groupingParentPath != []:
            node = self.movingItem
            node.groupingChildrenPath = copy.deepcopy(node.groupingOriChildrenPath)
            for i in xrange(len(self.groupNodes)):
                j = 0
                for n in self.groupNodes[i]:
                    if n == node:
                        self.groupNodes[i][j] = node.groupingParentPath[0]
                        self.removeItem(node)
                        self.addItem(node.groupingParentPath[0])
                        self.removeEdgesIncludeNode(node)
                        break
                    j+=1
        self.getGroupNodesEdges()
        if len(self.groupNodes) >2:
            self.equalWidthIntvlOneNodeLayoutNorthSouth(self.groupNodes)
        
    def mergeTwoLists(self, firstList, secList):
        firstStartLevel = self.getKeyByValue(self.ndlvl, firstList[0][0])
        secondStartLevel = self.getKeyByValue(self.ndlvl, secList[0][0])
        if firstStartLevel < secondStartLevel:
            mergedList = firstList
            otherList = secList
        else:
            mergedList = secList
            otherList = firstList
            
        startLevel = self.getIndexinList(otherList[0][0], mergedList)     
        for i in xrange(0, len(otherList)):
            if i < len(mergedList):
                for n in otherList[i]:
                    if n not in mergedList[i+startLevel]:
                        mergedList[i+startLevel].append(n)
            else:
                mergedList.append(otherList[i])
        return mergedList
    
    def mousePressEvent(self, evt):
        self.movingItem = self.itemAt(evt.scenePos())
        if len(self.ndlvlPath) > self.NODELEVELLIMIT:
            if self.movingItem is not None:
                if evt.button() == Qt.LeftButton:
                    if isinstance(self.movingItem, ClearanceNode):
                        if self.prevClickedIsGrouping:
                            self.removeUnusedRealNodesAndEdges()
                        if self.movingItem.groupingNode:
                            self.prevClickedIsGrouping = True
                            self.showRealNodes(self.movingItem, self.groupNodes)
                        elif not self.movingItem.rootNode and not self.movingItem.leafNode:
                            self.prevClickedIsGrouping = False
                            self.replaceGroupNode(self.movingItem, self.groupNodes)
                    
    def contextMenuEvent(self, event):
        item = self.itemAt(event.scenePos())
        menu = QMenu()
        if item is not None and isinstance(item, ClearanceNode):
            if item.groupingParentPath!=[]:
                bk2GroupNodeAction = menu.addAction('Revert to group node')
                bk2GroupNodeAction.triggered.connect(self.revert2Groupnode)
        menu.exec_(event.screenPos())