'''
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 Dec 19, 2011

@author: yifli
'''
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtHelp import QHelpEngine
from Ui_MainWindow import Ui_MainWindow
from AddNodeCommand import AddNodeCommand
from AddEdgeCommand import AddEdgeCommand
from DeleteNodeCommand import DeleteNodeCommand
from DeleteEdgeCommand import DeleteEdgeCommand 
from MoveCommand import MoveCommand
from MarkInitialDomainCommand import MarkInitialDomainCommand
from ChangeNameCommand import ChangeNameCommand
from ChangeTypeCommand import ChangeTypeCommand
from ChangeFlagCommand import ChangeFlagCommand
from AddFileNodeCommand import AddFileNodeCommand
from DeleteTypeCommand import DeleteTypeCommand
from ChangeAssignmentCommand import ChangeAssignmentCommand
from DiagramIOHelper import DiagramIOHelper
from GeneralGraphLayoutStrategy import GeneralGraphLayoutStrategy
from RadialTreeLayout import RadialTreeLayout
from QueryWindow import QueryWindow
from DomainNode import DomainNode
from TypeNode import TypeNode
from FileNode import FileNode
from EdgeItem import EdgeItem
from Assistant import Assistant
from SpecDialog import SpecDialog
from HelpBrowser import HelpBrowser
from DiagramScene import DiagramScene
from AutogradingTest import AutogradingTest
from SelfTestScene import SelfTestScene
import Rc_icons
import sys, os, datetime, errno

class DiagramView(QGraphicsView):
    def __init__(self, scene, parent = None):
        QGraphicsView.__init__(self, scene, parent)
        self.generalGraphScaleX = 1.0
        self.generalGraphScaleY = 1.0
         
        self.typeGraphScaleX = 1.0
        self.typeGraphScaleY = 1.0
        
        self.selfTestViewScaleX = 1.0
        self.selfTestViewScaleY = 1.0
        
        self.main = parent
        
    def fitGeneralGraph(self):
#        topLeftX = self.viewport().width()
#        topLeftY = self.viewport().height()
#        
#        bottomRightX = 0
#        bottomRightY = 0
#        for item in self.scene.items():
#            if isinstance(item, TypeNode) or isinstance(item, DomainNode):
#                p = item.pos()
#                #print 'domain', p
#                if p.x() < topLeftX:
#                    topLeftX = p.x()
#                if p.y() < topLeftY:
#                    topLeftY = p.y()
#                if p.x() > bottomRightX:
#                    bottomRightX = p.x()
#                if p.y() > bottomRightY:
#                    bottomRightY = p.y()
#        topLeftX *= 0.9
#        topLeftY *= 0.9
#        bottomRightX *= 1.5
#        bottomRightY *= 1.5
#        self.fitInView(topLeftX, topLeftY, 1*(bottomRightX-topLeftX), 1*(bottomRightY-topLeftY), mode = Qt.KeepAspectRatio)
#        #self.fitInView(topLeftX, topLeftY, 0.9*(-bottomRightX+topLeftX), 0.9*(-bottomRightY+topLeftY), mode = Qt.KeepAspectRatio)
#        self.centerOn(0, 0)
        topLeftX = 5000
        topLeftY = 5000
        bottomRightX = 0
        bottomRightY = 0
        for item in self.scene().items():
            if isinstance(item, TypeNode) or isinstance(item, DomainNode):
                p = item.pos()
                if p.x() < topLeftX:
                    topLeftX = p.x()
                if p.y() < topLeftY:
                    topLeftY = p.y()
                if p.x() > bottomRightX:
                    bottomRightX = p.x()
                if p.y() > bottomRightY:
                    bottomRightY = p.y()
        topLeftX -= 50
        topLeftY -= 50
        bottomRightX += 50
        bottomRightY += 50
        self.fitInView(topLeftX, topLeftY, bottomRightX-topLeftX, bottomRightY-topLeftY, mode = Qt.KeepAspectRatio)
        self.scene().setSceneRect(QRectF(0, 0, 5000, 5000))
        
    def fitTypeGraph(self):
#        topLeftX = self.viewport().width()
#        topLeftY = self.viewport().height()
##        topLeftX = -0.5*self.geometry().width()#viewport().width()
##        topLeftY = -0.5*self.geometry().height()#viewport().height()
#        bottomRightX = 0
#        bottomRightY = 0
#
#        for item in self.scene.items():
#            if isinstance(item, FileNode):
#                p = item.pos()
#                radius = math.sqrt(pow(p.x(),2)+pow(p.y(),2))
#                if bottomRightX < radius:
#                    bottomRightX = radius
#                    bottomRightY = radius
##                if abs(p.x())>bottomRightX:
##                    bottomRightX = abs(p.x());
##                if abs(p.y())>bottomRightY:
##                    bottomRightY = abs(p.y()); 
##                #print 'type', p
##                if p.x() < topLeftX:
##                    topLeftX = p.x()
##                if p.y() < topLeftY:
##                    topLeftY = p.y()
##                if p.x() > bottomRightX:
##                    bottomRightX = p.x()
##                if p.y() > bottomRightY:
##                    bottomRightY = p.y()
#        topLeftX = -bottomRightX
#        topLeftY = -bottomRightY
##        bottomRightX = -topLeftX
##        bottomRightY = -topLeftY
#        #bottomRightX *= 1.1
#        #bottomRightY *= 1.1
#        self.fitInView(topLeftX, topLeftY, 1*(bottomRightX-topLeftX), 1*(bottomRightY-topLeftY), mode = Qt.KeepAspectRatio)
#        #self.fitInView(topLeftX, topLeftY, 0.9*(bottomRightX-topLeftX), 0.9*(bottomRightY-topLeftY), mode = Qt.KeepAspectRatio)
#        #self.centerOn(0, 0)
        topLeftX = 5000
        topLeftY = 5000
        bottomRightX = 0
        bottomRightY = 0
        for item in self.scene().items():
            if isinstance(item, FileNode):
                p = item.pos()
                if p.x() < topLeftX:
                    topLeftX = p.x()
                if p.y() < topLeftY:
                    topLeftY = p.y()
                if p.x() > bottomRightX:
                    bottomRightX = p.x()
                if p.y() > bottomRightY:
                    bottomRightY = p.y()
        topLeftX -= 50
        topLeftY -= 50
        bottomRightX += 50
        bottomRightY += 50
        self.fitInView(topLeftX, topLeftY, bottomRightX-topLeftX, bottomRightY-topLeftY, mode = Qt.KeepAspectRatio)
        self.scene().setSceneRect(QRectF(0, 0, 5000, 5000))
        
    def fitSelfTestView(self):
        topLeftX = 5000
        topLeftY = 5000
        bottomRightX = 0
        bottomRightY = 0
        for item in (self.main.selfTestViewScene.interfaceQuesItems or self.main.selfTestViewScene.interfaceTableItems):
            p = item.pos()
            if p.x() < topLeftX:
                topLeftX = p.x()
            if p.y() < topLeftY:
                topLeftY = p.y()
            if p.x() > bottomRightX:
                bottomRightX = p.x()
            if p.y() > bottomRightY:
                bottomRightY = p.y()
        width = bottomRightX-topLeftX
        height = bottomRightY-topLeftY
        self.fitInView(topLeftX, topLeftY, width, height, mode = Qt.KeepAspectRatio)
        self.scene().setSceneRect(QRectF(self.main.centralWidget().geometry()))
