def __init__(self):
     QMainWindow.__init__(self)
     self.ui = Ui_MainWindow()
     self.ui.setupUi(self)
     self.loadTranslations()
     self.settings = QSettings("LVK Inc", "DataCenters")   
     self.resourcesGraphEditor = ResourcesGraphEditor()
     self.tenantEditor = TenantEditor()
     self.Vis = Vis()
     self.tenants = {}
     self.project = Project()
     # TODO: Captain, we have a problem!
     # For some reason, in Python 2.7 QSettings converts dicts to QVariant
     # So the ini file is undecypherable
     # This works fine in Python 3.2 by the way
     #if self.settings.value("vis"):
         #self.Vis.canvas.settings = self.settings.value("vis")
     #self.graphvis.settings = self.settings.value("graphVis")
     self.settingsDialog = SettingsDialog(self.Vis.canvas.settings)
     self.settingsDialog.ui.backup.setChecked(self.settings.value("backup").toBool())
     self.settingsDialog.ui.autosave.setChecked(self.settings.value("autosave").toBool())
     self.settingsDialog.ui.interval.setValue(self.settings.value("interval").toInt()[0])
     paramxml = self.settings.value("paramxml", "params").toString()
     ParamFactory.LoadDir(str(paramxml))
     self.settingsDialog.ui.params.setText(paramxml)
     i = 0
     for s in self.languages:
         self.settingsDialog.ui.languages.addItem(s)
         if s == str(self.settings.value("language").toString()):
             self.settingsDialog.ui.languages.setCurrentIndex(i)
         i += 1
     self.resourcesGraphEditor.setData(self.project.resources)
     for i in range(self.MaxRecentFiles):
         a = QAction(self)
         a.setVisible(False)
         a.setEnabled(False)
         if i <= 9:
             a.setShortcut(QKeySequence(self.tr("Alt+") + str(i + 1)))
         QObject.connect(a, SIGNAL("triggered()"), self.OpenRecentFile);
         self.ui.menuFile.insertAction(self.ui.actionExit, a)
         self.recentFileActions.append(a)
     self.UpdateRecentFileActions()
     self.basename = self.windowTitle()
     self.tenantEditor.tenant_changed.connect(self.tenantChanged)
     self.backupTimer = QTimer()
     self.backupTimer.setInterval(60000)
     self.backupTimer.setSingleShot(False)
     QObject.connect(self.backupTimer, SIGNAL("timeout()"), self.Backup)
     self.autosaveTimer = QTimer()
     self.autosaveTimer.setInterval(60000)
     self.autosaveTimer.setSingleShot(False)
     QObject.connect(self.autosaveTimer, SIGNAL("timeout()"), self.Autosave)
     self.Translate(str(self.settings.value("language", "English").toString()))
     self.projFilter = self.tr("Data centers projects (*.dcxml *.xml)")
     self.setWindowTitle(self.tr("Untitled") + " - " + self.basename)
