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

@author: yifli
'''

from PyQt4.QtGui import QUndoCommand
from FileNode import FileNode
from EdgeItem import EdgeItem
from RadialTreeLayout import RadialTreeLayout
import string
import re

class ChangeAssignmentCommand(QUndoCommand):

    def __init__(self, item, assign):
        QUndoCommand.__init__(self)
        self.item = item
        self.assign = str(assign)
        self.old_assign = self.item.assignment
        self.scene = self.item.scene()
        self.typeChangedNodes = {}
        self.flagChangedNodes = {}
        self.newNodes = []
        self.newEdges = []
        self.deletedNodes = []
        
    def undo(self):
        self.item.assignment = self.old_assign
        for n, t in self.typeChangedNodes.items():
            n.type = t[0]
            n.type.assignment = t[1]
        for n, f in self.flagChangedNodes.items():
            n.flag = f
        
        for e in self.newEdges:
            e.startItem.edgeList.remove(e)
            e.endItem.edgeList.remove(e)
            self.scene.removeItem(e)

        for n in self.newNodes:
            self.scene.removeItem(n)
            
        self.typeChangedNodes = {}
        self.flagChangedNodes = {}
        self.newNodes = []
        self.newEdges = []
        self.deletedNodes = []

        self.scene.update()
    
    
    def getFiles(self, assignment):
        fileMap = {}

        components = string.split(assignment, ';')[0:-1]
        for c in components:
            if string.find(c, '-s -r') != -1 or string.find(c, '-r -s') != -1:
                files = string.strip(c)[6:]
                for f in string.split(files, ','):
                    fileMap[string.strip(f)] = 3
            elif string.find(c, '-s') != -1:
                files = string.strip(c)[3:]
                for f in string.split(files, ','):
                    fileMap[string.strip(f)] = 1
            elif string.find(c, '-r') != -1:
                files = string.strip(c)[3:]
                for f in string.split(files, ','):
                    fileMap[string.strip(f)] = 2
            else:
                files = string.strip(c)
                for f in string.split(files, ','):
                    fileMap[string.strip(f)] = 0
#         print fileMap
        return fileMap
    
    def removeType(self, node):
        self.typeChangedNodes[node] = node.type
        oldType = node.type
        # go up in the tree to find out if it's possible to inherit a type from its ancestors
        inherited = False
        parent = node.parent
        while parent:
            if parent.type is not None:
                node.type = parent.type
                inherited = True
                break
            else:
                parent = parent.parent
        if not inherited:
            node.type = None
            
        # go down in the tree to look for affected children
        if oldType is not node.type:
            nodes = [i for i in node.children]
            for n in nodes:
                if n.inheritFromNode is not None:
                    self.typeChangedNodes[n] = node.type
                    n.type = node.type
                if n.children:
                    nodes.extend(n.children)

        
    def redo(self):
        self.item.assignment = self.assign
        fileMap = self.getFiles(self.assign)
        newFiles = set(fileMap.keys())
        oldFiles = set(self.getFiles(self.old_assign).keys())
        
        fileNodes = {}
        for item in self.scene.items():
            if isinstance(item, FileNode):
                fileNodes[item.fullPath] = item

        # which files type have been unassigned
        diff = oldFiles - newFiles
        if len(diff) > 0:
            for f in diff:
                self.removeType(fileNodes[f])
            

        # did we assign this type to any new files?
        diff = newFiles - set(fileNodes.keys())
        if len(diff) > 0:
            for f in diff:
                parent = self.scene.typeGraphRoot
                components = string.split(f, '/')[1:]
                level = len(components)
                for i in xrange(level-1):
                    name = components[i]
                    found = False
                    if i+1 in self.scene.typeGraph:
                        for fn in self.scene.typeGraph[i+1][0]:
                            if str(fn.name) == name:
                                parent = fn
                                found = True
                    else:
                        self.scene.typeGraph[i+1] = [[], None]
                    if not found:
                        node = FileNode(name, parent.type, 2)
                        node.setVisible(False)
                        self.scene.addItem(node)
                        self.newNodes.append(node)
                        edge = EdgeItem(EdgeItem.FILE_CONN, parent, node)
                        edge.setVisible(False)
                        parent.edgeList.append(edge)
                        node.edgeList.append(edge)
                        self.scene.addItem(edge)
                        self.newEdges.append(edge)
                        node.inheritFromNode = parent
                        node.fullPath = '/'+ '/'.join(components[0:i+1])
                        parent.children.append(node)
                        node.parent = parent
                        self.scene.typeGraph[i+1][0].append(node)
                        parent = node
                
                name = components[-1]
                node = FileNode(name, self.item, fileMap[f])
                self.newNodes.append(node)
                node.setVisible(False)
                self.scene.addItem(node)
                edge = EdgeItem(EdgeItem.FILE_CONN, parent, node)
                edge.setVisible(False)
                parent.edgeList.append(edge)
                node.edgeList.append(edge)
                self.scene.addItem(edge)
                self.newEdges.append(edge)
                parent.children.append(node)
                node.parent = parent
                node.fullPath = f
                node.inheritFromNode = None 
                if level not in self.scene.typeGraph:
                    self.scene.typeGraph[level] = [[node], None]
                else:
                    self.scene.typeGraph[level][0].append(node)   
        else:
            for f in newFiles:
                if fileNodes[f].flag != fileMap[f]:
                    self.flagChangedNodes[fileNodes[f]]=fileNodes[f].flag
                    fileNodes[f].flag = fileMap[f]
                if fileNodes[f].type is not self.item:
                    if fileNodes[f].type is not None:
                        self.typeChangedNodes[fileNodes[f]] =[fileNodes[f].type, fileNodes[f].type.assignment]
                        # don't forget to change the assignment statement of the file's old type
                        p = re.compile('%s\s*,' % f)
                        fileNodes[f].type.assignment = re.sub(p, '', fileNodes[f].type.assignment)
                    else:
                        self.typeChangedNodes[fileNodes[f]]=[None, '']


                    fileNodes[f].type = self.item
                    
        
        if self.newNodes:
            layout = RadialTreeLayout(self.scene)
            layout.buildTreeTable(self.scene.typeGraphRoot)
            layout.layout()
            layout.mapToScene()
            self.scene.views()[0].parent().calculateLevelCircles()
           
        self.scene.update()
                