#         self.centerOn(QPoint(topLeftX+0.5*width, topLeftY+0.5*height))
        
# class DiagramView(QGraphicsView):
#     def __init__(self, scene, parent = None):
#         QGraphicsView.__init__(self, scene, parent)
#         self.generalGraphScaleX = 1.0
#         self.generalGraphScaleY = 1.0
#         self.typeGraphScaleX = 1.0
#         self.typeGraphScaleY = 1.0
#         
#     def fitGeneralGraph(self):
#         topLeftX = self.viewport().width()
#         topLeftY = self.viewport().height()
#         bottomRightX = 0
#         bottomRightY = 0
#         for item in self.scene().items():
#             if isinstance(item, ClearanceNode):
#                 p = item.pos()
#                 if p.x() < topLeftX:
#                     topLeftX = p.x()
#                 if p.y() < topLeftY:
#                     topLeftY = p.y()
#                 if p.x() > bottomRightX:
#                     bottomRightX = p.x()
#                 if p.y() > bottomRightY:
#                     bottomRightY = p.y()
#         topLeftX -= self.viewport().width()/100
#         topLeftY -= self.viewport().height()/100
#         bottomRightX += self.viewport().width()/100
#         bottomRightY += self.viewport().height()/100
#         self.fitInView(topLeftX, topLeftY, bottomRightX-topLeftX, bottomRightY-topLeftY, mode = Qt.KeepAspectRatio)
#         
#     def fitTypeGraph(self):
#         topLeftX = self.geometry().width()
#         topLeftY = self.geometry().height()
#         bottomRightX = 0
#         bottomRightY = 0
#         
#         for item in self.scene().items():
#             if isinstance(item, FileNode):
#                 p = item.pos()
#                 if p.x() < topLeftX:
#                     topLeftX = p.x()
#                 if p.y() < topLeftY:
#                     topLeftY = p.y()
#                 if p.x() > bottomRightX:
#                     bottomRightX = p.x()
#                 if p.y() > bottomRightY:
#                     bottomRightY = p.y()
#         topLeftX -= self.geometry().width()/100
#         topLeftY -= self.geometry().height()/100
#         bottomRightX += self.geometry().width()/100
#         bottomRightY += self.geometry().height()/100
#         self.fitInView(topLeftX, topLeftY, bottomRightX-topLeftX, bottomRightY-topLeftY, mode = Qt.KeepAspectRatio)

class MainWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.currentPath = os.path.dirname(os.path.abspath(sys.argv[0]))
        os.chdir(self.currentPath)

        self.setFocusPolicy(Qt.StrongFocus)
        self.undoStack = QUndoStack()
        self.initiateStep = True
        self.setupUi()
        self.setupToolbars()
        self.setupConnections()
        self.diagramDir = './policies'
        self.logFile = self.diagramDir+"/DTE_log"
        self.lastOpenedDirFile = "./.DTEvisual"
        if not os.path.isdir(self.diagramDir):
            self.make_sure_path_exists(self.diagramDir)
        
        if os.path.exists(self.lastOpenedDirFile):
            with open(self.lastOpenedDirFile, 'r') as f:
                self.currentFileDir = f.readline()
            f.close()
            self.currentFileDir = self.currentFileDir.replace('\n', '')
        else:
            self.currentFileDir = str(os.path.abspath(self.diagramDir))
            self.writeToFile(self.lastOpenedDirFile, self.currentFileDir)
#        scrollarea = QScrollArea(self)
#        self.setCentralWidget(scrollarea)
# 
#        scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
#        scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
#        scrollarea.setWidgetResizable(False)
     
        self.savedFileName = None
        self.ioHelper = DiagramIOHelper(self.scene, self)
        self.radialLayout = RadialTreeLayout(self.scene)
        self.generalGraphLayout = GeneralGraphLayoutStrategy(self.scene)
        self.generalGraphLayout.finished.connect(self.layoutFinished)
        self.generalGraphLayout.showCurrentResult.connect(self.showCurrentLayout)
        self.autogradingTest = AutogradingTest(self)
        self.assistant = Assistant()
        self.readSettings()
        self.initLog()
        self.hasInfo = False
        self.selfTestViewScene = SelfTestScene(self.view, self)
        ''''''
