'''
Created on May 10, 2016

@author: manwang
'''
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import random
from FileNode import FileNode
from DomainNode import DomainNode
from EdgeItem import EdgeItem
from TypeNode import TypeNode
'''general'''
def setFontForUI(widget, size=20):
    import platform
    operSys = platform.system()
    if operSys == 'Linux':
        font = widget.font()
        font.setPointSize(size)
        widget.setFont(font)
    elif operSys == 'Darwin':
        font = QFont('Courier', size)
        widget.setFont(font)
        
class SelfTestScene(QWidget):
    TYPE_FILE_QUES = 0
    TYPE_FILE_DOMAIN_QUES = 1
    BINARY_EXEC_QUES = 2
    BINARY_PERM_QUES = 3
    QUES_INFO = [['What is the type of the specified file?'],\
                 ['What is the default type of a file created from the specified domain?'],\
                 ['Can the binary be successfully executed from the specified domain?', 'Yes', 'No'],\
                 ['Can the binary access the object with the specified permission?', 'Yes', 'No']
                ]
    def __init__(self, parent, main):
        QWidget.__init__(self, parent)
        self.main = main
        self.scene = main.scene
        self.initParam()
        self.setupWidget()
        self.initQuestion()
        
    def initParam(self):
        self.interfaceQuesItems = set()
        self.interfaceTableItems = set()
        self.settingIsChanged = True
        self.chosenAnswer = None
        self.answer = None
        
    def createGraphicsTextItem(self, scene, text, fontsize=20, isQuesItem = True):
        item = QGraphicsTextItem(text)
        setFontForUI(item, fontsize)
        scene.addItem(item)
        if isQuesItem:
            self.interfaceQuesItems.add(item)
        else:
            self.interfaceTableItems.add(item)
        return item
            
    def setupWidget(self):
        self.setupTableViews()
        geom = self.main.centralWidget().geometry()
        screenW, screenH = geom.width(), geom.height()#self.main.view.viewport().width(), self.main.view.viewport().height()
        self.tableViewQuestionConf.setGeometry(0, 0, screenW, 0.5*screenH)
        self.interfaceTableItems.add(self.tableViewQuestionConf)
        
    def setCellText(self, r, c, text, font = QFont('Courier', 18), isBold = False):
        item = QTableWidgetItem(QString(text))
        font.setBold(isBold)
        item.setFont(font)
        item.setTextAlignment(Qt.AlignCenter)
        item.setFlags(item.flags() &~Qt.ItemIsEditable)
        self.tableViewQuestionConf.setItem(r, c, item)
        return item
        
    def createLineEditAndButtonCombo(self, r, c, btnText='Random', isLineEdit = False):
        widget = QWidget()
        hlayout = QHBoxLayout(widget)
        if isLineEdit:
            lineEdit = QLineEdit()
        else:
            lineEdit = QComboBox()
            lineEdit.setSizeAdjustPolicy(QComboBox.AdjustToContents)
            lineEdit.setEditable(True)
            lineEdit.completer().setCompletionMode(QCompleter.PopupCompletion)

        button = QPushButton(btnText)
        hlayout.addWidget(lineEdit)
        hlayout.addWidget(button)
        hlayout.setContentsMargins(0, 0, 0, 0)
        widget.setLayout(hlayout)
        self.tableViewQuestionConf.setCellWidget(r,c,widget)
        return lineEdit, button
    
    def setupTableViews(self):
        self.tableViewQuestionConf = QTableWidget(self.main.view)
        #self.tableViewQuestionConf.setShowGrid(False)
        self.tableViewQuestionConf.setRowCount(2)
        self.tableViewQuestionConf.setColumnCount(5)
        self.tableViewQuestionConf.verticalHeader().setVisible(False)
        self.tableViewQuestionConf.horizontalHeader().setVisible(False)
        '''1'''
        self.tableViewQuestionConf.clear()
        self.setCellText(0, 1, 'File', QFont('Courier', 20))
        self.setCellText(1, 0, 'Name', QFont('Courier', 20))
        self.setCellText(2, 0, '', QFont('Courier', 20))
        self.setCellText(0,3, '', QFont('Courier', 20))
        self.setCellText(0,4, '', QFont('Courier', 20))
        self.setCellText(1,2, '', QFont('Courier', 20))
        self.setCellText(2,2, '', QFont('Courier', 20))
        
        self.lineEdit11, self.randomPBtn11 = self.createLineEditAndButtonCombo(1,1)
        
        self.objDirLineEdit, self.randomPBtnObj = self.createLineEditAndButtonCombo(1,3)
        
        self.permLineEdit, self.permRandomPBtn = self.createLineEditAndButtonCombo(1,4)
        permset = ['r', 'w', 'x', 'c', 'd']
        for p in permset:
            self.permLineEdit.addItem(p)
            
        self.objDirLineEdit.setVisible(False)
        self.randomPBtnObj.setVisible(False)
        self.permLineEdit.setVisible(False)
        self.permRandomPBtn.setVisible(False)
                
        for c in xrange(self.tableViewQuestionConf.columnCount()):
            for r in xrange(self.tableViewQuestionConf.rowCount()): 
                if self.tableViewQuestionConf.item(r, c) == None:
                    self.setCellText(r, c, '')
                if c < 2:
                    color = QColor(234, 250, 241)
                elif c < 4:
                    color = QColor(252, 243, 207)
                else:
                    color = QColor(250, 219, 216)
                self.tableViewQuestionConf.item(r, c).setData(Qt.BackgroundRole, QVariant(QBrush(color)))
        self.tableViewQuestionConf.verticalHeader().setResizeMode(QHeaderView.Stretch)
        self.tableViewQuestionConf.horizontalHeader().setResizeMode(QHeaderView.Stretch)
        
        self.lineEdit11.highlighted.connect(lambda dirIndex: self.showWholePathAsHint(dirIndex))
        self.randomPBtn11.clicked.connect(self.random11)
        self.randomPBtnObj.clicked.connect(self.random13)
        self.permRandomPBtn.clicked.connect(self.randomGetPerm)
        
        self.lineEdit11.editTextChanged.connect(self.settingChanged)
        self.permLineEdit.currentIndexChanged.connect(self.settingChanged)
        self.objDirLineEdit.editTextChanged.connect(self.settingChanged)
        
    def createUIInScene(self, widget, isQuesItem = True):
        proxyWidget = QGraphicsProxyWidget()
        proxyWidget.setWidget(widget)
        proxyWidget.setZValue(1)
        self.scene.addItem(proxyWidget)
        if isQuesItem:
            self.interfaceQuesItems.add(proxyWidget)
            self.interfaceQuesItems.add(widget)
        else:
            self.interfaceTableItems.add(proxyWidget)
            self.interfaceTableItems.add(widget)
        return proxyWidget
    
    def initQuestion(self):        
        self.labelQuestion = QGraphicsTextItem('Question Type: ')
        setFontForUI(self.labelQuestion, 25)
        self.scene.addItem(self.labelQuestion)
        self.interfaceQuesItems.add(self.labelQuestion)
        
        self.labelQuestionText = self.createGraphicsTextItem(self.scene, self.QUES_INFO[self.TYPE_FILE_QUES][0], 25)
        self.quesTextWidth = self.labelQuestionText.boundingRect().width()
            
        self.answerWidget = QWidget()
        self.answerWidget.setStyleSheet("background-color: white")
        answerLayout = QVBoxLayout()
        answerLayout.setMargin(0)
        self.answerRadioBtn1 = QRadioButton('Yes')
        self.answerRadioBtn1.setStyleSheet("background-color: white")
        setFontForUI(self.answerRadioBtn1, 25)
        self.answerRadioBtn2 = QRadioButton('No')
        self.answerRadioBtn2.setStyleSheet("background-color: white")
        setFontForUI(self.answerRadioBtn2, 25)
        self.answerRadioBtn3 = QRadioButton()
        self.answerRadioBtn3.setStyleSheet("background-color: white")
        setFontForUI(self.answerRadioBtn3, 25)
        self.answerRadioBtn4 = QRadioButton()
        self.answerRadioBtn4.setStyleSheet("background-color: white")
        setFontForUI(self.answerRadioBtn4, 25)
        answerLayout.addWidget(self.answerRadioBtn1)
        answerLayout.addWidget(self.answerRadioBtn2)
        answerLayout.addWidget(self.answerRadioBtn3)
        answerLayout.addWidget(self.answerRadioBtn4)
        self.answerWidget.setLayout(answerLayout)
        self.createUIInScene(self.answerWidget)
        self.interfaceQuesItems.add(self.answerRadioBtn1)
        self.interfaceQuesItems.add(self.answerRadioBtn2)
        self.interfaceQuesItems.add(self.answerRadioBtn3)
        self.interfaceQuesItems.add(self.answerRadioBtn4)
        
        self.nextExplainPBtn = QPushButton('Explain')
        setFontForUI(self.nextExplainPBtn, 25)
        self.createUIInScene(self.nextExplainPBtn)
        self.nextExplainPBtn.setStyleSheet("background-color: #328930")
    
        self.nextSkipPBtn = QPushButton('Check')
        setFontForUI(self.nextSkipPBtn, 25)
        self.createUIInScene(self.nextSkipPBtn)
            
        self.correctnessTextItem = self.createGraphicsTextItem(self.scene,'', 25)
                    
        self.explainTextEdit = QTextEdit()
        setFontForUI(self.explainTextEdit, 20)
        self.explainTextEditProxy = self.createUIInScene(self.explainTextEdit)
        
        self.answerRadioBtn1.setText('')
        self.answerRadioBtn2.setText('')
        self.answerRadioBtn3.setText('')
        self.answerRadioBtn4.setText('')
        self.answerRadioBtn1.clicked.connect(self.rBtn1Clicked)
        self.answerRadioBtn2.clicked.connect(self.rBtn2Clicked)
        self.answerRadioBtn3.clicked.connect(self.rBtn3Clicked)
        self.answerRadioBtn4.clicked.connect(self.rBtn4Clicked)
        
        self.nextSkipPBtn.clicked.connect(self.checkAnswer)
        self.nextExplainPBtn.clicked.connect(self.explainAnswer)
            
        self.questionType = QComboBox()
        self.questionType.addItem('Type of File')
        self.questionType.addItem('Domain Default File Type')
        self.questionType.addItem('Binary Execution From Domain')
        self.questionType.addItem('Binary Permission to File')
        setFontForUI(self.questionType, 20)
        self.createUIInScene(self.questionType)
        self.questionType.activated.connect(self.questionTypeChanged)
        
    def updateLayout(self):
        self.scene.setSceneRect(QRectF(self.main.centralWidget().geometry()))
        geomMain = self.main.centralWidget().geometry()
        screenX, screenY = geomMain.x(),geomMain.y()
        screenWidth = geomMain.width()
        screenHeight = geomMain.height()
        ratio = 0.3
        self.tableViewQuestionConf.setGeometry(0, 0, screenWidth, ratio*screenHeight)
        self.labelQuestion.setPos(screenX+10,\
                                  screenY+(ratio+0.01)*(screenHeight))
        if self.quesTextWidth > screenWidth:
            self.labelQuestionText.setTextWidth(screenWidth)
        else:
            self.labelQuestionText.setTextWidth(self.quesTextWidth)
           
        xinter = self.labelQuestionText.boundingRect().width()
        self.labelQuestionText.setPos(screenX+ratio*(screenWidth-xinter),\
                                    self.labelQuestion.pos().y()+self.labelQuestion.boundingRect().height()+5)
        self.answerWidget.setGeometry(self.labelQuestionText.pos().x()+10, self.labelQuestionText.pos().y()+self.labelQuestionText.boundingRect().height()+5, \
                                           self.answerWidget.geometry().width(), self.answerWidget.geometry().height())
        xinter = self.nextExplainPBtn.geometry().width()
        yinter = self.nextExplainPBtn.geometry().height()
        self.nextExplainPBtn.setGeometry(screenX+screenWidth-10-xinter, \
                                         screenY+screenHeight-10-yinter, \
                                         xinter, yinter)
        xinter = self.nextSkipPBtn.geometry().width()
        self.nextSkipPBtn.setGeometry(self.nextExplainPBtn.geometry().x()-xinter-10, self.nextExplainPBtn.geometry().y(), \
                                         self.nextExplainPBtn.geometry().width(), self.nextSkipPBtn.geometry().height())
        self.correctnessTextItem.setPos(self.nextSkipPBtn.geometry().x(), self.nextSkipPBtn.geometry().y()-self.nextSkipPBtn.geometry().height()-5)
        self.questionType.setGeometry(self.labelQuestion.pos().x()+self.labelQuestion.boundingRect().width(), \
                                      screenY+(ratio+0.01)*screenHeight, \
                                      self.questionType.geometry().width(), \
                                      self.questionType.geometry().height())
        if self.questionType.currentIndex() == self.TYPE_FILE_DOMAIN_QUES or \
            self.questionType.currentIndex() == self.TYPE_FILE_QUES:
            self.answerRadioBtn3.setVisible(True)
            self.answerRadioBtn4.setVisible(True)
        else:
            self.answerRadioBtn3.setVisible(False)
            self.answerRadioBtn4.setVisible(False)
        y = self.answerWidget.geometry().y()+self.answerWidget.geometry().height()+5
        self.explainTextEditProxy.setGeometry(QRectF(self.labelQuestionText.pos().x(), y, \
                                         self.correctnessTextItem.pos().x() - self.answerWidget.pos().x() - 20, \
                                         max(self.correctnessTextItem.pos().y()-y-5, 0)))
         
    def setVisibilityOfSceneItems(self, flag):
        for i in self.interfaceQuesItems:
            i.setVisible(flag)
        self.tableViewQuestionConf.setVisible(flag)
        for i in self.interfaceTableItems:
            i.setVisible(flag)
        
    def setTableBackgroundColor(self, endr, endc, isEnabled):
        for c in xrange(endc):
            for r in xrange(endr): 
                item = self.tableViewQuestionConf.item(r, c)
                if isEnabled:
                    if c < 2:
                        color = QColor(234, 250, 241)
                    elif c < 4:
                        color = QColor(252, 243, 207)
                    else:
                        color = QColor(250, 219, 216)
                else:
                    color = Qt.lightGray
                item.setData(Qt.BackgroundRole, QVariant(QBrush(color)))
                
    def questionTypeChanged(self):
        qtype = self.questionType.currentIndex()
        if qtype == self.TYPE_FILE_QUES:
            self.typeFileUIsetting()
        elif qtype == self.TYPE_FILE_DOMAIN_QUES:
            self.typeFileDomainUIsetting()
        elif qtype == self.BINARY_EXEC_QUES:
            self.binaryExecUIsetting()
        elif qtype == self.BINARY_PERM_QUES:
            self.binaryPermUIsetting()
        self.updateUIContents()
        self.updateLayout()