class MainWindow(QMainWindow):
    project = None
    projectFile = "default_project.xml"
    tenants = {}
    generators = {}

    MaxRecentFiles = 10
    ''' Limit on the number of items in recent files list'''

    recentFileActions = []
    ''' Recent files list'''

    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.loadTranslations()
        self.settings = QSettings("LVK Inc", "DataCenters")   
        self.resourcesGraphEditor = ResourcesGraphEditor()
        self.tenantEditor = TenantEditor()
        self.Vis = Vis()
        self.tenants = {}
        self.project = Project()
        # TODO: Captain, we have a problem!
        # For some reason, in Python 2.7 QSettings converts dicts to QVariant
        # So the ini file is undecypherable
        # This works fine in Python 3.2 by the way
        #if self.settings.value("vis"):
            #self.Vis.canvas.settings = self.settings.value("vis")
        #self.graphvis.settings = self.settings.value("graphVis")
        self.settingsDialog = SettingsDialog(self.Vis.canvas.settings)
        self.settingsDialog.ui.backup.setChecked(self.settings.value("backup").toBool())
        self.settingsDialog.ui.autosave.setChecked(self.settings.value("autosave").toBool())
        self.settingsDialog.ui.interval.setValue(self.settings.value("interval").toInt()[0])
        paramxml = self.settings.value("paramxml", "params").toString()
        ParamFactory.LoadDir(str(paramxml))
        self.settingsDialog.ui.params.setText(paramxml)
        i = 0
        for s in self.languages:
            self.settingsDialog.ui.languages.addItem(s)
            if s == str(self.settings.value("language").toString()):
                self.settingsDialog.ui.languages.setCurrentIndex(i)
            i += 1
        self.resourcesGraphEditor.setData(self.project.resources)
        for i in range(self.MaxRecentFiles):
            a = QAction(self)
            a.setVisible(False)
            a.setEnabled(False)
            if i <= 9:
                a.setShortcut(QKeySequence(self.tr("Alt+") + str(i + 1)))
            QObject.connect(a, SIGNAL("triggered()"), self.OpenRecentFile);
            self.ui.menuFile.insertAction(self.ui.actionExit, a)
            self.recentFileActions.append(a)
        self.UpdateRecentFileActions()
        self.basename = self.windowTitle()
        self.tenantEditor.tenant_changed.connect(self.tenantChanged)
        self.backupTimer = QTimer()
        self.backupTimer.setInterval(60000)
        self.backupTimer.setSingleShot(False)
        QObject.connect(self.backupTimer, SIGNAL("timeout()"), self.Backup)
        self.autosaveTimer = QTimer()
        self.autosaveTimer.setInterval(60000)
        self.autosaveTimer.setSingleShot(False)
        QObject.connect(self.autosaveTimer, SIGNAL("timeout()"), self.Autosave)
        self.Translate(str(self.settings.value("language", "English").toString()))
        self.projFilter = self.tr("Data centers projects (*.dcxml *.xml)")
        self.setWindowTitle(self.tr("Untitled") + " - " + self.basename)
        #self.loadPlugins()

    def NewProject(self):
        self.project = Project()
        self.resourcesGraphEditor.setData(self.project.resources)
        self.projectFile = None
        self.tenants = {}
        self.ui.tenants.clear()
        self.setWindowTitle(self.tr("Untitled") + " - " + self.basename)
        self.backupTimer.start()
        self.autosaveTimer.start()
    
    def OpenProject(self):
        name = unicode(QFileDialog.getOpenFileName(filter=self.projFilter))
        if name == None or name == '':
            return
        self.OpenProjectFromFile(name)
        
    def OpenProjectFromFile(self, name):
        self.tenants = {}
        self.project = Project()
        
        #try:
        self.project.Load(name)
        #except :
            # TODO: proper exceptioning
        #    QMessageBox.critical(self, self.tr("An error occured"), self.tr("File is not a valid project file: ") + name)
        #    return
        self.projectFile = name
        self.resourcesGraphEditor.setData(self.project.resources)
        self.ui.tenants.clear()
        for d in self.project.tenants:
            it = QTreeWidgetItem(self.ui.tenants, QStringList([d.name, self.tr("No") if d.critical else self.tr("Yes"), self.tr("Yes") if d.assigned else self.tr("No")]))
            cb = QComboBox()
            cb.addItems([self.tr("No"),self.tr("Yes")])
            cb.setCurrentIndex(0 if d.critical else 1)
            QObject.connect(cb, SIGNAL("currentIndexChanged(int)"), it.emitDataChanged)
            self.ui.tenants.setItemWidget(it,1,cb)
            it.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.tenants[it] = d
        self.UpdateRecentFiles()
        self.setWindowTitle(self.project.name + " - " + self.basename)
        self.ui.projectname.setText(self.project.name)
        self.showStats()
        self.backupTimer.start()
        self.autosaveTimer.start()

    def OpenRecentFile(self):
        ''' Opens a project from recent files list'''
        text = unicode(self.sender().data().toString())
        if os.path.exists(text):
            self.OpenProjectFromFile(text)
            self.UpdateRecentFiles()
        else:
            QMessageBox.critical(self, self.tr("Error"), self.tr("Project not found"))
            self.RemoveFromRecentFiles(text)
    
    def SaveProject(self):
        if self.projectFile == None:
            self.SaveProjectAs()
        else:
            self.project.Save(self.projectFile)
            self.UpdateRecentFiles()
    
    def SaveProjectAs(self):
        self.projectFile = unicode(QFileDialog.getSaveFileName(directory=self.project.name + ".dcxml", filter=self.projFilter))
        if self.projectFile != '':
            self.project.Save(self.projectFile)
            self.UpdateRecentFiles()
        self.setWindowTitle(self.projectFile.split('/').pop().split('.')[0] + " - " + self.basename)

    def Backup(self):
        if self.settings.value("backup").toBool():
            self.project.Save(self.projectFile + ".bak")

    def Autosave(self):
        if self.settings.value("autosave").toBool():
            self.project.Save(self.projectFile)

    def InitProject(self):
        self.project.resources._buildPaths()
        #self.project.method.tenant_assigned.connect(self.tenantAssigned)

    def Run(self):
        self.Reset()
        self.InitProject()
        #self.project.method.Clear()
        self.Reset()
        self.project.Save(self.projectFile)
        if self.ui.algorithm.currentIndex() == 0:
            alg = "s"
        elif self.ui.algorithm.currentIndex() == 1:
            alg = "a"
        elif self.ui.algorithm.currentIndex() == 2:
            alg = "c"
        elif self.ui.algorithm.currentIndex() == 3:
            alg = "d"
        elif self.ui.algorithm.currentIndex() == 4:
            alg = "f"
        else:
            alg = "r"
        if sys.platform.startswith("win"):
            name = "algo.exe"
        else:
            name = "algo/algo"
        if alg != "s":
            os.system(name + " \"" + os.path.relpath(self.projectFile) + "\"  \"" + os.path.relpath(self.projectFile) + "\" ")
            #self.project.Run()
            self.OpenProjectFromFile(self.projectFile)
        else:
            alg = SimulatedAnnealing(self.project)
            alg.Run()
        self.showStats()

    def RunMultipleTests(self):
        window = TestsWindow(self)
        window.show()
        while window.isVisible():
            qApp.processEvents()

    def RunSelected(self):
        fname = QFileDialog.getSaveFileName(directory="results.txt")
        results = []
        for alg in "acdfr":
            self.Reset()
            self.project.Save(self.projectFile)
            if sys.platform.startswith("win"):
                name = "Algorithm\\algorithm.exe"
            else:
                name = "Algorithm/Algolib"
            os.system(name + " \"" + os.path.relpath(self.projectFile) + "\" -c \"" + os.path.relpath(self.projectFile) + "\" " + alg)
            self.OpenProjectFromFile(self.projectFile)
            stats = self.project.GetStats()
            stats["algorithm"] = alg
            results.append(stats)
        text = ""
        for k in stats.keys():
            text += k + "\t"
        text += "\n"
        for r in results:
            for k in stats.keys():
                text += str(r[k]) + "\t"
            text += "\n"
        f = open(fname, "w")
        f.write(text)
        f.close()

    def showStats(self):
        if self.project.resources.vertices == []:
            return
        stats = self.project.GetStats()
        self.ui.tenantcount.setText(str(stats["tenants"]))
        self.ui.ratio.setText(str(stats["ratio"])+"%")
        self.ui.netavg.setText(str(stats["netavg"])+"%")
        self.ui.leafavg.setText(str(stats["leafavg"])+"%")

    def EditProgram(self):
        self.resourcesGraphEditor.canvas.changed = False
        self.resourcesGraphEditor.show()
        while self.resourcesGraphEditor.isVisible():
            qApp.processEvents()
        if self.resourcesGraphEditor.canvas.changed:
            for t in self.project.tenants:
                t.RemoveAssignment()

    def AddTenant(self):
        d = self.project.CreateTenant()
        it = QTreeWidgetItem(self.ui.tenants, QStringList(["New_tenant", self.tr("No"), self.tr("No")]))
        cb = QComboBox()
        cb.addItems([self.tr("No"),self.tr("Yes")])
        self.ui.tenants.setItemWidget(it,1,cb)
        QObject.connect(cb, SIGNAL("currentIndexChanged(int)"), it.emitDataChanged)
        it.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        self.tenants[it] = d
        self.ui.tenants.editItem(it)
        self.tenants[it].name = unicode(it.text(0))
        self.tenants[it].critical = False if self.ui.tenants.itemWidget(it,1).currentText() == self.tr("Yes") else True
        self.UpdateTenant(it)
    
    def DeleteTenant(self):
        item = self.ui.tenants.currentItem()
        if (item == None):
            return
        self.project.RemoveTenant(self.tenants[item])
        del self.tenants[item]
        self.ui.tenants.takeTopLevelItem(self.ui.tenants.indexOfTopLevelItem(item))
        del item

    def UpdateTenant(self, item):
        if item in self.tenants:
            rename = True
            entered = unicode(item.text(0))
            name = entered
            index = 1
            while rename:
                fixed = False
                for t in self.project.tenants:
                    if t.name == name:
                        if t != self.tenants[item]:
                            name = entered + " (" + str(index) + ")"
                            index += 1
                            fixed = True
                if not fixed:
                    rename = False
            item.setText(0, name)
            self.tenants[item].name = name
            
    def EditTenant(self):
        if (self.tenants == {}) or (self.ui.tenants.currentItem() == None):
            return
        d = self.tenants[self.ui.tenants.currentItem()]
        #d.RemoveAssignment()
        self.tenantEditor.canvas.tenants = self.project.tenants
        self.tenantEditor.setData(d)
        self.tenantEditor.show()

    def RandomTenant(self):
        d = self.randomDialog
        types = []
        for v in self.project.resources.vertices:
            if isinstance(v,Storage) and (types.count(v.type)==0):
                types.append(v.type)
        if len(types) == 1: #only type 0
            d.ui.cc1.setEnabled(False)
            d.ui.cc2.setEnabled(False)
        d.exec_()
        if d.result() == QDialog.Accepted: 
            dict = d.GetResult()
            dict["types"] = types
            for i in range(dict["n"]):
                tenant = self.project.CreateRandomTenant(dict)
                it = QTreeWidgetItem(self.ui.tenants, QStringList([tenant.id, self.tr("No"), self.tr("No")]))
                cb = QComboBox()
                cb.addItems([self.tr("No"),self.tr("Yes")])
                self.ui.tenants.setItemWidget(it,1,cb)
                QObject.connect(cb, SIGNAL("currentIndexChanged(int)"), it.emitDataChanged)
                it.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                self.tenants[it] = tenant

    def Reset(self):
        self.project.Reset()
        for k in self.tenants.keys():
            k.setText(2, self.tr("No"))
        self.showStats()

    def About(self):
        pass

    def Exit(self):
        sys.exit(0)

    def EditName(self):
        self.lineedit = QLineEdit(self.ui.projectname.parentWidget())
        self.lineedit.setGeometry(self.ui.projectname.geometry())
        self.lineedit.setText(self.ui.projectname.text())
        self.lineedit.setFocus()
        self.lineedit.show()
        self.ui.projectname.hide()
        # TODO: what's wrong?
        #self.ui.editname.hide()
        QObject.connect(self.lineedit, SIGNAL("editingFinished()"), self.ChangeName)

    def ChangeName(self):
        s = self.lineedit.text()
        self.ui.projectname.setText(s)
        self.ui.projectname.show()
        #self.ui.editname.show()
        self.lineedit.hide() 
        self.project.name = s
        self.setWindowTitle(self.project.name + " - " + self.tr("Data Centers GUI")) 

    def RemoveFromRecentFiles(self, s):
        ''' Removes an item from recent files list'''
        files = self.settings.value("recentFileList").toStringList();
        files.removeAll(s);
        self.settings.setValue("recentFileList", files);
        self.UpdateRecentFileActions()

    def UpdateRecentFiles(self):
        ''' Updates the recent files list to keep the chronological order'''
        files = self.settings.value("recentFileList").toStringList();
        files.removeAll(self.projectFile);
        files.prepend(self.projectFile);
        while files.count() > self.MaxRecentFiles:
            files.removeAt(files.count()-1)

        self.settings.setValue("recentFileList", files);
        self.UpdateRecentFileActions()

    def UpdateRecentFileActions(self):
        ''' Updates the list of QActions for recent files'''
        files = self.settings.value("recentFileList").toStringList();
        numRecentFiles = min(files.count(), self.MaxRecentFiles);

        for i in range(self.MaxRecentFiles):
            if i < numRecentFiles:
                text = self.tr("&%1: %2").arg(i + 1).arg(os.path.basename(unicode(files[i]))[:-6])
                self.recentFileActions[i].setText(text)
                self.recentFileActions[i].setData(files[i])
                self.recentFileActions[i].setVisible(True)
                self.recentFileActions[i].setEnabled(True)
            else:
                self.recentFileActions[i].setVisible(False)
                self.recentFileActions[i].setEnabled(False)

    def tenantChanged(self):
        it = self.ui.tenants.currentItem()
        it.setText(0, self.tenants[it].name)
        self.showStats()

    def ShowResults(self):
        self.Vis.setData(self.project)
        self.Vis.show()

    def ShowGraphVis(self):
        self.graphvis.setData(self.project)
        self.graphvis.show()

    def tenantAssigned(self, id):
        item = self.ui.tenants.findItems(id, Qt.MatchExactly)[0]
        item.setText(4, "Yes")

    def Settings(self):
        self.settingsDialog.exec_()
        if self.settingsDialog.result() == QDialog.Accepted:
            self.settings.setValue("vis", self.Vis.canvas.settings)  
            self.settings.setValue("backup", self.settingsDialog.ui.backup.isChecked())
            self.settings.setValue("autosave", self.settingsDialog.ui.autosave.isChecked())
            self.settings.setValue("interval", self.settingsDialog.ui.interval.value())
            self.settings.setValue("paramxml", self.settingsDialog.ui.params.text())
            ParamFactory.LoadDir(str(self.settingsDialog.ui.params.text()))
            self.autosaveTimer.setInterval(self.settings.value("interval").toInt()[0] * 1000)
            newlang = self.settingsDialog.ui.languages.currentText()
            if newlang != self.settings.value("language"):
                self.Translate(newlang)
                self.settings.setValue("language", newlang)


    def Translate(self, lang):
        translator = QTranslator(qApp)
        translator.load(":Translations/dc_" + lang + ".qm")
        qApp.installTranslator(translator)
        self.basename = self.tr("Data Centers GUI")
        self.tenantEditor.basename = self.tenantEditor.tr("Tenant Editor")
        self.resourcesGraphEditor.basename = self.resourcesGraphEditor.tr("Resources Graph Editor")
        self.ui.retranslateUi(self)
        self.settingsDialog.ui.retranslateUi(self.settingsDialog)
        self.tenantEditor.ui.retranslateUi(self.tenantEditor)
        self.resourcesGraphEditor.ui.retranslateUi(self.resourcesGraphEditor)
        self.Vis.ui.retranslateUi(self.Vis)
        self.showStats()
        for k in self.tenants.keys():
            cb = QComboBox()
            cb.addItems([self.tr("No"),self.tr("Yes")])
            cb.setCurrentIndex(0 if self.tenants[k].critical else 1)
            QObject.connect(cb, SIGNAL("currentIndexChanged(int)"), k.emitDataChanged)
            self.ui.tenants.setItemWidget(k,1,cb)
            if self.tenants[k].assigned:
                k.setText(2, self.tr("Yes"))
            else:
                k.setText(2, self.tr("No"))

    def loadTranslations(self):
        all = QDir(":Translations").entryList()
        tsfile = re.compile("dc_([a-zA-z]*)\.qm")
        res = []
        for s in all:
            m = tsfile.match(s)
            if m != None:
                res.append(m.group(1))
        self.languages = res

    def loadPlugins(self):
        sys.path.append(os.curdir + os.sep + "plugins")
        plugins = QActionGroup(self)
        for s in os.listdir("plugins"):
            # TODO: check all errors
            if s.endswith(".py"):
                plugin = __import__(s[:-3])
                if plugin == "__init__":
                    continue
                if "pluginMain" in dir(plugin):
                    pluginClass = plugin.pluginMain()
                    name = pluginClass.GetName()
                    action = QAction(name, self)
                    action.setCheckable(False)
                    QObject.connect(action, SIGNAL("triggered()"), self.GenerateRequests)
                    plugins.addAction(action)
                    self.ui.menuGenerators.addAction(action)
                    self.generators[action] = pluginClass()
                else:
                    print("pluginMain not found in " + s)

    def GenerateRequests(self):
        generator = self.generators[self.sender()]
        data = generator.GetSettings()
        d = ParamsDialog(data, self, generator.GetName()=="Tightly coupled", generator.GetName()=="Tightly coupled")
        d.exec_()
        if d.result() == QDialog.Accepted:
            generator.UpdateSettings(d.data)
            #TODO: populate the table with new tenants
            self.project.tenants = generator.Generate(self.project.resources)
            self.ui.tenants.clear()
            self.tenants = {}
            for tenant in self.project.tenants:
                it = QTreeWidgetItem(self.ui.tenants, QStringList([tenant.id, str(tenant.startTime), str(tenant.endTime), self.tr("No"), self.tr("No")]))
                cb = QComboBox()
                cb.addItems([self.tr("No"),self.tr("Yes")])
                self.ui.tenants.setItemWidget(it,1,cb)
                QObject.connect(cb, SIGNAL("currentIndexChanged(int)"), it.emitDataChanged)
                it.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                self.tenants[it] = tenant