'''
Created on Nov 3, 2014

@author: Mandy
'''
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from MainWindow import MainWindow
from rbacpolicy import *
import re

# user_role_mat {'sam': set(['sales']), 
#                 'charles': set(['cust']), 
#                 'dave': set(['cust', 'dev']), 
#                 'quinn': set(['qc']), 
#                 'cathy': set(['cust']), 
#                 'dot': set(['dev']), 
#                 'patty': set(['pres'])}
# 
# role_res_mat {'cust': {'/path/to/db': Permset(perms=set(['x', 'r', 'w']), recursive=False)}, 
#                 'qc': {'/path/to/tests': Permset(perms=set(['x']), recursive=False)}, 
#                 'dev': {'/path/to/tests': Permset(perms=set(['r', 'w']), recursive=False)}, 
#                 'pres': {'/path/to/evidence': Permset(perms=set(['r', 'w']), recursive=False)}, 
#                 'sales': {'/path/to/files': Permset(perms=set(['r', 'w']), recursive=True), 
#                           '/path/to/db': Permset(perms=set(['x', 'r', 'w']), recursive=False)}}
#
# role_adjmat {'cust': RoleNode(parents=set(['sales']), children=set([])), 
#                 'qc': RoleNode(parents=set(['sales', 'dev']), children=set([])), 
#                 'dev': RoleNode(parents=set(['pres']), children=set(['qc'])), 
#                 'pres': RoleNode(parents=set([]), children=set(['sales', 'dev'])), 
#                 'sales': RoleNode(parents=set(['pres']), children=set(['qc', 'cust']))}

