"""
Importation de données de Gemasco vers SLM,
sans modifier la structure de la base de données
"""

from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox, \
    QFileDialog, QListView, QTreeView
from PyQt6.QtGui import QFileSystemModel
from PyQt6.QtCore import QDir, QProcess, QThread

from Ui_importeDeGemasco import Ui_MainWindow

import sys, os
from subprocess import run

class MyMW (QMainWindow, Ui_MainWindow):
    def __init__(self, parent = None, argv=[]):
        QMainWindow.__init__(self, parent)
        self.argv = argv
        self.setupUi(self)
        self.actionChoix_du_r_pertoire.triggered.connect(self.choix_repertoire)
        self.actionXslx_CSV.triggered.connect(self.runConversions)
        self.convertButton.clicked.connect(self.runConversions)
        self.actionQuitter.triggered.connect(self.close)
        self.pwd = os.getcwd()
        if len(argv) >= 2:
            self.pwd = argv[1]
        self.xls_view = None
        self.csv_view = None
        # mise en place de l'arbre des répertoires
        self.dirModel = QFileSystemModel()
        dm = self.dirModel
        dm.setRootPath(QDir.rootPath())
        dm.setFilter(QDir.Filter.NoDotAndDotDot | QDir.Filter.AllDirs)
        self.dir_treeView.setModel(dm)
        self.dir_treeView.setRootIndex(dm.index(self.pwd))
        self.dir_treeView.clicked.connect(self.on_dir_clicked)
        self.dir_treeView.setColumnWidth(0, 400)
        self.initExcelCsvListViews()
        # place d'abord l'onglet des répertoires
        self.tabWidget.setCurrentIndex(0)
        self.idealThreadCount = QThread.idealThreadCount()
        return

    def on_dir_clicked(self, index):
        path = self.dirModel.fileInfo(index).absoluteFilePath()
        self.excel_listView.setRootIndex(self.excel_fileModel.setRootPath(path))
        self.csv_listView.setRootIndex(self.csv_fileModel.setRootPath(path))
        self.pwd = path
        return

    def initExcelCsvListViews(self):
        """
        Affiche les contenus de self.excel_listView et self.csv_listView
        en se basant sur self.pwd
        """
        def filemodel(names):
            fm = QFileSystemModel()
            fm.setFilter(QDir.Filter.NoDotAndDotDot |  QDir.Filter.Files)
            fm.setNameFilters(names)
            fm.setNameFilterDisables(False) # don't show when filtered out
            return fm
        
        fm = filemodel(["*.xlsx"])
        self.excel_listView.setModel(fm)
        self.excel_listView.setRootIndex(fm.setRootPath(self.pwd))
        self.excel_fileModel = fm
       
        fm = filemodel(["*.csv"])
        self.csv_listView.setModel(fm)
        self.csv_listView.setRootIndex(fm.setRootPath(self.pwd))
        self.csv_fileModel = fm
        return
    
    def choix_repertoire(self):
        self.tabWidget.setCurrentIndex(0)
        return

    def tabActions(self):
        self.tabWidget.setCurrentIndex(1)
        return

    convertIndex = 0      # sera incrémenté par newConvertProcess
    listeConversions = {} # en fait, dictionnaire int => instance de QProcess
    
   
    @staticmethod
    def newConvertProcess(infile, out, err, callback):
        """
        Crée un nouveau processus de conversion, prêt à démarrer avec start(),
        et lui associe un numéro autoincrémenté
        @param infile le fichier XSLX utilisé en entrée
        @param out une instance de QPlainTextEdit
        @param err une instance de QPlainTextEdit
        @param callback une fonction de rappel pour traiter les changements
               d'état du processus et sa terminaison ; le profil de cette
               fonction est (index:int, message) => None
        @return None ; mais modifie par effet de bord MyMW.convertIndex et
                MyMW.listeConversions
        """
        p = QProcess()
        MyMW.convertIndex += 1
        index = MyMW.convertIndex
        prefix = f"Conversion n° {index} : "
        
        def report_stdout():
            data = p.readAllStandardOutput()
            out.appendPlainText(prefix + bytes(data).decode("utf8"))
            return
        
        def report_stderr():
            data = p.readAllStandardError()
            err.appendPlainText(prefix + bytes(data).decode("utf8"))
            return

        def report_state(s):
            callback(index, s)
            return

        def cleanup():
            callback(index, "end")
            return
        
        p.readyReadStandardOutput.connect(report_stdout)
        p.readyReadStandardError.connect(report_stderr)
        p.stateChanged.connect(report_state)
        p.finished.connect(cleanup)
        p.setProgram("/usr/bin/libreoffice")
        p.setArguments(["--headless",
                        "--convert-to",
                        "csv",
                        infile,
                        "--outdir",
                        os.path.dirname(infile),
                        ])
        MyMW.listeConversions[index] = {"process": p, "fired": False}
        return

    conversions_actives = 0
    
    def activeConversionsDormantes(self):
        if self.conversions_actives >= self.idealThreadCount:
            return
        dormants = [p for i,p in self.listeConversions.items() \
                    if not p["fired"]]
        # on lance autant de programmes qu'il est bon d'en lancer
        for i in range (min(len(dormants),
                            self.idealThreadCount - self.conversions_actives)):
            dormants[i]["fired"] = True
            dormants[i]["process"].start()
            self.conversions_actives +=1
            print("GRRRRR on lance un processus dormant :", dormants[i])
        return
        
    def runConversions(self):
        deja_convertis = []
        model = self.csv_listView.model()
        idx = model.index(model.rootPath())
        for i in range(0, model.rowCount(idx)):
            mi = model.index(i, 0, idx)
            deja_convertis.append(model.fileInfo(mi).absoluteFilePath())
        
        a_convertir = []
        model = self.excel_listView.model()
        idx = model.index(model.rootPath())
        for i in range(0, model.rowCount(idx)):
            mi = model.index(i, 0, idx)
            path = model.fileInfo(mi).absoluteFilePath()
            if path.replace(".xlsx",".csv") in deja_convertis:
                continue
            a_convertir.append(path)
        print("GRRRR deja_convertis", deja_convertis)
        print("GRRRR a_convertir", a_convertir)

        self.tabActions()
        def cb(index, message):
            """
            fonction de rappel pour les changements d'état des process et leur
            terminaison
            """
            print("rappel GRRRR process", index, message)
            if message == "end":
                # terminaison du programme d'index 'index'.
                print(f"GRRRR le process {index} est fini")
                self.conversions_actives -= 1
                # on vient de finir un process,
                # voyons s'il en reste d'autres à activer
                self.activeConversionsDormantes()
            return
        
        for infile in a_convertir:
            self.newConvertProcess(infile, self.stdout_TextEdit, self.stderr_TextEdit, cb)
        self.activeConversionsDormantes()
        return

    def cleanup(self):
        print("GRRRR: Process finished.")
        self.process = None
        return

    def report_stdout(self):
        data = self.process.readAllStandardOutput()
        stdout = bytes(data).decode("utf8")
        print("GRRRR stdout :", stdout)
        self.stdout_TextEdit.appendPlainText(stdout)
        return

    def report_stderr(self):
        data = self.process.readAllStandardError()
        stderr = bytes(data).decode("utf8")
        print("GRRRR stderr :", stderr)
        self.stderr_TextEdit.appendPlainText(stderr)
        return

    def report_state(self, s):
        print("GRRRR state :", s)
        return

    
class MyApp(QApplication):
    def __init__(self, argv):
        QApplication.__init__(self, argv)
        global mw
        mw = MyMW(argv=argv)
        mw.show()
        return

# objet temporaire ; contiendra la fenêtre principale créée par effet de bord
mw = None

# crée mw en tenant compte des arguments
app = MyApp(sys.argv)
app.exec()
