'''
Created on 2 nov 2019

@author: Emanuele Trabattoni
'''
from PyQt5.QtCore import QObject, pyqtSignal

from slugify import slugify
import time, parse, re, copy, os, json
import traceback

class bananaSPLITTER(QObject):
    sendStatus = pyqtSignal(str)    
    def __init__(self, fileParams=None, logger=None):
        try:
            QObject.__init__(self)
            self.fileParams = fileParams
            self.log = logger
            self.rawFile = None
            self.status = "first"
            self.contentList = list()
            self.bodyCounter=0
            self.duplicateNumber=0
            #self.log.debug(f"Configurazione: \n {json.dumps(fileParams, indent=2)}")
            if fileParams is not None:
                self.log.debug(self.tr("Sto operando sul file: {}..").format(self.fileParams['name']))
                self.paths = self.fileParams['paths']
                self.docStruct = self.fileParams['docStruct']
                self.settings = self.fileParams['settings']
                self.fileName = self.fileParams['name']
                self.outPath = self.paths['OUTworkPath']+slugify(self.fileName)+'/'
                self.beginTime = time.time()
                pass
            else:
                self.log.critical(self.tr("Non e' stato fornito il nome di alcun file da splittare!"))
        except Exception as e:
            print(f"libSplit error: {e}")
        pass
    
    def run(self):
        self.log.info(self.tr("Nuovo SPLITTER  su file: {}").format(self.fileName))
        self.sendStatus.emit(self.tr(f"Inizio {self.fileName}"))
        try:
            self.openFile()
            self.remEmptyLines()
            if self.docStruct['fileVersNew']:
                self.newSplitFile()
            else:
                self.splitFile()
            
            if self.settings['removeDuplicates']:
                self.log.info(self.tr("Controllo se ci sono dei duplicati.."))
                self.removeDuplicates()
            else:
                for idx, ff in enumerate(self.contentList):
                    ff['duplicate']=False
                    self.contentList[idx]=ff
                self.log.warn(self.tr("Salto il controllo dei duplicati.."))
            # se il parse e la rimozione dei duplicati e' andata bene
            # preparo e inizio il salvataggio
            if os.path.exists(self.outPath):
                if self.settings['removeOldFiles']:
                    #os.chdir(self.outPath)
                    for f in os.listdir(self.outPath):
                        os.remove(self.outPath+f)
                else:
                    raise FileExistsError(self.tr("Non posso sovrascrivere i vecchi file, eliminali manualmente!"))
            else:
                os.mkdir(self.outPath)
                #os.chdir(self.outPath)
            if self.settings['saveSeparateFiles']:
                self.saveSeparate()
            if self.settings['saveBodyFile']:
                self.saveBody()
            self.log.info(self.tr("L'elaborazione del file ha richiesto {:4.2f} sec").format(time.time()-self.beginTime))
        
        except UnicodeDecodeError as ee:
            self.log.critical(self.tr("Il file [{}] contiene caratteri non compatibili con la codifica scelta! [{}]")
                            .format(self.fileParams['name'],ee))
        except FileExistsError as fe:
            self.log.critical(fe)
        except Exception as ee:
            traceback.print_exc()
            self.log.warn(ee)
        pass
        
    def openFile(self):
        try:
            #os.chdir(self.paths["INworkPath"])
            self.log.info(self.tr("Carico il contenuto..")) 
            fp = open(self.paths["INworkPath"]+'/'+self.fileParams['name'], mode='r', encoding=self.settings['encoding'])
            self.rawFile = fp.readlines()
            fp.close()
        except IOError as e:
            self.log.critical(self.tr("Impossibile aprire il file: {}! [{}]").format(self.fileName,e))
            raise BaseException("OpenFile") 	
            os.rmdir(self.outPath)
        except Exception as ee:
            print(ee)
        pass

    def remEmptyLines(self):
        self.log.info(self.tr("Elimino righe vuote e caratteri inutili.."))
        tempContent = []
        try:
            for ll in self.rawFile:
                for c in self.settings['delChars']:
                    ll = ll.replace(c,'')
                if ll not in ['\n', '\r', '\r\n']:
                    tempContent.append(ll)
            self.rawFile = copy.deepcopy(tempContent)
            self.rawFile.append('\n')  #linea vuota finale per essere sicuri di parsare bene
            return True
        except:
            self.log.error(self.tr("Errore inaspettato durante l'eliminazione delle righe vuote!"))
            raise BaseException("DelLines")
        del tempContent

    def newSplitFile(self):  #porting del codice dal programma originale
        self.log.info(self.tr("Individuo il contenuto.."))
        self.bodyCounter=0
        self.duplicateNumber=0
        docNumber = 0
        docSkipped = 0
        docDate = {}
        title = ''
        prevLine = ''
        newsPaperName = ''
        titleBegin = True
        newsName = False
        tempBody = list()
        
        for l in self.rawFile: #per ogni linea del file
            l=l.replace('\xa0', ' ')
            lineWords = l.lstrip().split(' ') #dividi la riga in parole
            if self.status == 'first':
                try:	
                    # ricerco la data
                    if (lineWords[self.settings['monthPosition']]).capitalize() in self.docStruct['language']['dateWords']:
                        try:
                            docDate=parse.parse(self.docStruct['dateFormat'],l).named
                            docDate['month']=docDate['month'].lstrip().rstrip().capitalize()
                            docDate['month']=self.docStruct['language']['dateWords'].index(docDate['month'])+1
                            # dopo la data inizia il titolo, ma prima si cerca il nome del giornale
                        except:
                            self.log.warn(self.tr("Ho trovato una riga ambigua.. potrebbe essere una data ma non so: [{}]"). format(l.strip('\r\n')))
                            pass
                    elif lineWords[0].upper() in self.docStruct['language']['headWords']:
                        #cambio stato e inizializzo un nuovo documento da riempire
                        self.status = 'head'
                        newDoc=dict()
                        newDoc['title']=title
                        newDoc['date']=docDate
                        newDoc['newsPaperName'] = newsPaperName
                        titleBegin = False
                        newsName = False
                    else:
                        if titleBegin:
                            title = l.strip().capitalize()
                            titleBegin = False
                            newsName = True
                        elif newsName:
                            if self.settings['getNewsPaperName']:
                                try:
                                    if l.strip():
                                        newsPaperName = l.strip()
                                    else:
                                        newsPaperName = self.settings['nameNotFoundStr']
                                except:
                                    self.log.warn(self.tr("E' successo qualcosa mentre stavo cercando il nome della pubblicazione,\
                                    controlla i file di uscita! [{}]").format(l.strip()))
                            else:
                                newsPaperName = self.settings['nameNotFoundStr']
                            newsName = False
                except IndexError:
                    self.log.error(self.tr("Errore inaspettato, contatta il tuo sviluppatore di fiducia!"))
                    pass
            elif self.status == 'head':
                tempContent = list()
                #doppio check per trovare l'inizio del corpo documento
                if re.match(self.docStruct['beginOfDocument'],l):
                    self.status='body' 
                #if lineWords[0].upper() not in self.docStruct['language']['headWords']:  #se la prima parola non e' tra quelle di inizio	
                    #tempBody.append(l)					   # vuol dire che ho trovato l'articolo e aggiungo la prima riga al contenuto del documento
                    #self.status = 'body'
            elif self.status == 'body':
                if re.match(self.docStruct['endOfDocument'],l) is not None: #controlla se ci sono articoli che non hanno le parole chiave finali 
                    self.log.warn(self.tr("Ho individuato una separatore valido prima che si chiusesse l'articolo precedente, controlla i tuoi file in uscita!\n\
            L'errore dovrebbe essere intorno all'articolo {} ma non sono sicuro! \n\t\t\t[{}]").format(docNumber, l.strip()))
                    self.status = 'tail' 
                    anomaly = True
                elif not lineWords[0].upper() in self.docStruct['language']['tailWords']: #se la prima parola non e' tra quelle di fine
                    if self.settings['delLF']:
                        tempBody.append(l.strip('\n'))		   #allora sto leggendo l'articolo
                    else:
                        tempBody.append(l)
                else:
                    self.status = 'tail'
                    anomaly = False
                
                pass
            elif self.status == 'tail':
                if anomaly or (re.match(self.docStruct['endOfDocument'],l) is not None):
                    self.status = 'first'
                    anomaly = False
                    titleBegin = True
                    if self.settings['delWordBreak']:
                        tempContent=[ll.replace('-\n', '') for ll in tempContent]
                    newDoc['content']=copy.deepcopy(''.join(tempBody))
                    self.contentList.append(copy.deepcopy(newDoc))
                    tempBody=list()
                    self.bodyCounter +=1
                pass
            else:
                self.log.critical(self.tr("Stato Interno Sconosciuto"))
            prevLine=l #salva sempre e comunque il contenuto della linea precedente
            pass
        #ricerca terminata, espongo i risultati
        self.log.info(self.tr("Nel file ho trovato {0} articoli..").format(self.bodyCounter))
        if docSkipped > 0:
            self.log.warn(self.tr("Attentione, LexisNexis ne ha saltati {} !!!").format(docSkipped))
            pass
        pass
    
    def splitFile(self):  #porting del codice dal programma originale
        self.log.info(self.tr("Individuo il contenuto.."))
        self.bodyCounter=0
        self.duplicateNumber=0
        docNumber = 0
        docSkipped = 0
        docDate = {}
        title = ''
        prevLine = ''
        newsPaperName = ''
        titleBegin = False
        tempBody = list()
        docSep=re.compile(self.docStruct['docSep'])
        
        for l in self.rawFile: #per ogni linea del file
            lineWords = l.lstrip().split(' ') #dividi la riga in parole
            if self.status == 'first':
                #prendo il numero di documento per vedere se ci sono buchi
                try:	
                    try:
                        nn = parse.parse("{current:d} Of {total} Documents",l.strip().capitalize()).named
                        if nn["current"]-docNumber==1:
                            pass
                        else:
                            if self.settings["showSkipped"]:
                                self.log.warn(self.tr("Il conto dei documenti non torna! LexisNexis ne ha saltato qualcuno!\n Precedente:{0}-Attuale:{1}").format(docNumber,nn["current"]))
                            docSkipped+=1
                        docNumber = nn["current"]
                    except:
                        pass #non segnalare eccezione se il parse fallisce
                    # ricerco la data
                    if (lineWords[self.settings['monthPosition']]).capitalize() in self.docStruct['language']['dateWords']:
                        try:
                            docDate=parse.parse(self.docStruct['dateFormat'],l).named
                            docDate['month']=docDate['month'].lstrip().rstrip().capitalize()
                            docDate['month']=self.docStruct['language']['dateWords'].index(docDate['month'])+1
                            title = ''
                            titleBegin=True
                            # dopo la data inizia il titolo, ma prima si cerca il nome del giornale
                            if self.settings['getNewsPaperName']:
                                try:
                                    if prevLine.split(' ')[0].strip().isalpha():
                                        newsPaperName = prevLine.strip()
                                    else:
                                        newsPaperName = self.settings['nameNotFoundStr']
                                except:
                                    self.log.warn(self.tr("E' successo qualcosa mentre stavo cercando il nome della pubblicazione,\
                                    controlla i file di uscita! [{}]").format(prevLine.strip()))
                            else:
                                newsPaperName = self.settings['nameNotFoundStr']
                        except:
                            self.log.warn(self.tr("Ho trovato una riga ambigua.. potrebbe essere una data ma non so: [{}]").format(l.strip('\r\n')))
                            pass
                    elif lineWords[0] in self.docStruct['language']['headWords']:
                        #cambio stato e inizializzo un nuovo documento da riempire
                        self.status = 'head'
                        newDoc=dict()
                        newDoc['title']=title
                        newDoc['date']=docDate
                        newDoc['newsPaperName'] = newsPaperName
                        titleBegin=False
                    else:
                        if titleBegin:
                            title += l.strip().capitalize()
                except IndexError:
                    self.log.error(self.tr("Errore inaspettato, contatta il tuo sviluppatore di fiducia!"))
                    pass
            elif self.status == 'head':
                tempContent = list()
                if lineWords[0] not in self.docStruct['language']['headWords']:  #se la prima parola non e' tra quelle di inizio	
                    tempBody.append(l)					   # vuol dire che ho trovato l'articolo e aggiungo la prima riga al contenuto del documento
                    self.status = 'body'	 
                    pass
            elif self.status == 'body':
                if not lineWords[0] in self.docStruct['language']['tailWords']: #se la prima parola non e' tra quelle di fine
                    if self.settings['delLF']:
                        tempBody.append(l.strip('\n'))		   #allora sto leggendo l'articolo
                    else:
                        tempBody.append(l)
                else:
                    self.status = 'tail'
                    anomaly = False
                if docSep.match(l) is not None: #controlla se ci sono articoli che non hanno le parole chiave finali 
                    self.log.warn(self.tr("Ho individuato una separatore valido prima che si chiusesse l'articolo precedente, controlla i tuoi file in uscita!\n\
                    L'errore dovrebbe essere intorno all'articolo {} ma non sono sicuro! \n\t\t[{}]").format(docNumber, l.strip()))
                    self.status = 'tail' 
                    anomaly = True
                pass
            elif self.status == 'tail':
                if docSep.match(l) is not None or anomaly:
                    self.status = 'first'
                    anomaly = False
                    if self.settings['delWordBreak']:
                        tempContent=[ll.replace('-\n', '') for ll in tempContent]
                    newDoc['content']=copy.deepcopy(''.join(tempBody))
                    self.contentList.append(copy.deepcopy(newDoc))
                    tempBody=list()
                    self.bodyCounter +=1
                pass
            else:
                self.log.critical(self.tr("Stato Interno Sconosciuto"))
            prevLine=l #salva sempre e comunque il contenuto della linea precedente
            pass
        #ricerca terminata, espongo i risultati
        self.log.info(self.tr("Nel file ho trovato {0} articoli..").format(self.bodyCounter))
        if docSkipped > 0:
            self.log.warn(self.tr("Attentione, LexisNexis ne ha saltati {} !!!").format(docSkipped))
            pass
        pass

    def removeDuplicates(self):
        titleList=[]
        duplicateList=[]
        for idx, ff in enumerate(self.contentList):
            if ff['title'] not in titleList:
                titleList.append(ff['title'])
                ff['duplicate']=False
                self.contentList[idx]=ff
                pass
            else:
                if ff['title'] not in duplicateList:
                    duplicateList.append(ff['title'])
                    if self.settings['showRemovedDuplicates']:
                        self.log.info(self.tr("Duplicato: {}").format(ff['title'].strip()))
                ff['duplicate'] = True
                self.contentList[idx]=ff
                self.duplicateNumber+=1
        self.log.info(self.tr("Ho rimosso {} duplicati di {} articoli..").format(self.duplicateNumber, len(duplicateList)))
        pass
        
    def saveSeparate(self):
        outFileCounter = 0
        self.paths['OUTworkPath']=self.paths['OUTworkPath']+slugify(self.fileName)
        self.log.info(self.tr("Salvo gli articoli in file separati..."))
        self.log.debug(self.tr("Persorso: {0}").format(self.outPath))
        for ff in self.contentList:
            try:
                if ff['duplicate'] == False:
                    fName=self.docStruct['outNameFormat'].format(title=slugify(ff['title'][:self.docStruct['maxTitleLen']]),\
                                                         filename=slugify(self.fileName),\
                                                         docnum=outFileCounter,\
                                                         papername=ff['newsPaperName'].strip(),\
                                                         **ff['date'])
                    out=open(self.outPath+'{0}'.format(fName),'wb')
                    if self.settings['includeTitle']:
                        ff['content'] = ff['title']+os.linesep+ff['content']
                    out.write(ff['content'].encode(self.settings['encoding']))
                    out.close()
                    outFileCounter+=1
            except IOError as e:
                self.log.error(self.tr("Qualcosa e\' andato storto, non riesco a scrivere il file: {}").format(e))
                continue
            except KeyError as ke:
                self.log.error(self.tr(f"Chiave {ke} non trovata per:{outFileCounter} {ff['title']} "))
            except Exception as ee:
                traceback.print_exc()
                self.log.error(self.tr(f"Errore generale nel salvataggio: {ee}"))
        if outFileCounter < self.bodyCounter:
            self.log.error(self.tr("Ho salvato meno file rispetto a quelli trovati!"))
        pass
    
    def saveBody(self):
        self.log.info(self.tr('Salvo gli articoli in un singolo file vicino agli originali...'))
        self.log.debug(self.tr('Persorso: {0}').format(self.outPath))
        #os.chdir(self.outPath)
        try:
            fName=slugify(self.fileName)
            fName='BODYFILE_{0}.txt'.format(fName[:self.docStruct['maxTitleLen']])
            fileContent = os.linesep.join([cc['content'] for cc in self.contentList])
            out=open(self.outPath+'{0}'.format(fName),'wb')
            out.write(fileContent.encode(self.settings['encoding']))
            out.close()
        except IOError as e:
            self.log.error(self.tr("Qualcosa e\' andato storto, non riesco a scrivere il file: {}").format(e))
        except Exception as ee:
                self.log.error(self.tr(f"Errore generale nel salvataggio: {ee}"))
        pass

    
        