class RBACForCommandLine(object):

    def __init__(self):
        self.user_role_mat = {}
        self.role_res_mat = {}
        self.role_adjmat = {}
        self.res_adjmat = {}
        self.app = None
        self.main = None
        #self.importPolicy('/Users/manwang/Documents/workspace/RBACvisual/src/policies/test2.rbac')
        #self.initVisualizationForQuery(QString('/Users/manwang/Documents/workspace/RBACvisual/src/policies/test2.rbac'), 1)
            
    def UIConfiForQuery(self, questionId, paras):
        self.main.initInterface()
        self.main.showQueryWindow()
        self.main.queryWindow.setQueryForQuestion(questionId)
        self.main.queryWindow.getQueryResultFromCommandLineInput(questionId, paras)
        self.main.currentAction = self.main.ui.actionView_Separate
        self.main.viewModeChanged(self.main.currentAction)
        
    def startVisualizationWindow(self):
        app = QApplication(sys.argv)
        QCoreApplication.setApplicationName('RBACvisual')
        QCoreApplication.setApplicationVersion('1.0')
        
        QApplication.setStyle('plastique')
        app.setStyleSheet("""QToolTip {
                            background-color:white;
                            color:black;
        }""")
        mainw = MainWindow()
        mainw.show()
        self.main = mainw
        return app
    
    def initVisualizationForQuery(self, filename, questionId, paras=[]):
        #paras is a list containing the input for query interface
        if self.app is None:
            self.app= self.startVisualizationWindow()
        self.main.importSpec(filename)#need changes to avoid repeated reloads
        self.UIConfiForQuery(questionId, paras)
        exitcode = self.app.exec_()
        sys.exit(exitcode)
        self.app = None
    
    def importPolicy(self, filename):
        self.user_role_mat, self.role_res_mat, self.role_adjmat = rbacpolicy(filename)
        self.dirList = self.getDirList()
        self.computeObjectHierarchy()
        self.computeRoleHierarchy()
        #self.queryManager('none','pres', '/path/to/files/omg', set())
    
    def getDirList(self):
        dirList = ['/']
        for i in self.role_res_mat.keys():
            for k in self.role_res_mat[i].keys():
                parents = k.split('/')
                parents.remove('')
                current = ''
                for p in parents:
                    current += '/'+p
                    if current not in dirList:
                        dirList.append(current)
        return dirList
    
    def computeRoleHierarchy(self):
        self.role_adjmatComplete = self.role_adjmat
        for r in self.role_adjmatComplete.keys():
            self.getAllParentRoles(r)
            self.getAllChildRoles(r)
        
    def getAllParentRoles(self, r):
        parents = list(self.role_adjmatComplete[r].parents)
        allParents = self.role_adjmatComplete[r].parents
        while parents!= []:
            currentrole = parents.pop(0)
            rolelist = list(self.role_adjmatComplete[currentrole].parents)
            for p in rolelist:
                if p not in allParents:
                    allParents.add(p)
                    parents.append(p)
        self.role_adjmatComplete[r].parents.update(allParents)
        
    def getAllChildRoles(self, r):
        children = list(self.role_adjmatComplete[r].children)
        allChildren = self.role_adjmatComplete[r].children
        while children!= []:
            currentrole = children.pop(0)
            rolelist = list(self.role_adjmatComplete[currentrole].children)
            for c in rolelist:
                if c not in allChildren:
                    allChildren.add(c)
                    children.append(c)
        self.role_adjmatComplete[r].children.update(allChildren)
        
    def computeObjectHierarchy(self):
        self.res_adjmat = {}
        for n in self.dirList:
            parents = set()
            children = set()
            for p in self.dirList:
                if p!=n:
                    dirList2 = p.split('/')
                    dirList1 = n.split('/')
                    dirList2 = filter(lambda a: a != '', dirList2)
                    dirList1 = filter(lambda a: a != '', dirList1)
                    for i in xrange(len(dirList2)):
                        tempDir2 = '/'+'/'.join(dirList2[:i])
                        if tempDir2 == n:
                            children.add(p)
                    for i in xrange(len(dirList1)):
                        tempDir1 = '/'+'/'.join(dirList1[:i])
                        if tempDir1 == p:
                            parents.add(p)
                            
    def isSubDirectory(self, dirToCompare, dirToDecide):
        dirList = dirToDecide.split('/')
        dirList = filter(lambda a: a != '', dirList)
        for i in xrange(len(dirList)):
            tempDir2 = '/'+'/'.join(dirList[:i])
            if tempDir2 == dirToCompare:
                return True
        return False
                
    def queryManager(self, username, rolename, objname, permissions):
        noneinput = re.compile('none', re.IGNORECASE)
        if re.match(noneinput, username):
            self.queryRoleAccessObj(rolename, objname)
        elif re.match(noneinput, rolename):
            self.queryUserAccessObj(username, objname)
        else:
            self.queryUserAccessObjFromRole(username, rolename, objname)
        
    def queryRoleAccessObj(self, rolename, objname):
        '''add explanation for no access'''
        answer = None
        output = 'Role '+rolename
        if rolename not in self.role_adjmatComplete.keys():
            output += ' does not exist in the policy.\n'
            answer = False
        else:
            rolenodes = list(self.role_adjmatComplete[rolename].children)
            rolenodes.append(rolename)
            permset = set()
            for r in rolenodes:
                if objname in self.role_res_mat[r].keys():
                    for p in self.role_res_mat[r][objname].perms:
                        permset.add(p)
                else:
                    for obj in self.role_res_mat[r].keys():
                        if self.isSubDirectory(obj, objname) and self.role_res_mat[r][obj].recursive:
                            for p in self.role_res_mat[r][obj].perms:
                                permset.add(p)
            if permset:
                permstr = ', '.join(permset)
                output += ' has ' + permstr + ' access to the object '+objname+'.\n'
                answer = True
            else:
                output+= ' has no access to object '+objname+'.\n'
                answer = False
        return answer, output
        
    def queryUserAccessObj(self, username, objname):
        pass
        
    def queryUserAccessObjFromRole(self, username, rolename, objname):
        if username not in self.main.user_role_mat.keys():
            output = 'There is no user as '+username+'.\n'
            answer = False
        else:
            output = 'Role '+rolename
            if rolename in self.main.user_role_mat[username]:
                if objname in self.main.role_res_mat[rolename].keys():
                    permstr = ', '.join(self.main.role_res_mat[rolename][objname].perms)
                    output += ' allows user '+username+' access to the object '+objname+' with permission(s) of '+permstr+'.\n'
                    answer = True
                else:
                    output+= ' has user '+username+' assigned to it but no access to the object '+objname+\
                            '. Therefore, user '+username+' has no access to object '+objname+'.\n'
                    answer = False
            else:
                output+= ' does not have user '+username+' assigned to it. Therefore, user '+\
                        username+' can not access to object '+objname+' through role '+rolename+'.\n'
                answer = False
        return answer, output
        