#!/usr/bin/python3
 
from PyQt5.QtWidgets import (  QApplication, QProgressDialog, QMainWindow,
        QDialog, QFileDialog,)
from PyQt5.QtGui import ( QStandardItemModel,QStandardItem,   )
from PyQt5 import QtCore  # , Qt, QThread, QObject, pyqtSignal)
import sys
import mageiaSyncUI
import mageiaSyncExt
import mageiaSyncDBprefs
import mageiaSyncDBrename


class prefsDialog(QDialog,mageiaSyncDBprefs.Ui_prefsDialog ):
   
    def __init__(self, parent=None):
        QDialog.__init__(self,parent)
        self.setupUi(self)
        self.selectDest.clicked.connect(isosSync.selectDestination)

class renameDialog(QDialog,mageiaSyncDBrename.Ui_renameDialog ):
    #   Display a dialog box to choose to rename an old collection of ISOs to a new one
   
    def __init__(self, parent=None):
        QDialog.__init__(self,parent)
        self.setupUi(self)
        self.chooseDir.clicked.connect(isosSync.renameDir)


class LogWindow(QProgressDialog):
    #   Display a box at start during the remote directory list loading
   
    def __init__(self, parent=None):
        super(LogWindow, self).__init__(parent)
        self.setWindowModality(QtCore.Qt.WindowModal)
        self.setWindowTitle('Loading...')
        self.setLabelText('Loading images list from repository.')
        self.setMinimum(0)
        self.setMaximum(0)
        self.setAutoReset(False)
        self.setAutoClose(False)
        self.setMinimumDuration(1)
 
    def perform(self):
        self.progressDialog.setValue(self.progress)
 
class dbWarning(QProgressDialog):
    #   Display a box at start during the remote directory list loading
   
    def __init__(self, parent=None):
        super(dbWarning, self).__init__(parent)
        self.setWindowModality(QtCore.Qt.WindowModal)
        self.setWindowTitle('Loading...')
        self.setLabelText('Loading images list from repository.')
        self.setMinimum(0)
        self.setMaximum(0)
        self.setAutoReset(False)
        self.setAutoClose(False)
        self.setMinimumDuration(1)
 
    def perform(self):
        self.progressDialog.setValue(self.progress)
 
class IsosViewer(QMainWindow, mageiaSyncUI.Ui_mainWindow):
    #   Display the main window
    def __init__(self, parent=None):
        super(IsosViewer, self).__init__(parent)
        self.setupUi(self)
        self.connectActions()
        self.IprogressBar.setMinimum(0)
        self.IprogressBar.setMaximum(100)
        self.IprogressBar.setValue(0)
        self.IprogressBar.setEnabled(False)
        self.selectAllState=True
        self.stop.setEnabled(False)
        self.destination=''
        self.rsyncThread = mageiaSyncExt.syncThread(self)    # create a thread to launch rsync
        self.rsyncThread.progressSignal.connect(self.setProgress) 
        self.rsyncThread.speedSignal.connect(self.setSpeed) 
        self.rsyncThread.sizeSignal.connect(self.setSize) 
        self.rsyncThread.remainSignal.connect(self.setRemain) 
        self.rsyncThread.endSignal.connect(self.syncEnd) 
        self.rsyncThread.lvM.connect(self.lvMessage)
        self.rsyncThread.checkSignal.connect(self.checks)
        self.checkThreads=[]    #   A list of thread for each iso

 #      Model for list view in a table       
        self.model = QStandardItemModel(0, 6, self)
        headers=["Directory","Name","Size","Date","SHA1","MD5"]
        i=0
        for label in headers:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal,label )
            i+=1