#         self.selfTestViewScene.setVisibilityOfSceneItems(self.ui.actionView_SelfTest.isChecked())
#         self.selfTestViewScene.updateUIContents()
#         self.selfTestViewScene.updateLayout()
        self.viewModeChanged(self.ui.actionView_SelfTest)


    def make_sure_path_exists(self, path):
        try:
            os.makedirs(path)
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise
            
    def resizeEvent(self, evt):
        '''test'''
        QMainWindow.resizeEvent(self, evt)
        if self.ui.actionView_SelfTest.isChecked():
            self.selfTestViewScene.updateLayout()
#         self.scene.setSceneRect(QRectF(-0.5*self.view.geometry().width(), -0.5*self.view.geometry().height(), self.view.geometry().width(),self.view.geometry().height()))
#         if self.ui.actionGeneral_Graph.isChecked():
#             self.view.fitGeneralGraph()
#             self.view.scale(0.95, 0.95)
#             self.view.generalGraphScaleX = self.view.transform().m11()
#             self.view.generalGraphScaleY = self.view.transform().m22()
#         else:
#             self.view.fitTypeGraph()
#             
#         self.initiateStep = False   
        
    def getCurrentTime(self):
        return str(datetime.datetime.now())
    
    def initLog(self):
        self.writeToFile(self.logFile, '\n'+20*'-'+"Start DTEvisual"+20*'-'+"\n")
        message = self.scene.stuInfo+"\n"
        self.writeToFile(self.logFile, message)

    def setupUi(self):
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self) 
        
        # create scene to display diagrams
        self.scene = DiagramScene(self)
        self.view = DiagramView(self.scene, self)
        self.setCentralWidget(self.view)
                
        self.specDialog = SpecDialog(self.scene)
        self.helpPannel = QSplitter(Qt.Horizontal)

        self.help_engine = QHelpEngine('DTEVis.qhc', self)
        self.help_engine.setupData()
        self.helpBrowser = HelpBrowser(self.help_engine)
        self.helpPannel.insertWidget(0, self.help_engine.contentWidget())
        self.helpPannel.insertWidget(1, self.helpBrowser)
                
        self.queryWindowDockWidget = QDockWidget('Query Window', self)
        self.queryWindowDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        self.queryWindow = QueryWindow(self.scene)
        self.queryWindowDockWidget.setWidget(self.queryWindow)
        self.addDockWidget(Qt.RightDockWidgetArea, self.queryWindowDockWidget)
        self.queryWindowDockWidget.setVisible(False)
        self.ui.menu_View.addAction(self.queryWindowDockWidget.toggleViewAction())
        
        self.ui.actionNew.setShortcuts(QKeySequence.New)
        self.ui.actionOpen.setShortcuts(QKeySequence.Open)
        self.ui.actionSave.setShortcuts(QKeySequence.Save)
        self.ui.actionSave_As.setShortcuts(QKeySequence.SaveAs)
        
        undoAction = self.undoStack.createUndoAction(self, 'Undo')
        self.ui.menu_Edit.addAction(undoAction)
        undoAction.setShortcuts(QKeySequence.Undo)
        redoAction = self.undoStack.createRedoAction(self, 'Redo')
        self.ui.menu_Edit.addAction(redoAction)
        redoAction.setShortcuts(QKeySequence.Redo)
        
        self.ui.actionZoom_In.setShortcuts(QKeySequence.ZoomIn)
        self.ui.actionZoom_Out.setShortcuts(QKeySequence.ZoomOut)
        
        self.ui.actionHelp_Contents.setShortcuts(QKeySequence.HelpContents)
        
        # set action icons
        self.ui.actionNew.setIcon(QIcon(QPixmap(':/icons/images/new.png')))
        self.ui.actionOpen.setIcon(QIcon(QPixmap(':/icons/images/open.png')))
        self.ui.actionSave.setIcon(QIcon(QPixmap(':/icons/images/save.png')))
        self.ui.actionImport.setIcon(QIcon(QPixmap(':/icons/images/import.png')))
        self.ui.actionExport.setIcon(QIcon(QPixmap(':/icons/images/export.png')))
        self.ui.actionNormal_Mode.setIcon(QIcon(QPixmap(':/icons/images/move.png')))
        self.ui.actionHighlight_Mode.setIcon(QIcon(QPixmap(':/icons/images/highlight.png')))
        self.ui.actionDomain_Node.setIcon(QIcon(QPixmap(':/icons/images/domain.png')))
        self.ui.actionType_Node.setIcon(QIcon(QPixmap(':/icons/images/type.png')))
        self.ui.actionAuto_Connection.setIcon(QIcon(QPixmap(':/icons/images/auto_conn.png')))
        self.ui.actionExec_Connection.setIcon(QIcon(QPixmap(':/icons/images/exec_conn.png')))
        self.ui.actionType_Connection.setIcon(QIcon(QPixmap(':/icons/images/type_conn.png')))
        self.ui.actionZoom_In.setIcon(QIcon(QPixmap(':/icons/images/Zoom-In.png')))
        self.ui.actionZoom_Out.setIcon(QIcon(QPixmap(':/icons/images/Zoom-Out.png')))
        self.ui.actionFullScreen.setIcon(QIcon(QPixmap(':/icons/images/fullscreen.png')))

        self.editActionGroup = QActionGroup(self)
        self.editActionGroup.addAction(self.ui.actionNormal_Mode)
        self.editActionGroup.addAction(self.ui.actionHighlight_Mode)
        self.editActionGroup.addAction(self.ui.actionDomain_Node)
        self.editActionGroup.addAction(self.ui.actionType_Node)
        self.editActionGroup.addAction(self.ui.actionAuto_Connection)
        self.editActionGroup.addAction(self.ui.actionExec_Connection)
        self.editActionGroup.addAction(self.ui.actionType_Connection)
        
        self.viewActionGroup = QActionGroup(self)
        self.viewActionGroup.addAction(self.ui.actionGeneral_Graph)
        self.viewActionGroup.addAction(self.ui.actionType_Graph)
        self.viewActionGroup.addAction(self.ui.actionView_SelfTest)
        
        self.nodeSizeActionGroup = QActionGroup(self)
        self.nodeSizeActionGroup.addAction(self.ui.actionSmall)
        self.nodeSizeActionGroup.addAction(self.ui.actionNormal)
        self.nodeSizeActionGroup.addAction(self.ui.actionLarge)
        self.nodeSizeActionGroup.addAction(self.ui.actionX_Large)
        
    def setupToolbars(self):
        self.ui.toolBar.addAction(self.ui.actionNew)  
        self.ui.toolBar.addAction(self.ui.actionOpen)
        self.ui.toolBar.addAction(self.ui.actionSave)
        self.ui.toolBar.addAction(self.ui.actionImport)
        self.ui.toolBar.addAction(self.ui.actionExport)
        self.ui.toolBar.addSeparator()
        
        self.normalModeButton = QToolButton(self)
        self.normalModeButton.setDefaultAction(self.ui.actionNormal_Mode)
        self.ui.toolBar.addWidget(self.normalModeButton)
        
        self.highlightModeButton = QToolButton(self)
        self.highlightModeButton.setDefaultAction(self.ui.actionHighlight_Mode)
        self.ui.toolBar.addWidget(self.highlightModeButton)
        
        self.domainNodeButton = QToolButton(self)
        self.domainNodeButton.setDefaultAction(self.ui.actionDomain_Node)
        self.ui.toolBar.addWidget(self.domainNodeButton)
        
        self.typeNodeButton = QToolButton(self)
        self.typeNodeButton.setDefaultAction(self.ui.actionType_Node)
        self.ui.toolBar.addWidget(self.typeNodeButton)
        
        self.autoConnButton = QToolButton(self)
        self.autoConnButton.setDefaultAction(self.ui.actionAuto_Connection)
        self.ui.toolBar.addWidget(self.autoConnButton)

        self.execConnButton = QToolButton(self)
        self.execConnButton.setDefaultAction(self.ui.actionExec_Connection)
        self.ui.toolBar.addWidget(self.execConnButton)
        
        self.typeConnButton = QToolButton(self)
        self.typeConnButton.setDefaultAction(self.ui.actionType_Connection)
        self.ui.toolBar.addWidget(self.typeConnButton)
        
        self.ui.toolBar.addSeparator()
        self.ui.toolBar.addAction(self.ui.actionGeneral_Graph)
        self.ui.toolBar.addAction(self.ui.actionType_Graph)
        self.ui.toolBar.addAction(self.ui.actionView_SelfTest)
        self.ui.toolBar.addSeparator()
        self.ui.toolBar.addAction(self.ui.actionRefresh)
        self.ui.toolBar.addSeparator()
        self.ui.toolBar.addAction(self.queryWindowDockWidget.toggleViewAction())
        self.ui.toolBar.addAction(self.ui.actionSpecification_Dialog)
        
        self.ui.toolBar.addSeparator()
        self.ui.toolBar.addAction(self.ui.actionZoom_In)
        self.ui.toolBar.addAction(self.ui.actionZoom_Out)
        self.ui.toolBar.addAction(self.ui.actionFullScreen)
        
        self.ui.toolBar.addSeparator()
        self.animationControl = QToolButton()
        self.animationControl.setIcon(QIcon(QPixmap(':/icons/images/pause.png')))
        self.animationControl.setDisabled(True)
        self.stopAnimationButton = QToolButton()
        self.stopAnimationButton.setIcon(QIcon(QPixmap(':/icons/images/stop.png')))
        self.stopAnimationButton.setDisabled(True)
        self.ui.toolBar.addWidget(self.animationControl)
        self.ui.toolBar.addWidget(self.stopAnimationButton)

    def setupConnections(self):
        self.ui.actionNew.triggered.connect(self.newDiagram)
        self.ui.actionSave.triggered.connect(self.saveDiagram)
        self.ui.actionSave_As.triggered.connect(self.saveDiagramAs)
        self.ui.actionOpen.triggered.connect(self.openDiagram)
        self.ui.actionImport.triggered.connect(self.importDTESpec)
        self.ui.actionExport.triggered.connect(self.exportToDTESpec)
        self.ui.actionExit.triggered.connect(self.quitApp)
        
        self.ui.actionRefresh.triggered.connect(self.refreshVis)
        
        self.scene.nodeAdded.connect(self.onNodeAdded)
        self.scene.nodeDeleted.connect(self.onItemDeleted)
        self.scene.edgeAdded.connect(self.onEdgeAdded)
        self.scene.edgeDeleted.connect(self.onEdgeDeleted)
        self.scene.itemMoved.connect(self.onItemMoved)
        self.scene.initialDomainChanged.connect(self.onInitialDomainChanged)
        self.scene.nameChanged.connect(self.onNameChanged)
        self.scene.typeChanged.connect(self.onTypeChanged)
        self.scene.fileNodeAdded.connect(self.onFileNodeAdded)
        self.scene.typeDeleted.connect(self.onTypeDeleted)
        self.scene.flagChanged.connect(self.onFlagChanged)
        self.scene.assignmentChanged.connect(self.onAssignmentChanged)
        
        self.editActionGroup.triggered.connect(self.editModeChanged)
        #self.undoStack.cleanChanged.connect(self.undoStackCleanChanged)
        
        # view menu 
        self.ui.actionDomain_Graph_Only.toggled.connect(self.toggleDomainGraphOnly)
        self.ui.actionFullScreen.toggled.connect(self.toggleFullScreen)
        self.ui.actionZoom_In.triggered.connect(self.zoomIn)
        self.ui.actionZoom_Out.triggered.connect(self.zoomOut)
        self.ui.actionAutomatic_Layout.triggered.connect(self.layoutItems)
        self.viewActionGroup.triggered.connect(self.viewModeChanged)
        self.ui.actionShow_Domain_Domain_Edge_Labels.toggled.connect(self.toggleDDLabels)
        self.ui.actionShow_Domain_Type_Edge_Labels.toggled.connect(self.toggleDTLabels)
        self.ui.actionSpecification_Dialog.triggered.connect(self.showSpecDialog)
        
        self.ui.actionEnable_Query_Animation.toggled.connect(self.toggleAnimation)
        self.ui.actionSet_Animation_Interval.triggered.connect(self.setAnimationInterval)
        
        self.ui.actionHelp_Contents.triggered.connect(self.showDocumentation)
        self.help_engine.contentWidget().linkActivated.connect(self.helpBrowser.setSource)
        
        self.animationControl.clicked.connect(self.onAnimationButtonClicked)
        self.stopAnimationButton.clicked.connect(self.onAnimationStopped)
        self.scene.animationStarted.connect(self.onAnimationStarted)
        self.scene.animationStopped.connect(self.onAnimationStopped)
        self.queryWindow.animateQuery0.connect(self.scene.animateQuery0)
        self.queryWindow.animateQuery1.connect(self.scene.animateQuery1)
        self.queryWindow.animateQuery2.connect(self.scene.animateQuery2)
        self.queryWindow.animateQuery3.connect(self.scene.animateQuery3)
        self.queryWindow.animateQuery4.connect(self.scene.animateQuery4)
        self.queryWindow.animateQuery5.connect(self.scene.animateQuery5)
        self.queryWindow.animateQuery6.connect(self.scene.animateQuery6)
        self.queryWindow.animateQuery7.connect(self.scene.animateQuery7)
        self.queryWindow.animateQuery8.connect(self.scene.animateQuery8)
        self.queryWindow.animateQuery9.connect(self.scene.animateQuery9)
        self.queryWindow.animateQuery10.connect(self.scene.animateQuery10)
        self.ui.actionTest.triggered.connect(self.showAutogradingTest)
    
    def disableAllGui(self):
        self.ui.menu_File.setEnabled(False)
        self.ui.menu_Edit.setEnabled(False)
        self.ui.menu_View.setEnabled(False)
        self.ui.menu_Practice.setEnabled(False)
        self.ui.actionNew.setEnabled(False)
        self.ui.actionOpen.setEnabled(False)
        self.ui.actionSave.setEnabled(False)
        self.ui.actionImport.setEnabled(False)
        self.ui.actionExport.setEnabled(False)
        self.ui.actionGeneral_Graph.setEnabled(False)
        self.ui.actionType_Graph.setEnabled(False)
        self.ui.actionView_SelfTest.setEnabled(False)
    
    def enableAllGui(self):
        self.ui.menu_File.setEnabled(True)
        self.ui.menu_Edit.setEnabled(True)
        self.ui.menu_View.setEnabled(True)
        self.ui.menu_Practice.setEnabled(True)
        self.ui.actionNew.setEnabled(True)
        self.ui.actionOpen.setEnabled(True)
        self.ui.actionSave.setEnabled(True)
        self.ui.actionImport.setEnabled(True)
        self.ui.actionExport.setEnabled(True)
        self.ui.actionGeneral_Graph.setEnabled(True)
        self.ui.actionType_Graph.setEnabled(True)
        self.ui.actionView_SelfTest.setEnabled(True)
        
    def showAutogradingTest(self):
        self.autogradingTest.questionDlg.show()
            
    def closeEvent(self, evt):
        evt.accept()
