'''--------------------------------------------------------------------------------------------------
Im Controller wird in der Init eine View erstellt und gestartet. Zudem wird der Acquisitionserver 
gestartet, die shared Librarz geladen, die Konstanten Initialtisiert.
Mittels der Funktion Action Performed wird die jeweilige Reaktion auf die Benutyeraktion ausgeloest.
Hierfuer stehen die verschiedenen eigenen Funktionen die die Reaktion in der richtigen reihenfolge 
an ein neu instanizieertes Modell weitergibt.
Zudem stehen einige Funktionen zur Informationsuebergabe an die View oder das Modell zur Verfuegung
'''


from UIModellVisuell import *
from UIModellTaktil import *
from UIModell import *
import UIViewTKinter as viewTkinter
from shutil import copyfile
import ctypes 
import os



class Controller():
    
    def __init__(self):
        '''--------------------------------------------------------------------------------------------------
        In der Init wird die shared Library geladen, die View erstellt und geoeffnet und die verschiedenen
        Konstanten gesetzt
        '''

        self.getSharedLibrary()

        self.view = viewTkinter.View(self, self.dll.getDefaultPath_visuell())
        self.modi = "visuellesBCI"
        self.pathOVFile = self.dll.getPathOVFile_visuell()
        self.pathSpatialCfg = self.dll.getSpatialCFGFile_visuell()
        self.pathClassifierCfg = self.dll.getClassifierCFGFile_visuell()
        self.infotext = ""

        self.commands = {
            "copySpelling": self.commandoCopySpelling,
            "stop": self.commandStop,
            "freeSpelling": self.commandFreeSpelling,
            "test": self.test,
            "wechsel": self.wechsel,
            "speicherort": self.setDataset
        }

        self.nexts = {
            "filterXdawn" : self.filterXdawn,
            "filterClassic": self.filterClassic,
            "save": self.speichern
        }

        self.pagesTaktil = {
            "stop": "StartPage",
            "copySpelling": "WorkingPageTaktil",
            "freeSpelling": "WorkingPageTaktil",
            "test": "WorkingPageTaktil"
        }
        self.pagesVisuell = {
            "stop": "StartPage",
            "copySpelling": "WorkingPageVisuell",
            "freeSpelling": "WorkingPageVisuell",
            "test": "WorkingPageVisuell"
        }
        self.acquisitionServer = Modell(self, self.dll)
        self.acquisitionServer.start()

        self.model = None
        self.view.mainloop()

    def getSharedLibrary(self):
        '''--------------------------------------------------------------------------------------------------
        Laedt die shared Library (diese muss sich im gleichen Ordner wie die ausfuehrunde Datei befinden) 
        und setzt die Eingabe und Rueckgabewerte.
        '''

        path = os.path.abspath(".")
        path = path + "/dll.so"
        self.dll = ctypes.CDLL(path)

        self.dll.getCommandFreespellingTaktil.argtypes = []
        self.dll.getCommandFreespellingTaktil.restype = ctypes.c_char_p

        self.dll.getCommandFreespellingVisuell.argtypes = []
        self.dll.getCommandFreespellingVisuell.restype = ctypes.c_char_p

        self.dll.getCommandCopyspellingTaktil.argtypes = []
        self.dll.getCommandCopyspellingTaktil.restype = ctypes.c_char_p

        self.dll.getCommandCopyspellingVisuell.argtypes = []
        self.dll.getCommandCopyspellingVisuell.restype = ctypes.c_char_p

        self.dll.getCommandXDawn_taktil.argtypes = []
        self.dll.getCommandXDawn_taktil.restype = ctypes.c_char_p

        self.dll.getCommandXDawn_visuell.argtypes = []
        self.dll.getCommandXDawn_visuell.restype = ctypes.c_char_p

        self.dll.getCommandClassifier_visuell.argtypes = []
        self.dll.getCommandClassifier_visuell.restype = ctypes.c_char_p

        self.dll.getCommandClassifier_taktil.argtypes = []
        self.dll.getCommandClassifier_taktil.restype = ctypes.c_char_p

        self.dll.getDefaultPath_visuell.argtypes = []
        self.dll.getDefaultPath_visuell.restype = ctypes.c_char_p

        self.dll.getDefaultPath_taktil.argtypes = []
        self.dll.getDefaultPath_taktil.restype = ctypes.c_char_p

        self.dll.getPathOVFile_visuell.argtypes = []
        self.dll.getPathOVFile_visuell.restype = ctypes.c_char_p

        self.dll.getSpatialCFGFile_visuell.argtypes = []
        self.dll.getSpatialCFGFile_visuell.restype = ctypes.c_char_p

        self.dll.getClassifierCFGFile_visuell.argtypes = []
        self.dll.getClassifierCFGFile_visuell.restype = ctypes.c_char_p

        self.dll.getPathOVFile_taktil.argtypes = []
        self.dll.getPathOVFile_taktil.restype = ctypes.c_char_p

        self.dll.getSpatialCFGFile_taktil.argtypes = []
        self.dll.getSpatialCFGFile_taktil.restype = ctypes.c_char_p

        self.dll.getClassifierCFGFile_taktil.argtypes = []
        self.dll.getClassifierCFGFile_taktil.restype = ctypes.c_char_p

        self.dll.getCommandStartAquisitionServer.argtypes = []
        self.dll.getCommandStartAquisitionServer.restype = ctypes.c_char_p        
    
    def actionPerformed(self, action):
        '''--------------------------------------------------------------------------------------------------
        Wird aufgerufen wenn ein Button in der Init gedrueckt wird zuerst wird kontrolliert ob die Seite 
        gewechselt werden muss, dann nach dem Buttonnamen die Funktion zugeordnert. Der Funktion zum
        Buttomnamen wird in der Init festgelegt
        '''

        self.changePage(action)
        func = self.commands.get(action)
        if(func is not None):
            func()
        else:
            print("Kommado existiert nicht")


    def changePage(self,action):
        '''--------------------------------------------------------------------------------------------------
        Change Page ruft je nach Aktion und Modi die jeweilige Seite auf
        '''

        if(self.modi == "taktilesBCI"):
            page = self.pagesTaktil.get(action)
        elif(self.modi == "visuellesBCI"):
            page = self.pagesVisuell.get(action)
        if(page is not None):
            self.view.changeFrame(page)

    def wechsel(self):
        '''--------------------------------------------------------------------------------------------------
        Wechsel aendert den Modi. Dafuer wird zuerst der aktuelle Modi ueberprueft und dann auf den anderen 
        Modi gesetzt.
        '''

        self.resetInfo()
        self.addInfoText("")
        if(self.modi == "taktilesBCI"):
            self.view.setChangeBtnText("Wechsel zu taktilen BCI")
            self.view.setTitleText("visuelles BCI")
            self.view.setDefaultPath(self.dll.getDefaultPath_visuell())
            self.pathOVFile = self.dll.getPathOVFile_visuell()
            self.pathSpatialCfg = self.dll.getSpatialCFGFile_visuell()
            self.pathClassifierCfg = self.dll.getClassifierCFGFile_visuell()
            self.modi = "visuellesBCI"
        elif(self.modi == "visuellesBCI"):
            self.setTitle("taktiles BCI")
            self.view.setChangeBtnText("Wechsel zu visuellen BCI")
            self.view.setDefaultPath(self.dll.getDefaultPath_taktil())
            self.pathOVFile = self.dll.getPathOVFile_taktil()
            self.pathSpatialCfg = self.dll.getSpatialCFGFile_taktil()
            self.pathClassifierCfg = self.dll.getClassifierCFGFile_taktil()
            self.modi = "taktilesBCI"

    def speichern(self):
        '''--------------------------------------------------------------------------------------------------
        wird nach dem Classifier Training aufgerufen und speichert die erstellte .ov Datei und die 
        spatial.cfg, classifier.cgf-Dateine unter dem ausgewaehlten Ordnername. 
        (Ordnername wird zu Beginn des CopySpellings festgelegt)
        '''
        print("saveFile")
        print(self.file)
        os.mkdir(self.file)
        copyfile(self.pathOVFile, (self.file +  "/p300-xdawn-train.ov"))
        copyfile(self.pathClassifierCfg, (self.file + "/p300-classifier.cfg"))
        copyfile(self.pathSpatialCfg, (self.file + "/p300-spatial-filter.cfg"))

        #copyfile(self.pathOVFile, self.file)

    def setDataset(self):
        '''--------------------------------------------------------------------------------------------------
        Legt fest welches Datenset fuer das Freestelling genutzt wird
        '''

        file = self.view.openfiledialog()
        print(file)
        if not  file:
            print("nichts ausgewaehlt")
        else:
            if os.path.exists((file +  "/p300-classifier.cfg")):
                print("classifier kopiert")
                copyfile((file + "/p300-classifier.cfg"), self.pathClassifierCfg)
            if os.path.exists((file +  "/p300-spatial-filter.cfg")):
                print("spatialfilter kopiert")
                copyfile((file + "/p300-spatial-filter.cfg"), self.pathSpatialCfg)
            if os.path.exists((file +  "/p300-xdawn-train.ov")):
                print(".ov kopiert")
                copyfile((file +  "/p300-xdawn-train.ov"), self.pathOVFile)
            


    def test(self):
        '''--------------------------------------------------------------------------------------------------
        Funktion zum testen von xDawn und ClassifierTraining 
        '''
        self.file = self.view.savefiledialog()
        if(self.modi == "taktilesBCI"):
            self.model = ModellTaktil(self, dll=self.dll)
        elif(self.modi == "visuellesBCI"):
            self.model = ModellVisuell(self, dll=self.dll)
        self.model.setFunktion(self.model.trainXDawn)
        self.model.start()

    def commandoCopySpelling(self):
        self.view.savefiledialog()
        

    def copyspelling(self, path):
        '''--------------------------------------------------------------------------------------------------
        Speichert den  gewuenschte Pfad und erstellt anschliessend ein Model (als Thread) und fuehrt die 
        Funktion CopySpelling aus.
        '''
        self.file = path
        print(path)
        if self.file == "-1":
            print("kein gueltiger Pfad")
            self.view.changeFrame("StartPage")
        else:
            if(self.modi == "taktilesBCI"):
                self.model = ModellTaktil(self, dll=self.dll)
            elif(self.modi == "visuellesBCI"):
                self.model = ModellVisuell(self, dll=self.dll)
            self.model.setFunktion(self.model.startCopySpelling)
            self.model.start()

    def filterXdawn(self):
        '''--------------------------------------------------------------------------------------------------
        wird nach dem CopySpelling gestartet. Die Funktion erstellt ein Model (als Thread) und fuehrt die 
        Funktion trainXDawn aus.
        '''

        if(self.modi == "taktilesBCI"):
            self.model = ModellTaktil(self, dll=self.dll)
        elif(self.modi == "visuellesBCI"):
            self.model = ModellVisuell(self, dll=self.dll)
        self.model.setFunktion(self.model.trainXDawn)
        self.model.start()

    def filterClassic(self):
        '''--------------------------------------------------------------------------------------------------
        wird nach dem xDawn-Training gestartet. Die Funktion erstellt ein Model (als Thread) und fuehrt die 
        Funktion trainClassifier aus.
        '''

        if(self.modi == "taktilesBCI"):
            self.model = ModellTaktil(self, dll=self.dll)
        elif(self.modi == "visuellesBCI"):
            self.model = ModellVisuell(self, dll=self.dll)
        self.model.setFunktion(self.model.trainClassifier)
        self.model.start()

    def commandFreeSpelling(self):
        '''--------------------------------------------------------------------------------------------------
        startet nach Benutzeraktion das Freespelling: ie Funktion erstellt ein Model (als Thread) und fuehrt die 
        Funktion freespelling aus.
        '''
        print("freespelling")
        if(self.modi == "taktilesBCI"):
            self.model = ModellTaktil(self, dll=self.dll)
        elif(self.modi == "visuellesBCI"):
            self.model = ModellVisuell(self, dll=self.dll)
        self.model.setFunktion(self.model.freeSpelling)
        self.model.start()


    def commandStop(self):
        '''--------------------------------------------------------------------------------------------------
        Wird nach Benutzeraktion aufgerufen und stopt, falls vorhanden, den ablaufenden Thread und schliesst
        Openvibe
        '''
        if(self.model is not None):
            self.addInfoText("Aktion: STOP-Befehl\n")
            self.model.stop()
            self.model.join()
            self.model.killProzess()
            self.mode = None    
    
    def stopAcquisitionServer(self):
        '''--------------------------------------------------------------------------------------------------
        Wird nur beim schliessen der GUI aufgerufen und stopt und schliesst den Acquisitionserver
        '''
        self.acquisitionServer.stop()
        self.acquisitionServer.join()
        self.acquisitionServer.killAcquisitionServer()
        self.acquisitionServer = None

    def stop(self, next=None):
        '''--------------------------------------------------------------------------------------------------
        Wird aus dem Modell nach Ablauf oder bei Fehler aufgerufen und stopt den aktuellen Thread. 
        Anschliessend wird je nach Prozess die naechste Funktion (XDawn-Training oder Classifier) aufgerufen
        '''
        print("Stop aus Modell")
        self.model.stop()
        self.model.join()
        self.model.killProzess()
        self.mode = None    
        if(next is not None):
            func = self.nexts.get(next)
            func()


    def addInfoText(self,text):
        '''--------------------------------------------------------------------------------------------------
        Fuegt dem Infotext den Text hinzu und schreibt es in die Ausgabe 
        '''
        self.infotext = self.infotext + text
        self.view.setInfoText(self.infotext)

    def resetInfo(self):
        self.infotext = ""

    def setTitle(self,text):
        self.view.setTitleText(text)  

    def changeScreen(self, pageName):
        self.view.changeFrame(pageName=pageName) 

    def getModi(self):
        return self.modi

    def getWindowSize(self):
        return self.view.getWindowSize()
    