#         self.settingChanged()
        
    def generateChoices(self):
        if self.questionType.currentIndex() == self.TYPE_FILE_QUES:
            self.generateChoicesForTypeFileQues()
        elif self.questionType.currentIndex() == self.TYPE_FILE_DOMAIN_QUES:
            self.generateChoicesForTypeFileDomainQues()
        elif self.questionType.currentIndex() == self.BINARY_EXEC_QUES:
            self.generateChoicesForBinaryExecQues()
        elif self.questionType.currentIndex() == self.BINARY_PERM_QUES:
            self.generateChoicesForBinaryPermQues()
   
    def generateChoicesForTypeFileQues(self):
        types = []
        for t in self.scene.items():
            if isinstance(t, TypeNode):
                types.append(t.name)
        count = len(types)
        index = [0,1,2,3]
        choices = ['']*4
        if self.main.hasInfo:
            i = random.choice(index)
            choices[i] = self.answer
            index.remove(i)
            self.answer = i
            length = len(index)
            for j in xrange(length):
                if j+1 < count-1:
                    i = random.choice(index)
                    k = random.randrange(count)
                    item = types[k]
                    tryCount = 100
                    while(item in choices and tryCount > 0):
                        k = random.randrange(count)
                        item = types[k]
                        tryCount -= 1
                    choices[i] = item
                    index.remove(i)
        self.answerRadioBtn1.setText(choices[0])
        self.answerRadioBtn2.setText(choices[1])
        self.answerRadioBtn3.setText(choices[2])
        self.answerRadioBtn4.setText(choices[3])
        
    def generateChoicesForTypeFileDomainQues(self):
        types = ['No write access']
        for t in self.scene.items():
            if isinstance(t, TypeNode):
                types.append(t.name)
        count = len(types)
        index = [0,1,2,3]
        choices = ['']*4
        if self.main.hasInfo:
            i = random.choice(index)
            choices[i] = self.answer
            index.remove(i)
            self.answer = i
            length = len(index)
            for j in xrange(length):
                if j+1 < count-1:
                    i = random.choice(index)
                    k = random.randrange(count)
                    item = types[k]
                    tryCount = 100
                    while(item in choices and tryCount > 0):
                        k = random.randrange(count)
                        item = types[k]
                        tryCount -= 1
                    choices[i] = item
                    index.remove(i)
        self.answerRadioBtn1.setText(choices[0])
        self.answerRadioBtn2.setText(choices[1])
        self.answerRadioBtn3.setText(choices[2])
        self.answerRadioBtn4.setText(choices[3])
             
    def generateChoicesForBinaryExecQues(self):
        self.answerRadioBtn1.setText(self.QUES_INFO[self.BINARY_EXEC_QUES][1])
        self.answerRadioBtn2.setText(self.QUES_INFO[self.BINARY_EXEC_QUES][2])
    
    def generateChoicesForBinaryPermQues(self):
        self.answerRadioBtn1.setText(self.QUES_INFO[self.BINARY_PERM_QUES][1])
        self.answerRadioBtn2.setText(self.QUES_INFO[self.BINARY_PERM_QUES][2])
        
    def typeFileUIsetting(self):
        '''
        0 |     1        | 2 |  3   |  4
        0 | File    1    | 2 |      | 
        1 | Name         |   |      | 
        '''
        self.tableViewQuestionConf.item(0,1).setText('File')
        self.tableViewQuestionConf.item(1,0).setText('Name')
        self.tableViewQuestionConf.item(0,3).setText('')
        self.tableViewQuestionConf.item(0,4).setText('')
        self.tableViewQuestionConf.item(1,2).setText('')
        
        self.objDirLineEdit.setVisible(False)
        self.randomPBtnObj.setVisible(False)
        self.permLineEdit.setVisible(False)
        self.permRandomPBtn.setVisible(False)
        self.labelQuestionText.setPlainText(self.QUES_INFO[self.TYPE_FILE_QUES][0])
        self.quesTextWidth = QFontMetrics(self.labelQuestionText.font()).width(self.QUES_INFO[self.TYPE_FILE_QUES][0])
        
    def typeFileDomainUIsetting(self):
        '''
        0 |     1           | 2 |  3   |  4
        0 |   Domain        | 2 |  File    | 
        1 |   name          |  name |      | 
        '''
        self.tableViewQuestionConf.item(0,1).setText('Domain')
        self.tableViewQuestionConf.item(1,0).setText('Name')
        self.tableViewQuestionConf.item(0,3).setText('File')
        self.tableViewQuestionConf.item(1,2).setText('Name')
        self.tableViewQuestionConf.item(0,4).setText('')
        self.objDirLineEdit.setVisible(True)
        self.randomPBtnObj.setVisible(True)
        self.permLineEdit.setVisible(False)
        self.permRandomPBtn.setVisible(False)
        self.labelQuestionText.setPlainText(self.QUES_INFO[self.TYPE_FILE_DOMAIN_QUES][0])
        self.quesTextWidth = QFontMetrics(self.labelQuestionText.font()).width(self.QUES_INFO[self.TYPE_FILE_DOMAIN_QUES][0])

    def binaryExecUIsetting(self):
        '''
        0 |     1           | 2     |  3       |  4
        0 |   Binary        | 2     |  Domain  | 
        1 |   name          | name  |          | 
        '''
        self.tableViewQuestionConf.item(0,1).setText('Binary')
        self.tableViewQuestionConf.item(1,0).setText('Name')
        self.tableViewQuestionConf.item(0,3).setText('Domain')
        self.tableViewQuestionConf.item(1,2).setText('Name')
        self.tableViewQuestionConf.item(0,4).setText('')
        self.objDirLineEdit.setVisible(True)
        self.randomPBtnObj.setVisible(True)
        self.permLineEdit.setVisible(False)
        self.permRandomPBtn.setVisible(False)
        self.labelQuestionText.setPlainText(self.QUES_INFO[self.BINARY_EXEC_QUES][0])
        self.quesTextWidth = QFontMetrics(self.labelQuestionText.font()).width(self.QUES_INFO[self.BINARY_EXEC_QUES][0])
    
    def binaryPermUIsetting(self):
        '''
        0 |     1           | 2     |  3       |  4
        0 |   Binary        | 2     |  Domain  | Permission
        1 |   name          | name  |          | 
        '''
        self.tableViewQuestionConf.item(0,1).setText('Binary')
        self.tableViewQuestionConf.item(1,0).setText('Name')
        self.tableViewQuestionConf.item(0,3).setText('File')
        self.tableViewQuestionConf.item(1,2).setText('Name')
        self.tableViewQuestionConf.item(0,4).setText('Permission')
        self.objDirLineEdit.setVisible(True)
        self.randomPBtnObj.setVisible(True)
        self.permLineEdit.setVisible(True)
        self.permRandomPBtn.setVisible(True)
        self.labelQuestionText.setPlainText(self.QUES_INFO[self.BINARY_PERM_QUES][0])
        self.quesTextWidth = QFontMetrics(self.labelQuestionText.font()).width(self.QUES_INFO[self.BINARY_PERM_QUES][0])
        
    def resetAnimatedItems(self):
        self.main.animationStep = 0
        self.explainTextEdit.clear()
            
    def onAnimateSelfTest(self):
        if self.main.animationStep < len(self.msgAnalysis):
            for i in self.msgAnalysis[self.main.animationStep]:
                self.updateExplainText(i)
            cursor = self.explainTextEdit.textCursor()
            cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
            self.explainTextEdit.setTextCursor(cursor)       
        
    def updateUIContents(self):
        if self.main.hasInfo:
            if self.questionType.currentIndex() == self.TYPE_FILE_QUES:
                self.lineEdit11.blockSignals(True)
                self.lineEdit11.clear()
                for item in self.scene.items():
                    if isinstance(item, FileNode):
                        self.lineEdit11.addItem(item.fullPath)
                self.lineEdit11.blockSignals(False)
            elif self.questionType.currentIndex() == self.TYPE_FILE_DOMAIN_QUES:
                '''domain'''
                self.lineEdit11.blockSignals(True)
                self.lineEdit11.clear()
                for item in self.scene.items():
                    if isinstance(item, DomainNode):
                        self.lineEdit11.addItem(item.name)
                self.lineEdit11.blockSignals(False)
                '''file'''
                self.objDirLineEdit.blockSignals(True)
                self.objDirLineEdit.clear()
                for item in self.scene.items():
                    if isinstance(item, FileNode):
                        self.objDirLineEdit.addItem(item.fullPath)
                self.objDirLineEdit.blockSignals(False)
            elif self.questionType.currentIndex() == self.BINARY_EXEC_QUES:
                '''bin'''
                self.lineEdit11.blockSignals(True)
                self.lineEdit11.clear()
                for item in self.scene.items():
                    if isinstance(item, FileNode):
                        self.lineEdit11.addItem(item.fullPath)
                self.lineEdit11.blockSignals(False)
                '''domain'''
                self.objDirLineEdit.blockSignals(True)
                self.objDirLineEdit.clear()
                for item in self.scene.items():
                    if isinstance(item, DomainNode):
                        self.objDirLineEdit.addItem(item.name)
                self.objDirLineEdit.blockSignals(False)
            elif self.questionType.currentIndex() == self.BINARY_PERM_QUES:
                self.lineEdit11.blockSignals(True)
                self.lineEdit11.clear()
                '''bin'''
                for item in self.scene.items():
                    if isinstance(item, FileNode):
                        self.lineEdit11.addItem(item.fullPath)
                self.lineEdit11.blockSignals(False)
                self.objDirLineEdit.blockSignals(True)
                self.objDirLineEdit.clear()
                for item in self.scene.items():
                    if isinstance(item, FileNode):
                        self.objDirLineEdit.addItem(item.fullPath)
                self.objDirLineEdit.blockSignals(False)
            self.settingChanged()
            
    '''handlers'''        
    def showWholePathAsHint(self, dirIndex):
        self.lineEdit11.setItemData(dirIndex, str(self.lineEdit11.itemText(dirIndex)), Qt.ToolTipRole)
    
    '''change'''
    def random11(self):
        if not self.main.hasInfo:
            QMessageBox.critical(self.main, '', 'Please import a policy or visualization file!')
            return
        index = random.randrange(self.lineEdit11.count())
        self.lineEdit11.setCurrentIndex(index)
        self.settingChanged()
    
    def random13(self):
        if not self.main.hasInfo:
            QMessageBox.critical(self.main, '', 'Please import a policy or visualization file!')
            return
        index = random.randrange(self.objDirLineEdit.count())
        self.objDirLineEdit.setCurrentIndex(index)
        
    def randomGetPerm(self):
        if not self.main.hasInfo:
            QMessageBox.critical(self.main, '', 'Please import a policy or visualization file!')
            return
        perm = random.randrange(self.permLineEdit.count())
        self.permLineEdit.setCurrentIndex(perm)
        self.settingChanged()
        
    def radioButtonSetChecked(self, rbtn, flag):
        rbtn.setAutoExclusive(False)
        rbtn.setChecked(flag)
        rbtn.setAutoExclusive(True)
        
    def rBtn1Clicked(self):
        self.chosenAnswer = 0
        self.radioButtonSetChecked(self.answerRadioBtn2, False)
        self.radioButtonSetChecked(self.answerRadioBtn3, False)
        self.radioButtonSetChecked(self.answerRadioBtn4, False)
        
    def rBtn2Clicked(self):
        self.chosenAnswer = 1
        self.radioButtonSetChecked(self.answerRadioBtn1, False)
        self.radioButtonSetChecked(self.answerRadioBtn3, False)
        self.radioButtonSetChecked(self.answerRadioBtn4, False)

    def rBtn3Clicked(self):
        self.chosenAnswer = 2
        self.radioButtonSetChecked(self.answerRadioBtn1, False)
        self.radioButtonSetChecked(self.answerRadioBtn2, False)
        self.radioButtonSetChecked(self.answerRadioBtn4, False)
        
    def rBtn4Clicked(self):
        self.chosenAnswer = 3
        self.radioButtonSetChecked(self.answerRadioBtn1, False)
        self.radioButtonSetChecked(self.answerRadioBtn2, False)
        self.radioButtonSetChecked(self.answerRadioBtn3, False)
        
    def settingChanged(self):
        self.resetAnimatedItems()
        self.main.animationStep = 0
        self.correctnessTextItem.setHtml("")
        self.explainTextEdit.clear()
        self.settingIsChanged = True
        self.nextExplainPBtn.setText('Explain')
        self.nextExplainPBtn.setStyleSheet('QPushButton {background-color: #328930;}')
        self.reanalyze()
        self.generateChoices()          
    
    def reanalyze(self):
        self.msgAnalysis = []
        if self.questionType.currentIndex() == self.TYPE_FILE_QUES:
            filename = str(self.lineEdit11.currentText())
            filenode = self.main.queryWindow.findFileType(filename)
            if filenode:
                filetype = filenode.type.name
                fileflag = filenode.flag
            else:
                filetype = 'None'
                fileflag = -1
            temp = ['- A file can only be assigned to one type. The type of a file is determined by:']
            temp.append('\n 1. Look through all the parent paths to the file, the type of the file is the recursively assigned type to the longest parent path of the file.')
            temp.append('\n 2. If along the paths, there is a static recursive type assignment, then the type of the file is the type at the static assignment.')
            temp.append('\n 3. If no recursive assignment exists and there is direct type assignment to the file, the type is the directly assigned.')
            self.msgAnalysis.append(temp)
            temp = ['\n- The file is \'%s\'.'%filename]
            self.msgAnalysis.append(temp)
            temp=['\n- Check static recursive type assignment to parent paths:']
            if fileflag == 3 and filenode.fullPath!=filename:
                temp.append('\n There is a static recursive static type assignment to parent path \'%s\'.'%(filenode.fullPath))
                temp.append('\n Its type of file \'%s\' is \'%s\'.'%(filename, filetype))
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([filetype])
                return
            temp.append('\n There is no static recursive type assignment to any parent path.')
            self.msgAnalysis.append(temp)
            temp=['\n- Check recursive type assignment to parent paths:']
            if fileflag != -1 and filenode.fullPath!=filename:
                temp.append('\n The longest parent path that has recursive type assignment is \'%s\'.'%(filenode.fullPath))
                temp.append('\n Its type of file \'%s\' is \'%s\'.'%(filename, filetype))
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([filetype])
                return
            temp.append('\n There is no recursive type assignment to its parent paths.')
            self.msgAnalysis.append(temp)
            temp = ['\n- Check the existence of a direct type assignment:']
            if fileflag != -1 and filenode.fullPath == filename:
                temp.append('\n There is a direct type assignment.')
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([filetype])
                return
            temp.append('\n There is no direct type assignment.')
            temp.append('\n Therefore, the file does not have any type assignment to its parent paths and itself.')
            self.concludeAnalysis([filetype])
            return
        elif self.questionType.currentIndex() == self.TYPE_FILE_DOMAIN_QUES:
            domainname = str(self.lineEdit11.currentText())
            filename = str(self.objDirLineEdit.currentText())
            success, fileType, defaultType, fileNode = self.main.queryWindow.runQuery9(domainname, filename)
            fileFlag = fileNode.flag
            temp = ['- The process of finding type of a file created from a domain by default at a certain path is:']
            temp.append('\n 1. Check the type of the file and make sure the domain has write access to the type.')
            temp.append('\n 2. If step 1 fails, exit. Otherwise, find the type that the domain has create access to.')
            temp.append('\n 3. If there is no default creation type, the resulting default type is the file type in step 1.')
            temp.append('\n 4. If there is default creation type and the file type in step 1 is not statically assigned, the resulting type is the default creation type.')
            temp.append('\n 5. In step 4, if the file type is statically assigend, the resulting type is the file type in step 1.')
            self.msgAnalysis.append(temp)
            temp = ['\n- Check the type of the file:']
            statical = 'not'
            if (fileFlag == 1 or fileFlag == 3):
                statical = ''
            temp.append('\n The type of \'%s\' is \'%s\' and it is %s statically assigned.'%(filename, fileType.name, statical))
            self.msgAnalysis.append(temp)
            temp = ['\n- Check domain\'s access to the type:']
            temp.append('\n The domain is \'%s\'.'%domainname)
            if success:
                temp.append('\n It has write access to type \'%s\'.'%(fileType.name))
                self.msgAnalysis.append(temp)
            else:
                temp.append('\n It does not have write access to type \'%s\'.'%(fileType.name))
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([success, fileType, defaultType, fileFlag])
                return
            temp = ['\n- Check domain\'s default creation type:']
            if defaultType:
                temp.append('\n There is an edge with \'c\' flag going out of domain \'%s\'.'%domainname)
                temp.append('\n The default type created from the domain is \'%s\'.'%defaultType.name)
            else:
                temp.append('\n There is no edge with \'c\' flag going out of domain \'%s\'.'%domainname)
                temp.append('\n There is no default type created from the domain.')
                temp.append('\n The resulting default type created from the domain hence is the type of the file.')
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([success, fileType, defaultType, fileFlag])
                return
            self.msgAnalysis.append(temp)
            if statical == '':
                temp = ['\n Since the file is statically assigned to a type, the resulting default type created from the domain hence is forced to be the type of the file.']
            else:
                temp = ['\n Since the file is not statically assigned to a type, the resulting default type created from the domain hence is the type connected to the domain with \'c\' flag.']
            self.msgAnalysis.append(temp)
            self.concludeAnalysis([success, fileType, defaultType, fileFlag])
            return
        elif self.questionType.currentIndex() == self.BINARY_EXEC_QUES:
            temp = []
            temp.append('- A binary can be executed from a domain if:')
            temp.append('\n 1. The type the binary belongs to can be executed through the domain itself or any domain that has auto and exec transition from the domain.')
            temp.append('\n 2. The binary is an entry point program of the domain or any domain that has auto and exec transition from the domain.')
            self.msgAnalysis.append(temp)
            '''1'''
            temp = []
            filename = str(self.lineEdit11.currentText())
            domainname = str(self.objDirLineEdit.currentText())
            filename, success, domain, fileType, edge, resultingDomains, entryPoints = \
            self.main.queryWindow.runQuery10(domainname, filename)
            temp.append('\n- The type of \'%s\' is \'%s\'.' % (filename, fileType.name))
            self.msgAnalysis.append(temp)
            '''2'''
            temp = ['\n- Checking auto transition:']
            autoDomains = set()
            for e in domain.edgeList:
                if e.startItem == domain and e.type == EdgeItem.AUTO_CONN:
                    autoDomains.add(e.endItem.name)
            if autoDomains:
                    temp.append('\n Auto transition found from \'%s\' to %s.'%(edge.startItem.name, ', '.join('\''+c+'\'' for c in autoDomains)))
            if edge.description != '' and edge.type == EdgeItem.AUTO_CONN:
                temp.append('\n File \'%s\' is an entry point to domain \'%s\' through auto transition.' %\
                                (filename, edge.endItem.name))
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([success])
                return
            if autoDomains:
                temp.append('\n The binary is no entry point of any auto transition.')
            else:
                temp.append('\n There is no auto transition out of the domain.')
            self.msgAnalysis.append(temp)
            ''''''
            temp = ['\n- Checking exec transition:']
            execDomains = set()
            for e in domain.edgeList:
                if e.startItem == domain and e.type == EdgeItem.EXEC_CONN:
                    execDomains.add(e.endItem.name)
            if execDomains:
                temp.append('\n Exec transition found from \'%s\' to %s.'%(edge.startItem.name, ', '.join('\''+c+'\'' for c in execDomains)))
            if edge.description != '' and edge.type == EdgeItem.EXEC_CONN:
                temp.append('\n File \'%s\' is an entry point to domain \'%s\' through exec transition.' %\
                                (filename, edge.endItem.name))
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([success])
                return
            if execDomains:
                temp.append('\n The binary is no entry point of any exec transition.')
            else:
                temp.append('\n There is no exec transition out of the domain.')
            self.msgAnalysis.append(temp)
            ''''''
            temp = []
            temp.append('\n- Checking the permission of the domain to the binary type:')
            if edge.description != '':
                if edge.startItem is domain and edge.endItem is fileType:
                    temp.append('\n Executing \'%s\' from domain \'%s\' is successful because the domain has \'execute\' permission on type \'%s\'.\n The resulting domain is the domain itself.' % \
                                    (filename, domain.name, fileType.name))
                    self.msgAnalysis.append(temp)
                    self.concludeAnalysis([success])
                    return
            temp.append('\n Domain \'%s\' does not have \'executable\' permission on type \'%s\'.' % \
                        (domain.name, fileType.name))
            temp.append('\n File \'%s\' is neither in any type which domain \'%s\' has \'executable\' permission on, nor an entry point program which can be executed by the domain.' % \
                            (filename, domain.name))
            self.msgAnalysis.append(temp)
            self.concludeAnalysis([success])
            return
        elif self.questionType.currentIndex() == self.BINARY_PERM_QUES:
            '''if a binary can access a file in certain mode'''
            binary = str(self.lineEdit11.currentText())
            filename = str(self.objDirLineEdit.currentText())
            mode = str(self.permLineEdit.currentText())
            typeNode1, typeNode2, domains, domainsWithEntryPoint, resultDomains = \
            self.main.queryWindow.runQuery4(binary, filename, mode)
            '''wording'''
            '''0'''
            temp = ['- Check if a binary can access a file in certain mode, we need to check:']
            temp.append('\n 1. The domains that can execute the type this binary belongs to.')
            temp.append('\n 2. Find the domains among the ones in step 1 that can access the file in the given mode.')
            temp.append('\n 3. The domains that can execute the binary as an entry point to other domains.')
            temp.append('\n 4. Find the domains among the ones after domain transitions in step 3 that can access the file in the given mode. ')
            temp.append('\n 5. If the given mode is \'x\', check if the domains from steps 1 and 3 have the file as entry point program to other domains.')
            self.msgAnalysis.append(temp)
            temp = ['\n- Check domains that can execute the type this binary blongs to...']
            temp.append('\n The binary is \'%s\'.'%binary)
            temp.append('\n Its type is \'%s\'.'%typeNode1.name)
            temp.append('\n Domain(s) %s have executable permission on type %s.' % (str([str(d.name) for d in domains]), str(typeNode1.name)))
            self.msgAnalysis.append(temp)
            temp = ['\n- Check these domains\' access to the file...']
            effectiveDomains = []
            for dlist in resultDomains:
                if len(dlist) == 1:
                    for d in dlist:
                        effectiveDomains.append(d.name)
            if effectiveDomains:
                temp.append('\n Domain(s) %s among %s have permission %s on type %s.' % \
                (str(effectiveDomains), 
                 str([str(d.name) for d in domains]),
                 str(mode),
                 typeNode2.name))
            else:
                temp.append('\n No domain among %s have permission %s on type %s.' % \
                (str([str(d.name) for d in domains]),
                 str(mode),
                 typeNode2.name))
            self.msgAnalysis.append(temp)
            temp = ['\n- Check if domains can execute the binary as entry point program to get to other domains...']
            self.msgAnalysis.append(temp)
            if domainsWithEntryPoint:
                temp=['\n Domain(s) %s can execute the binary to transition to another domain.' % \
                    (str([d.name for d in domainsWithEntryPoint]))]
                self.msgAnalysis.append(temp)
                temp = ['\n- Check the resulting domains that have the given access to the file...']
                transferDomains = []
                for dlist in resultDomains:
                    if len(dlist) == 2:
                        transferDomains.append(str([d.name for d in dlist]))
                if transferDomains:
                    temp.append('\n The transitions between domains %s can execute the binary as entry point and the resulting domain has permission %s on type %s' % \
                    (str(transferDomains),\
                     str(mode),\
                     typeNode2.name))
                else:
                    temp.append('\n The resulting domains of the transition(s) can not access the file with permission %s'%\
                     str(mode))
                self.msgAnalysis.append(temp)
                if mode == 'x':
                    temp = ['\n- For mode \'x\', the file can be an entry point program. We should check if the resulting domains after running the binary from domain(s) %s can run the file as entry points...'%(','.join(d.name for d in domains))]
                    transferDomains = []
                    for dlist in resultDomains:
                        if len(dlist) == 3:
                            transferDomains.append(str([d.name for d in dlist]))
                    if transferDomains:
                        temp.append('\n In the transition(s) between domains %s, the first domain can execute the binary as entry point to get to the second domain, and then the second domain can execute the file as entry point program to get to the third domain.' % \
                        (str(transferDomains)))
                    else:
                        temp.append('\n No transition satisfies.')
                    self.msgAnalysis.append(temp)
                self.concludeAnalysis([binary, filename, mode, resultDomains])
                return
            else:
                temp = ['\n %s is not an entry point program to transition from domain(s) \'%s\' to any other domain.'%(binary, ','.join(d.name for d in domains))]
                self.msgAnalysis.append(temp)
                self.concludeAnalysis([binary, filename, mode, resultDomains])
                return
            
    def concludeAnalysis(self, param):
        if self.questionType.currentIndex() == self.TYPE_FILE_QUES:
            filetype = param[0]
            self.answer = filetype
            if filetype == 'None':
                self.msgAnalysis.append('\n- Conclusion:','\n The file is not assigned to any type.')
            else:
                self.msgAnalysis.append(['\n- Conclusion:', '\n The type is \'%s\'.'%filetype])
        elif self.questionType.currentIndex() == self.TYPE_FILE_DOMAIN_QUES:
            success, fileType, defaultType, fileFlag = param[0], param[1], param[2], param[3]
            domainname = str(self.lineEdit11.currentText())
            filename = str(self.objDirLineEdit.currentText())
            if not success:
                self.answer = 'No write access'
                self.msgAnalysis.append(['\n- Conclusion:', '\n Domain \'%s\' does not have write access to the file thus can not create file \'%s\'.'%(domainname, filename)])
            else:
                if defaultType is None or fileType is defaultType:
                    self.answer = fileType.name
                else:
                    if fileFlag == 1 or fileFlag == 3:
                        self.answer = fileType.name
                    else:
                        self.answer = defaultType.name
                self.msgAnalysis.append(['\n- Conclusion:', '\n The default type of file \'%s\' created from Domain \'%s\' is \'%s\'.'%(filename, domainname, self.answer)])
        elif self.questionType.currentIndex() == self.BINARY_EXEC_QUES:
            success = param[0]
            if success:
                self.answer = 0
                self.msgAnalysis.append(['\n- Conclusion:', '\n The answer is \'Yes\'.'])
            else:
                self.answer = 1
                self.msgAnalysis.append(['\n- Conclusion:', '\n The answer is \'No\'.'])
        elif self.questionType.currentIndex() == self.BINARY_PERM_QUES:
            binary, filename, mode, resultDomains = param[0], param[1], param[2], param[3]
            if resultDomains:
                self.answer = 0
                condition = ''
                domains = []
                domaintrans = []
                for dlist in resultDomains:
                    if len(dlist) == 1:
                        domains.append(dlist[0].name)
                    else:
                        domaintrans.append(' to '.join(d.name for d in dlist))
                if domains:
                    condition += 'domain(s): ' + ','.join(domains)
                if domaintrans:
                    if condition:
                        condition += ', and '
                    condition += 'domain transition: '+ ','.join(d for d in domaintrans)
                self.msgAnalysis.append(['\n- Conclusion:', '\n The binary can access the file at \'%s\' mode through %s.'%(mode, condition),'\n The answer is \'Yes\'.'])
            else:
                self.answer = 1
                self.msgAnalysis.append(['\n- Conclusion:', '\n No domain or domain transition can run the binary and allow \'%s\' access to the file at the same time.'%mode, \
                                         '\n The answer is \'No\'.'])

    def checkAnswer(self):
        if not self.main.hasInfo:
            QMessageBox.critical(self.main, '', 'Please import a policy or visualization file!')
            return
        if self.chosenAnswer == None:
            QMessageBox.critical(self.main, '', 'Please choose an answer!')
            return
        if self.answer == self.chosenAnswer:
            self.correctnessTextItem.setHtml('<font color="green" size="3">Correct</font>')
        else:
            self.correctnessTextItem.setHtml('<font color="red" size="3">Wrong</font>')
            
    def updateExplainText(self, text):
        self.msg += text
        self.explainTextEdit.setText(self.msg)
        
    def explainAnswer(self):
        if str(self.nextExplainPBtn.text()) == 'End':
            self.nextExplainPBtn.setText('Explain')
            self.nextExplainPBtn.setStyleSheet('QPushButton {background-color: #328930;}')
            self.resetAnimatedItems()
            return
        if str(self.nextExplainPBtn.text()) == 'Explain':
            self.nextExplainPBtn.setText('Next')
            self.nextExplainPBtn.setStyleSheet('QPushButton {background-color: yellow;}')
            if not self.main.hasInfo:
                QMessageBox.critical(self.main, '', 'Please import a policy or visualization file!')
                return
            self.msg = ''
            self.main.animationStep = 0
        self.onAnimateSelfTest()
        if self.main.animationStep == len(self.msgAnalysis):
            self.nextExplainPBtn.setText('End')
            self.nextExplainPBtn.setStyleSheet('QPushButton {background-color: red;}')
        self.main.animationStep += 1