#         if self.maybeSave():
#             self.writeSettings()
#             #self.writeToFile(self.logFile, 20*'-'+"Close DTEvisual   "+self.getCurrentTime()+20*'-'+"\n")
#             evt.accept()
#         else:
#             evt.ignore()
            
    def refreshVis(self):
        self.scene.update()
        
    def readSettings(self):
        return
        settings = QSettings()
        settings.beginGroup('MainWindow')
        self.resize(settings.value('Size', QVariant(QSize(800,600))).toSize())
        self.move(settings.value('Pos', QVariant(QPoint(200,200))).toPoint())
        self.diagramDir = settings.value('DiagramDir', QVariant('')).toString()
        self.specDir = settings.value('SpecDir', QVariant('')).toString()
        settings.endGroup()
    
    def writeSettings(self):
        return
        settings = QSettings()
        settings.setValue('MainWindow/Size', self.size())
        settings.setValue('MainWindow/Pos', self.pos())
        settings.setValue('MainWindow/DiagramDir', self.diagramDir)
        settings.setValue('MainWindow/SpecDir', self.specDir)
        
    def maybeSave(self):
        if not self.undoStack.isClean():
            ret = QMessageBox.warning(self, '', 'The diagram has been modified. Do you want to save your changes', 
                                      buttons=QMessageBox.Save|QMessageBox.Discard|QMessageBox.Cancel, 
                                      defaultButton=QMessageBox.Save)
            if ret == QMessageBox.Save:
                return self.saveDiagram()
            elif ret == QMessageBox.Discard:
                self.undoStack.setClean()
                return True
            elif ret == QMessageBox.Cancel:
                return False
        return True
    
    def saveDiagram(self):
        if self.savedFileName is not None:
            self.ioHelper.write(self.savedFileName)
            self.undoStack.setClean()
            self.setWindowTitleAsFilename(self.savedFileName)
            '''Collect user operation data'''
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Save DTE vis file\n")
            self.writeToFile(self.logFile, 50*'-'+'\n')
            self.saveLastOpenedPolicyDir(str(self.savedFileName))
        else:
            self.saveDiagramAs()
    
    def saveDiagramAs(self):
        filename = QFileDialog.getSaveFileName(self, 'Save Diagram As...', directory=self.currentFileDir, filter='(*.dtevis)')
        if not filename.isEmpty():
            self.diagramDir = QFileInfo(filename).absolutePath()
            self.ioHelper.write(str(filename))
            self.savedFileName = str(filename)
            self.undoStack.setClean()
            self.setWindowTitleAsFilename(str(filename))
            '''Collect user operation data'''
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Save DTE vis file\n")
            self.writeToFile(self.logFile, 50*'-'+'\n')
            '''Store last spec/vis file location'''
            self.saveLastOpenedPolicyDir(str(filename))
            
    def initPars(self):
