'''
Created on Oct 23, 2012

A lattice of clearance,category set security levels
@author: carr
'''

from LatticeNode import LatticeNode

class SecurityLevelLattice(object):

    def __init__(self,clearances,categories):
        '''
        
        :param clearances: a set of clerances
        :param categories: the powerset of all categories
        '''
        self.latticeNodes = dict() # the nodes in the lattice
        self.top = LatticeNode(clearances[len(clearances)-1],categories[len(categories)-1]) # the top element
        self.latticeNodes[clearances[len(clearances)-1]+str(categories[len(categories)-1])] = self.top
                          
        self.bottom = LatticeNode(clearances[0],categories[0]) # the bottom element
        self.latticeNodes[clearances[0]+str(categories[0])] = self.bottom

        self.clearances = clearances
        self.categories = categories
        
        self.computeMaxLatticeLevel()
        self.securityLevels = dict() # all posssible security levels
            
        self.buildSecurityLevels()

        self.buildLattice()
 
    def computeMaxLatticeLevel(self):
        '''
        Calculate the maximum lattice level
        '''
        self.maxLatticeLevel = 0
        for category in self.categories :
            level = len(category)
            self.maxLatticeLevel = max(self.maxLatticeLevel,level)
           
    def buildSecurityLevels(self):
        '''
        Construct a map for all security levels
        '''
        for category in self.categories :
            level = len(category)
            if not level in self.securityLevels  :
                self.securityLevels[level] = []
            for clearance in self.clearances :
                sLevel = (clearance,set(category))
                self.securityLevels[level].append(sLevel)
                
    def addEdge(self,pred,succ):
        '''
        Add an edge between lattice ndoes
        :param pred: the predecessor node
        :param succ: the successor node
        '''
        pred.addSuccessor(succ)
        succ.addPredecessor(pred)
        
    def strictlyDominates(self,sLevel1, sLevel2):
        '''
        Does the dominates relation hold for two security levels and are they not the same level
        :param sLevel1: a security level pair
        :param sLevel2: a security level pair
        '''
        return (sLevel1 != sLevel2) and (self.clearances.index(sLevel2[0]) <= self.clearances.index(sLevel1[0])) and (sLevel2[1].issubset(sLevel1[1]))
            
    def getLatticeNode(self,sLevel):
        '''
        Return a lattice node associated with a particular security level
        :param sLevel: asecurity level
        '''
        key = sLevel[0]+str(sLevel[1])
        if key in self.latticeNodes :
            return self.latticeNodes[key]
        else :
            node = LatticeNode(sLevel[0],sLevel[1])
            self.latticeNodes[key] = node
            return node
    
    def buildLattice(self):
        '''
        Build the lattice of security levels
        '''
        for level in range(0,self.maxLatticeLevel) :
            for sLevel1 in self.securityLevels[level] :
                for sLevel2 in self.securityLevels[level] :
                    '''
                    An edge can be between levels with the same number of categories
                    '''
                    if self.strictlyDominates(sLevel1,sLevel2) :
                        self.addEdge(self.getLatticeNode(sLevel1), self.getLatticeNode(sLevel2))
                    elif self.strictlyDominates(sLevel2,sLevel1) :
                        self.addEdge(self.getLatticeNode(sLevel2), self.getLatticeNode(sLevel1))
                        
                '''
                An edge can be between a level and the next level with one more category in the set
                '''
                for sLevel2 in self.securityLevels[level+1] :
                    if self.strictlyDominates(sLevel2,sLevel1) :
                        self.addEdge(self.getLatticeNode(sLevel2), self.getLatticeNode(sLevel1))
                        
        for sLevel1 in self.securityLevels[self.maxLatticeLevel] :
            for sLevel2 in self.securityLevels[self.maxLatticeLevel] :
                if self.strictlyDominates(sLevel1,sLevel2) :
                    self.addEdge(self.getLatticeNode(sLevel1), self.getLatticeNode(sLevel2))
                elif self.strictlyDominates(sLevel2,sLevel1) :
                    self.addEdge(self.getLatticeNode(sLevel2), self.getLatticeNode(sLevel1))

                        
    def printLattice(self):
        '''
        Print out the lattice in order of increasing category set size
        '''
        for level in range(0,self.maxLatticeLevel+1) :
            for sLevel in self.securityLevels[level] :
                node = self.getLatticeNode(sLevel)
                print("Security Level = "+node.getNodeIdString())
                print("\tSuccessors: "+node.getSuccessorIdString())
                print("\tPredecessors: "+node.getPredecessorIdString())
                
    def printLatticeToFile(self, fileDir):
        file = open(fileDir, 'w')
        for level in range(0,self.maxLatticeLevel+1) :
            for sLevel in self.securityLevels[level] :
                node = self.getLatticeNode(sLevel)
                file.write("Security Level = "+node.getNodeIdString()+'\n')
                file.write("\tSuccessors: "+node.getSuccessorIdString()+'\n')
                file.write("\tPredecessors: "+node.getPredecessorIdString()+'\n')
                
        file.close()