#       settings for the list view
        self.localList.setModel(self.model)
        self.localList.setColumnWidth(0,220)
        self.localList.setColumnWidth(1,220)
        self.localList.horizontalHeader().setStretchLastSection(True)
        # settings for local iso names management
        self.localListNames=[]

    def multiSelect(self):
    #   allows to select multiple lines in remote ISOs list
        self.listIsos.setSelectionMode(2)
        
    def add(self, iso):
    #   Add an remote ISO in list
         self.listIsos.addItem(iso)

    def localAdd(self, path,iso,isoSize):
        #   Add an entry in local ISOs list, with indications about checking
        itemPath=QStandardItem(path)
        itemIso=QStandardItem(iso)
        if isoSize==0:
            itemSize=QStandardItem('--')
        else:
            formatedSize='{:n}'.format(isoSize) #.replace(","," ")
            itemSize=QStandardItem(formatedSize)
        itemSize.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemDate=QStandardItem("--/--/--")
        itemDate.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemCheck1=QStandardItem("--")
        itemCheck1.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemCheck5=QStandardItem("--")
        itemCheck5.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        self.model.appendRow([itemPath,itemIso,itemSize,itemDate, itemCheck1, itemCheck5,])
        self.localListNames.append([path,iso])
    
    def setProgress(self, value):
        #   Update the progress bar
        self.IprogressBar.setValue(value)

    def setSpeed(self, value):
        #   Update the speed field
        self.speedLCD.display(value)

    def setSize(self, value):
        #   Update the size field
        self.Lsize.setText(str(value)+" bytes")
    
    def setRemain(self,remainTime):
        content=QtCore.QTime.fromString(remainTime,"h:mm:ss")
        self.timeRemaining.setTime(content)
        
    def checks(self,isoIndex):
        #   process a checking for each iso
        #   launches a thread for each iso
        newThread=mageiaSyncExt.checkThread(self)
        self.checkThreads.append(newThread)
        self.checkThreads[-1].setup(self.destination,self.model.data(self.model.index(isoIndex,0)) ,self.model.data(self.model.index(isoIndex,1)),isoIndex)
        self.checkThreads[-1].md5Signal.connect(self.md5Check)
        self.checkThreads[-1].sha1Signal.connect(self.sha1Check)
        self.checkThreads[-1].dateSignal.connect(self.dateCheck)
        self.checkThreads[-1].sizeSignal.connect(self.sizeUpdate)
        self.checkThreads[-1].checkStartSignal.connect(self.checkStart)
        self.checkThreads[-1].start()

    def checkStart(self,isoIndex):
        #   the function indicates that checking is in progress
        #   the hundred contains index of the value to check, the minor value contains the row
        col=(int)(isoIndex/100)
        row=isoIndex-col*100
        self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), "Checking")
        
    def md5Check(self,check):
        if check>=128:
            val="OK"
            row=check-128
        else:
            val="Failed"
            row=check
        self.model.setData(self.model.index(row, 5, QtCore.QModelIndex()), val)

    def sha1Check(self,check):
        if check>=128:
            val="OK"
            row=check-128
        else:
            val="Failed"
            row=check
        self.model.setData(self.model.index(row, 4, QtCore.QModelIndex()), val)
    
    def dateCheck(self,check):
        if check>=128:
            val="OK"
            row=check-128
        else:
            val="Failed"
            row=check
        self.model.setData(self.model.index(row, 3, QtCore.QModelIndex()), val)

    def sizeUpdate(self,signal,isoSize):
        col=(int)(signal/100)
        row=signal-col*100
        self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), '{:n}'.format(isoSize))
        
    def syncEnd(self, rc):
        if rc==1:
            self.lvMessage("Command rsync not found")
        elif rc==2:
            self.lvMessage("Error in rsync parameters")
        elif rc==3:
            self.lvMessage("Unknown error in rsync")
        self.IprogressBar.setEnabled(False)
        self.syncGo.setEnabled(True)
        self.listIsos.setEnabled(True)
        self.selectAll.setEnabled(True)
        self.stop.setEnabled(False)

    def prefsInit(self):
    #   Load the parameters at first
        params=QtCore.QSettings("Mageia","mageiaSync")
        try:
            #   the parameters already initilised?
            self.user=params.value("user", type="QString")
        except:
            # Values are not yet set
            self.pd=prefsDialog()
            #   Set values which are already defined
            try:
                self.pd.release.setText(params.value("release", type="QString"))
            except:
                pass
            try:
                self.pd.password.setText(params.value("password", type="QString"))
            except:
                pass
            try:
                self.pd.location.setText(params.value("location", type="QString"))
            except:
                pass
            try:
                self.pd.selectDest.setText(params.value("destination", type="QString"))
            except:
                self.pd.selectDest.setText(QtCore.QDir.currentPath())
            try:
                self.pd.bwl.setText(params.value("bwl", type="QInt"))
            except:
                pass
            try:
                self.pd.password.setText(params.value("password", type="QString"))
            except:
                pass
            answer=self.pd.exec_()
            if answer:                
                #   Update params
                params=QtCore.QSettings("Mageia","mageiaSync")
                params.setValue("release", self.pd.release.text())
                params.setValue("user",self.pd.user.text())
                params.setValue("password",self.pd.password.text())
                params.setValue("location",self.pd.location.text())
                params.setValue("destination",self.pd.selectDest.text())
                params.setValue("bwl",str(self.pd.bwl.value()))
                self.user=self.pd.user.text()
            else:
                pass