#         self.scene.clear()
        for i in self.scene.items():
            if i not in self.selfTestViewScene.interfaceQuesItems and \
                i not in self.selfTestViewScene.interfaceTableItems:
                self.scene.removeItem(i)
                del i
        self.hasInfo = False
        self.savedFileName = None
        self.scene.typeGraph = {}
            
    def writeToFile(self, filename, message):
        with open(filename, 'a+') as f:
            f.write(message)
        f.close()
        
    def getCurrentTimeString(self):
        return self.getCurrentTime()+': '
    
    def openDiagram(self):
        self.maybeSave()
        filename = QFileDialog.getOpenFileName(self, 'Open Diagram', directory=self.currentFileDir, filter='(*.dtevis);;All Files(*.*)')
        if not filename.isEmpty():
            self.diagramDir = QFileInfo(filename).absolutePath()
            self.initPars()
            self.hasInfo = True
            self.ui.actionGeneral_Graph.setChecked(True)
            self.viewModeChanged(self.ui.actionGeneral_Graph)
            self.ioHelper.read(str(filename))
            self.calculateLevelCircles()
            self.savedFileName = str(filename)
            self.undoStack.setClean()
            self.scene.update()
            self.setWindowTitleAsFilename(str(filename))
            self.saveLastOpenedPolicyDir(str(filename))
            message = self.getCurrentTimeString()+"Open DTE vis file: "+str(filename)+"\n"+50*'*'+'\n'
            lines = []
            with open(str(filename), 'r') as f:
                lines = f.readlines()
            f.close()
            message+='\n'.join(lines)
            self.writeToFile(self.logFile, message+"\n"+50*'*'+"\n")
        self.queryWindow.refreshDomainComboBox()
            
    def quitApp(self):
        self.writeToFile(self.logFile, 20*'-'+"Close DTEvisual   "+self.getCurrentTime()+20*'-'+"\n")
        QCoreApplication.quit()
    
    def setWindowTitleAsFilename(self, filepath):
        filename = filepath.split('/')[-1]
        self.setWindowTitle(filename)
        
    def saveLastOpenedPolicyDir(self, filepath):
        self.currentFileDir = filepath[:filepath.rfind('/')]
        with open(self.lastOpenedDirFile, 'w') as f:
            f.write(self.currentFileDir)
        f.close()
            
    def showCurrentLayout(self):
        self.generalGraphLayout.mapToScene()
        self.scene.update()
    
    def calculateLevelCircles(self):
        if len(self.scene.typeGraph) > 0:
            rootX = self.scene.typeGraphRoot.pos().x()
            rootY = self.scene.typeGraphRoot.pos().y()
            d = min(self.view.viewport().width(), self.view.viewport().height()) / 2.0  / len(self.scene.typeGraph)
            for level in self.scene.typeGraph.keys():
                radius = d * level
                self.scene.typeGraph[level][1] = QRectF(rootX-radius, rootY-radius, 2*radius, 2*radius)

                    
    def importDTESpec(self):
        self.maybeSave()
        filename = QFileDialog.getOpenFileName(self, 'Import from DTE Spec', directory=self.currentFileDir, filter='(*.dte);;All Files(*.*)')
        if not filename.isEmpty():
            self.specDir = QFileInfo(filename).absolutePath()
            self.initPars()
            try:
                self.importSpec(str(filename))
                self.saveLastOpenedPolicyDir(str(filename))
                
                message = self.getCurrentTimeString()+"Import DTE spec file: "+str(filename)+"\n"+50*'*'+'\n'
                lines = []
                with open(str(filename), 'r') as f:
                    lines = f.readlines()
                f.close()
                message+='\n'.join(lines)
                self.writeToFile(self.logFile, message+"\n"+50*'*'+"\n")
            except Exception as e:
                self.writeToFile(self.logFile, "Failed!\n")
                QMessageBox.critical(self, 'Error', str(e))
                return
            self.writeToFile(self.logFile, "Succeeded!\n")
            self.setWindowTitleAsFilename(str(filename))
            
    def importSpec(self, filename):
        self.ioHelper.import_(filename)
        self.hasInfo = True
        self.setupGraph()
        self.selfTestViewScene.updateUIContents()
        self.selfTestViewScene.setVisibilityOfSceneItems(self.ui.actionView_SelfTest.isChecked())
        self.resizeEvent(None)
        self.scene.update()
            
    def setupGraph(self):
        self.activateWindow()
        self.setFocus()
        self.scene.showGeneralGraph()
        self.generalGraphLayout.start()
        #self.scene.message = 'Hit space bar to stop executing layout algorithm'
        if self.scene.typeGraphRoot:
            self.radialLayout.buildTreeTable(self.scene.typeGraphRoot)
            self.radialLayout.layout()
            self.radialLayout.mapToScene()
            self.ui.actionType_Graph.setEnabled(True)
            levelNodes = self.scene.typeGraphRoot.children
            level = 1
            while levelNodes:
                self.scene.typeGraph[level] = [levelNodes, None]
                levelNodes = []
                for n in self.scene.typeGraph[level][0]:
                    if n.children:
                        levelNodes.extend(n.children)
                level += 1

            self.calculateLevelCircles()
        else:
            self.ui.actionType_Graph.setDisabled(True)
        self.scene.update()
        self.queryWindow.refreshDomainComboBox()
        
            
    
    def exportToDTESpec(self):
        filename = QFileDialog.getSaveFileName(self, 'Export to DTE Spec', directory=self.currentFileDir, filter='(*.dte)')
        if not filename.isEmpty():
            self.specDir = QFileInfo(filename).absolutePath()
            try:
                self.ioHelper.export(str(filename))  
                message = self.getCurrentTimeString()+"Export DTE spec to file "+str(filename)+'\n'
                self.writeToFile(self.logFile, message)
                self.undoStack.setClean() 
            except Exception as e:
                self.writeToFile(self.logFile, "Failed!\n")
                QMessageBox.critical(self, 'Error', str(e))
                if QFile.exists(filename):
                    QFile.remove(filename)
                return
            self.writeToFile(self.logFile, "Succeeded!\n")
            
            self.setWindowTitleAsFilename(str(filename))
            self.saveLastOpenedPolicyDir(str(filename))

    
    def setMainWindowAllGuiState(self, state):
        self.ui.toolBar.setEnabled(state)
        self.queryWindowDockWidget.setEnabled(state)
