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

from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtGui import QFileSystemModel
from PyQt6.QtCore import QDir, QProcess, QThread

from Ui_importeDeGemasco import Ui_MainWindow

import sys
import os

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()