#                answer=QDialogButtonBox(QDialogButtonBox.Ok)
                # the user must set values or default values
            self.pd.close()
        self.release=params.value("release", type="QString")
        self.location=params.value("location", type="QString")
        self.password=params.value("password", type="QString")
        self.destination=params.value("destination", type="QString")
        self.bwl=params.value("bwl",type=int)
        self.localDirLabel.setText("Local directory: "+self.destination)
        if self.location !="":
            self.remoteDirLabel.setText("Remote directory: "+self.location)

    def selectDestination(self):
        #   dialog box to select the destination (local directory)
        directory = QFileDialog.getExistingDirectory(self, 'Select a directory','~/') 
        isosSync.destination = directory   
        self.pd.selectDest.setText(isosSync.destination)

        
    def selectAllIsos(self):
        #   Select or unselect the ISOs in remote list
        if self.selectAllState :
            for i in range(self.listIsos.count()):
                self.listIsos.item(i).setSelected(True)
            self.selectAll.setText("Unselect all")
        else:
            for i in range(self.listIsos.count()):
                self.listIsos.item(i).setSelected(False)
            self.selectAll.setText("Select all")
        self.selectAllState=not self.selectAllState

    def connectActions(self):
        self.actionQuit.triggered.connect(app.quit)
        self.actionRename.triggered.connect(self.rename)
        self.actionUpdate.triggered.connect(self.updateList)
        self.actionPreferences.triggered.connect(self.prefs)
        self.syncGo.clicked.connect(self.launchSync)
        self.selectAll.clicked.connect(self.selectAllIsos)
 
    def updateList(self):
        # From the menu entry
        self.lw = LogWindow()
        self.lw.show()
        self.listIsos.clear()
        self.model.removeRows(0,self.model.rowCount())
        if self.location  == "" :
            self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/'
            print self.nameWithPath
        else:
            self.nameWithPath=self.location+'/'
        self.fillList = mageiaSyncExt.findIsos()
        self.fillList.setup(self.nameWithPath, self.password,self.destination)
        self.fillList.endSignal.connect(self.closeFill)
        self.fillList.start()
        
    def lvMessage( self,message):
        #   Add a line in the logview 
        self.lvText.append(message)
    
    def renameDir(self):
        #   Choose the directory where isos are stored
        directory = QFileDialog.getExistingDirectory(self, 'Select a directory',self.destination) 
        self.rd.chooseDir.setText(directory)

    def rename(self):
        #   rename old isos and directories to a new release
        self.rd=renameDialog()
        loc=[]
        loc=self.location.split('/')
        self.rd.oldRelease.setText(loc[-1])
        self.rd.newRelease.setText(loc[-1])
        self.rd.chooseDir.setText(self.location)
        answer=self.rd.exec_()
        if answer:
            returnMsg=mageiaSyncExt.rename(self.rd.chooseDir.text(),self.rd.oldRelease.text(),self.rd.newRelease.text())
            self.lvMessage(returnMsg)
        self.rd.close()
    
    def prefs(self):
        # From the menu entry
        self.pd=prefsDialog()
        self.pd.release.setText(self.release)
        self.pd.password.setText(self.password)
        self.pd.user.setText(self.user)
        self.pd.location.setText(self.location)
        self.pd.selectDest.setText(self.destination)
        self.pd.bwl.setValue(self.bwl)
        params=QtCore.QSettings("Mageia","mageiaSync")
        answer=self.pd.exec_()
        if answer:
            params.setValue("release", self.pd.release.text())
            params.setValue("user",self.pd.user.text())
            params.setValue("password",self.pd.password.text())
            params.setValue("location",self.pd.location.text())
            params.setValue("destination",self.pd.selectDest.text())
            params.setValue("bwl",str(self.pd.bwl.value()))
        self.prefsInit()
        self.pd.close()
        
    
    def launchSync(self):
        self.IprogressBar.setEnabled(True)
        self.stop.setEnabled(True)
        self.syncGo.setEnabled(False)
        self.listIsos.setEnabled(False)
        self.selectAll.setEnabled(False)
        # Connect the button Stop
        self.stop.clicked.connect(self.stopSync)
        self.rsyncThread.params(self.password, self.bwl)
        for iso in self.listIsos.selectedItems():
            path,name=iso.text().split('/')
            try:
                #   Look for ISO in local list
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            except:
                #   Remote ISO is not yet in local directory. We add it in localList and create the directory
                self.localAdd(path,name,0)
                basedir=QtCore.QDir(self.destination)
                basedir.mkdir(path)
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            row=self.model.indexFromItem(item).row()
            if self.location  == "" :
                self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/'+path
            else:
                self.nameWithPath=self.location+path
            if (not str(path).endswith('/')):
                    self.nameWithPath+='/'
            self.rsyncThread.setup(self.nameWithPath, self.destination+'/'+path+'/',row)
        self.rsyncThread.start()             # start the thread
        # Pour les tests uniquement
            #rsync://$user@bcd.mageia.org/isos/$release/
        #self.nameWithPath='rsync://ftp5.gwdg.de/pub/linux/mageia/iso/4.1/Mageia-4.1-LiveCD-GNOME-en-i586-CD/'

    def closeFill(self,code):
        if code==0: #   list returned
            list=self.fillList.getList()
            for iso in list:
                self.add(iso)
        elif code==1:
            self.lvMessage("Command rsync not found")
        elif code==2:
            self.lvMessage("Error in rsync parameters")
        elif code==3:
            self.lvMessage("Unknown error in rsync")
        list=self.fillList.getList()

        list=self.fillList.getLocal()
        for path,iso,isoSize in list:
            self.localAdd(path,iso, isoSize)
        self.fillList.quit()
        self.lw.hide()
        
    def stopSync(self):
        self.rsyncThread.stop()
        self.IprogressBar.setEnabled(False)
        self.stop.setEnabled(False)
        self.syncGo.setEnabled(True)
        self.listIsos.setEnabled(True)
        self.selectAll.setEnabled(True)


    def main(self):
        self.show()
        #   Load or look for intitial parameters
        self.prefsInit()
        # look for Isos list and add it to the isoSync list. Update preferences
        self.updateList()
        self.multiSelect()
    
    def close(self):
        self.rsyncThread.stop()
        exit(0)
        
     
if __name__=='__main__':
    app = QApplication(sys.argv)
    isosSync = IsosViewer()
    isosSync.main()
    sys.exit(app.exec_())