#         self.toolBoxDockWidget.setEnabled(state)
        self.ui.menubar.setEnabled(state)
        
    def onNodeAdded(self, item, name):
        command = AddNodeCommand(item, name, self.scene)
        self.undoStack.push(command)
    
    def onItemDeleted(self, item):
        command = DeleteNodeCommand(item)
        self.undoStack.push(command)
        '''User operation data collection'''
        if isinstance(item, DomainNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Delete Domain Node: "+item.name+'\n')
        elif isinstance(item, TypeNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Delete Type Node: "+item.name+'\n')
            
    def onEdgeAdded(self, item, descr):
        command = AddEdgeCommand(item, descr, self.scene)
        self.undoStack.push(command)
        
    def onEdgeDeleted(self, item):
        command = DeleteEdgeCommand(item)
        self.undoStack.push(command)
        '''User operation data collection'''
        if item.type==EdgeItem.AUTO_CONN or item.type==EdgeItem.EXEC_CONN:
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Delete Edge from Domain "+item.startItem.name+" to Domain "+item.endItem.name+'\n')
        elif item.type == EdgeItem.TYPE_CONN:
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Delete Edge from Domain "+item.startItem.name+" to Type "+item.endItem.name+'\n')
    
    def onItemMoved(self, item, oldPos):
        command = MoveCommand(item, oldPos,  self.scene)
        self.undoStack.push(command)
    
    def onInitialDomainChanged(self, item, entryPoint):
        command = MarkInitialDomainCommand(item, entryPoint)
        self.undoStack.push(command)
        '''User operation data collection'''
        self.writeToFile(self.logFile, self.getCurrentTimeString()+"Initial Domain changes to Domain "+item.name+" at "+entryPoint+'\n')
        
    def onNameChanged(self, item, name):
        command = ChangeNameCommand(item, name)
        self.undoStack.push(command)
        '''User operation data collection'''
        itemType = ''
        if isinstance(item, DomainNode):
            itemType = ' Domain Node '
            self.writeToFile(self.logFile, self.getCurrentTimeString()+itemType+item.name +" changes name to "+ name+'\n')
        elif isinstance(item, TypeNode):
            itemType = ' Type Node '
            self.writeToFile(self.logFile, self.getCurrentTimeString()+itemType+item.name +" changes name to "+ name+'\n')
        elif isinstance(item, FileNode):
            itemType = ' File Node '
            self.writeToFile(self.logFile, self.getCurrentTimeString()+itemType+item.name +" changes name to "+ name+'\n')
        
    def onTypeChanged(self, item, type):
        command = ChangeTypeCommand(item, type)
        self.undoStack.push(command)
        '''User operation data collection'''
        if isinstance(item, FileNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"File Node changes type to "+type.name+'\n')
        
    def onFileNodeAdded(self, parent, name, type, flag):
        command = AddFileNodeCommand(parent, name, type, flag)
        self.undoStack.push(command)
        self.calculateLevelCircles()

    def onTypeDeleted(self, item):
        command = DeleteTypeCommand(item)
        self.undoStack.push(command)
        '''User operation data collection'''
        if isinstance(item, FileNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"File Node "+item.name +" own type "+item.type.name+" deleted\n")
        
    def onFlagChanged(self, item, flag):
        command = ChangeFlagCommand(item, flag)
        self.undoStack.push(command)
        '''User operation data collection'''
        if isinstance(item, FileNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"File Node "+item.name+ " flag changes from "+item.flag + " to "+ flag+'\n')
        
    def onAssignmentChanged(self, item, assignment):
        command = ChangeAssignmentCommand(item, assignment)
        self.undoStack.push(command)
        '''User operation data collection'''
        if isinstance(item, TypeNode):
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Type Node "+item.name+" changes assignment to "+ assignment+'\n')
    
    def newDiagram(self):  
        if self.maybeSave():
            self.scene.initialDomain = None
#             self.scene.clear()
            self.initPars()
            self.scene.typeGraphRoot = FileNode('', None, 2)
            self.scene.typeGraphRoot.setVisible(False)
            self.scene.typeGraphRoot.setPos(2500, 2500)
            #self.scene.typeGraphRoot.setPos(0.5*self.geometry().width(), 0.5*self.geometry().height())
            self.scene.addItem(self.scene.typeGraphRoot)
            self.scene.update()
            self.setWindowTitle("DTEvisual")
            '''Collect user operation data'''
            self.writeToFile(self.logFile, 50*'-'+'\n')
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"New diagram\n")
            self.queryWindow.refreshDomainComboBox()
            QApplication.processEvents()
    
    '''
    def undoStackCleanChanged(self, clean):
        if clean:
            self.ui.actionSave.setDisabled()
        else:
            self.ui.actionSave.setEnabled()
    '''

    def editModeChanged(self, action):
        if action is self.ui.actionNormal_Mode:
            self.scene.mode = DiagramScene.Normal
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Set to Normal Mode\n")
        elif action is self.ui.actionHighlight_Mode:
            self.scene.mode = DiagramScene.Highlight
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Set to Highlight Mode\n")
            self.scene.stateMachine.start()
        elif action is self.ui.actionDomain_Node:
            self.scene.mode = DiagramScene.InsertDomainNode
        elif action is self.ui.actionType_Node:
            self.scene.mode = DiagramScene.InsertTypeNode
        elif action is self.ui.actionAuto_Connection:
            self.scene.mode = DiagramScene.InsertAutoConn
        elif action is self.ui.actionExec_Connection:
            self.scene.mode = DiagramScene.InsertExecConn
        elif action is self.ui.actionType_Connection:
            self.scene.mode = DiagramScene.InsertTypeConn
        
        if action is not self.ui.actionHighlight_Mode:
            if self.scene.stateMachine.isRunning():
                self.scene.stateMachine.stop()
                for item in self.scene.items():
                    item.setOpacity(1.0)
                self.scene.update()
        
    
    def toggleDomainGraphOnly(self, on):
        if on:
            self.scene.domainGraphOnly = True
        else:
            self.scene.domainGraphOnly = False
            
        self.scene.update()
        
    def toggleDDLabels(self, on):
        self.scene.showDomainDomainLabels = on
        self.scene.update()
        
    def toggleDTLabels(self, on):
        self.scene.showDomainTypeLabels = on
        self.scene.update()
        
        
    def showSpecDialog(self):
        self.specDialog.reGenerateSpec()
        self.specDialog.show()
        self.writeToFile(self.logFile, self.getCurrentTimeString()+"Specification Dialog turned on\n")
         
    def toggleFullScreen(self, on):
        if on:
            self.showFullScreen()
            self.ui.actionFullScreen.setIcon(QIcon(QPixmap(':/icons/images/fullscreen_exit.png')))
        else:
            self.showNormal()
            self.ui.actionFullScreen.setIcon(QIcon(QPixmap(':/icons/images/fullscreen.png')))
            
    def zoomIn(self):
        self.view.scale(1.25, 1.25)
        if self.ui.actionGeneral_Graph.isChecked():
            self.view.generalGraphScaleX = self.view.transform().m11()
            self.view.generalGraphScaleY = self.view.transform().m22()
        elif self.ui.actionType_Graph.isChecked():
            self.view.typeGraphScaleX = self.view.transform().m11()
            self.view.typeGraphScaleY = self.view.transform().m22()
        elif self.ui.actionView_SelfTest.isChecked():
            self.view.selfTestViewScaleX = self.view.transform().m11()
            self.view.selfTestViewScaleY = self.view.transform().m22()
            
    def zoomOut(self):
        self.view.scale(0.75, 0.75) 
        if self.ui.actionGeneral_Graph.isChecked():
            self.view.generalGraphScaleX = self.view.transform().m11()
            self.view.generalGraphScaleY = self.view.transform().m22()
        elif self.ui.actionType_Graph.isChecked():
            self.view.typeGraphScaleX = self.view.transform().m11()
            self.view.typeGraphScaleY = self.view.transform().m22()
        elif self.ui.actionView_SelfTest.isChecked():
            self.view.selfTestViewScaleX = self.view.transform().m11()
            self.view.selfTestViewScaleY = self.view.transform().m22()
            
    def layoutItems(self):
        self.scene.update()
    
    def viewModeChanged(self, action):
        for item in self.scene.items():
            item.setVisible(False)
        self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.selfTestViewScene.setVisibilityOfSceneItems(self.ui.actionView_SelfTest.isChecked())
        if action is self.ui.actionGeneral_Graph:
            self.editActionGroup.setEnabled(True)
            self.scene.showGeneralGraph()
            self.view.fitGeneralGraph()
            self.view.centerOn(2500,2500)
            self.view.setTransform(QTransform())
            self.view.scale(self.view.generalGraphScaleX, self.view.generalGraphScaleY)
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Switch to General Graph\n")
        elif action is self.ui.actionType_Graph:
            self.editActionGroup.setDisabled(True)
            self.scene.showTypeGraph()
            self.view.fitTypeGraph()
            self.view.centerOn(self.scene.typeGraphRoot)
            self.view.setTransform(QTransform())
            self.view.scale(self.view.typeGraphScaleX, self.view.typeGraphScaleY)
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Switch to Type Graph\n")
        elif action is self.ui.actionView_SelfTest:
            self.editActionGroup.setDisabled(False)
            self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.view.fitSelfTestView()
            self.selfTestViewScene.updateLayout()
            self.view.setTransform(QTransform())
            self.view.scale(self.view.selfTestViewScaleX, self.view.selfTestViewScaleY)
            self.writeToFile(self.logFile, self.getCurrentTimeString()+"Switch to Self Test Mode\n")
        QApplication.processEvents()
        
    def toggleAnimation(self, state):
        if state == Qt.Checked:
            self.queryWindow.animationEnabled = True
        else:
            self.queryWindow.animationEnabled = False
            
    def setAnimationInterval(self):
        interval, accept = QInputDialog.getInt(self, 'Set Interval', '', value=self.scene.timerInterval/1000, min=1, max=10, step=1)
        if accept:
            self.scene.timerInterval = interval * 1000
            
    def onAnimationStarted(self):
        self.queryWindow.ui.runQueryButton.setDisabled(True)
        self.animationControl.setEnabled(True)
        self.stopAnimationButton.setEnabled(True)
    
    def onAnimationStopped(self):
        self.queryWindow.ui.runQueryButton.setEnabled(True)
        self.animationControl.setDisabled(True)
        self.stopAnimationButton.setDisabled(True)
        self.animationControl.setIcon(QIcon(QPixmap(':/icons/images/pause.png')))
        if self.scene.animationTimer.isActive():
            self.scene.animationStep = 0
            self.scene.animationTimer.setInterval(5000)
            self.scene.message = ''
            self.scene.animationTimer.stop()
            for item in self.scene.items():
                if item.highlighted:
                    item.highlighted = False
            self.scene.update()
            
    def onAnimationButtonClicked(self):
        if self.scene.animationTimer.interval() == 9000000:
            self.animationControl.setIcon(QIcon(QPixmap(':/icons/images/pause.png')))
            self.scene.animationTimer.setInterval(self.scene.timerInterval)
        else:
            self.animationControl.setIcon(QIcon(QPixmap(':/icons/images/start.png')))
            self.scene.animationTimer.setInterval(9000000)

            
    def showDocumentation(self):
        #self.assistant.showDocumentation("index.html")  
        self.helpPannel.show()
        self.helpPannel.raise_() 
        
    def layoutFinished(self):
        self.clearFocus()
        self.scene.message = ''
        self.scene.update() 
        

    
         
