#Imporitert alle Bibliotheken
from pypdf import PdfWriter #pip install pypdf
import re
import os
import sys
import subprocess
import pikepdf #pip install pikepdf
import time
import glob
import locale
from PIL import Image, ImageOps #pip install Pillow
import tkinter as tk
#Um exe zu erstellen: python -m PyInstaller --onefile --icon icon_new.ico CCT.py

locale.setlocale(locale.LC_ALL, "")
language = locale.getlocale() # Gibt die Region des Benutzers zurück
german = ["\nPdf Merge von Nikita Kossack v2\n===================================\n[1] Bilder & PDF's mergen\n[1?] Hilfe zum mergen\n[2] Bild komprimieren\n[2?] Hilfe zum komprimieren\n[3] Kontakt\n===================================\n",'↦ Welches Tool willst du verwenden?: ',"\n↳ Zurückkehren (Ja/Nein): ","JA","Editiere die Reihenfolge der zu mergenden PDF's...","↦ Ordnerpfad der zusammenzufügenden PDF\'s: ","↦ Dateiname der zusammengefügten PDF: ","↦ Bilder wurden gefunden, sollen diese komprimiert werden? (Ja/Nein): ","============================================\nPDF\'s werden zusammengefügt...","Datei wird komprimiert...","============================================\n🟢Fertig, bis zum nächsten mal!🟢","============================================\n🟥Fehlgeschlagen: Nur eine PDF gefunden🟥","============================================\n🟥Fehlgeschlagen: Keine PDF gefunden🟥","============================================\n🟥Fehlgeschlagen: Invalider Dateiname🟥","============================================\n🟥Fehlgeschlagen: Dateipfad existiert nicht🟥","\n===================================\nSchritt 1: Bevor du beginnst, erstelle einen Ordner auf deinem Computer. \nSchritt 2: Bewege alle Bilder und PDF\'s in diesen neu erstellten Ordner, die zusammengefügt werden sollen. Achte darauf, dass alle Datein in diesem Ordner zusammengefügt werden und Altbestände aus früheren merges vorher entfernt werden müssen. In einem seperaten Fenster kann nun die Reihenfolge der Merges der PDF's festgelegt werden. Unterstützte Dateiformate sind PDF, PNG und JPEG. \nSchritt 3: Kopiere den Dateipfad des Ordners (Du siehst ihn wenn du oben beim Ordner in die Leiste klickst) und füge ihn hier im Programm ein. Benenne anschließend deine neu entstehende Datei. Die gemergde Datei ist unter deiner Benennung im Ordner \'Merged_PDF\' aufzufinden.","============================================\n🟥Fehlgeschlagen: Invalider Dateipfad🟥","\n===================================\nFinde das Bild auf, dass du komprimieren möchtest. Rechtsklicke darauf und kopiere den Dateipfad. Gib ihn nun hier im Porgramm ein und das Bild wird zu einer komprimierten PDF konvertiert. Nur JPEG und PNG Dateien werden unterstützt.","\n===================================\nKontakt: https://nikanoku.de/contact.html",'↦ Dateipfad des zu komprimierenden Bild: ',"PDF-Reihenfolge editieren", "Fertig", "============================================\n🟥Fehlgeschlagen: Invalide Eingabe🟥"]
english = ["\nPdf Merge created by Nikita Kossack v2\n===================================\n[1] Merge images & PDF's\n[1?] Help for merging\n[2] Compress image\n[2?] Help for compressing\n[3] Contact\n===================================\n",'↦ Which tool do you want to use?: ',"\n↳ Return (Yes/No): ","YES","Edit the merge order of the PDF's...","↦ Folderpath of the PDF\'s: ","↦ Filename of the merged PDF: ","↦ Images were found, should they be compressed? (Yes/No): ","============================================\nPDF\'s are beeing merged...","File is beeing compressed...","============================================\n🟢Finished, until next time!🟢","============================================\n🟥Failed: Only a singular PDF was found🟥","============================================\n🟥Failed: No PDF was found🟥","============================================\n🟥Failed: Invalid filename🟥","============================================\n🟥Failed: Filepath doesn't exist🟥","\n===================================\nStep 1: Before you start create a folder on your computer. \nStep 2: Move all pictures and PDF\'s, which need to be merged, into the newly created folder. Pay atention that all files in this folder will be merged and files from earlier merges are removed beforehand. The order of the merges of the PDF's can be choosen in a seperate window. Supported fileformats are PDF, PNG and JPEG. \nStep 3: Copy the filepath of the folder (You can see it on top of the file-explorer) and paste it into the program. Finally name the to be generated merged file. The merged file can be found under the given name in the folder \'Merged_PDF\'.","============================================\n🟥Failed: Invalid filepath🟥","\n===================================\nFind the picture that you want to be compressed. Rightclick on it and copy the path. Paste it into the program so that the image can be converted into a PDF. Only JPEG and PNG files are beeing supported.","\n===================================\nContact: https://nikanoku.de/contact.html",'↦ Filepath of the to be comprimised image: ',"Edit PDF-order", "Finished","============================================\n🟥Failed: Invalid input🟥"]
current_language = []
if language[0] == "de_DE" or language[0] == "de_AT": # Hier wird die Lokalization reingeladen (Deutsch für Deutschland und Österreich, sonst Englisch)
    current_language = german
else:
    current_language = english

#Funktion zum Neustart des Skriptes
def Restart(Answer):
    if str(Answer).upper() == current_language[3]:
        skriptPfad = os.path.abspath(__file__)
        subprocess.run([sys.executable] + [skriptPfad] + sys.argv[1:])
#Funktion zum konvertieren von Bilddateien zu PDFs mit Pillow
def image_to_pdf_pillow(input_path, output_path, resolution=300):
    img = Image.open(input_path) #Bild laden
    img = ImageOps.exif_transpose(img) #Bild richtig orientieren
    if img.mode in ("RGBA", "LA"): #Transparenten Hintergrund ersetzen, fallse existent
        background = Image.new("RGB", img.size, (255, 255, 255)) #Weißen Hintergrund generieren
        background.paste(img, mask=img.split()[-1]) #Transparentes Bild auf weißen Hintergrund platzieren
        img = background
    else:
        img = img.convert("RGB") # Direkt zu nicht transparentem Bildformat konvertieren
    img.save(output_path, "PDF", resolution=resolution) # Als PDF speichern
# Funktion zum konvertieren von regulären Bilddateien in effizientere webp Bilddateien
def convert_to_webp(input_path, output_path, quality=80, lossless=False):
    img = Image.open(input_path) # Bild laden
    img = ImageOps.exif_transpose(img) # Bild richtig orientieren
    img.save(output_path, "WEBP", quality=quality, method=6, lossless=lossless) #Bild als WEBP speichern

def RestartPrompt(Message):
    print(Message)
    Restart(input(current_language[2])) # Leitet Antwort an Neustart Funktion weiter
# Checkt ob komprimiert werden soll und sammelt Bilddateien
def compress(Command, images):
    if str(Command).upper() == current_language[3]:
        ID = 0 # Ladebalken
        for img in images:
            ID+=1 # Ladebalken
            Loading_Bar = '_'*(ID-1) + '/' + '|'*(len(images)-ID) # Ladebalken
            sys.stdout.write(f"\rPNG/JPG -> WebP {Loading_Bar}") # Ladebalken
            sys.stdout.flush() # Ladebalken
            convert_to_webp(img, FolderPath + "\\Img_" + str(ID) + ".webp")
        Loading_Bar = '_'*(ID) # Ladebalken
        sys.stdout.write(f"\rPNG/JPG -> WebP {Loading_Bar}") # Ladebalken
        sys.stdout.flush() # Ladebalken
        print('\n')
        webp = glob.glob(str(FolderPath) +"\\*.webp") # Alle webp Datei-pfade werden aufgelistet
        return webp

class DragDropListbox(tk.Listbox): # Drag and Drop Box; Credits: https://stackoverflow.com/questions/14459993/tkinter-listbox-drag-and-drop-with-python
    def __init__(self, master, **kw): # Key-Binding
        kw['selectmode'] = tk.SINGLE # Nur ein Item kann ausgewählt werden
        tk.Listbox.__init__(self, master, kw)
        self.bind('<Button-1>', self.setCurrent)
        self.bind('<B1-Motion>', self.shiftSelection)
        self.curIndex = None

    def setCurrent(self, event):
        self.curIndex = self.nearest(event.y)

    def shiftSelection(self, event): # Bewegen von Listen-Einträgen mit der Maus
        i = self.nearest(event.y)
        if i < self.curIndex:
            x = self.get(i)
            self.delete(i)
            self.insert(i+1, x)
            self.curIndex = i
        elif i > self.curIndex:
            x = self.get(i)
            self.delete(i)
            self.insert(i-1, x)
            self.curIndex = i

# Öffnet ein seperates Fenster in dem die Reihenfolge der PDF's gewählt werden kann
def sortPdfs(pdfs):
    window = tk.Tk() # Erstellen des Fensters
    window.title(current_language[20]) # Titel
    window.geometry("300x500") # Größe
    List = DragDropListbox(window,selectbackground="#0088FF") # Einfügen der Darg and Drop Liste
    for pdf in pdfs: # Einträge werden in die Liste geladen (PDF Namen)
        List.insert(tk.END, os.path.basename(pdf))
    List.pack(fill=tk.BOTH, expand=True) # Liste wird an die Größe des Fenster angepasst

    result = []

    def finish():
        nonlocal result # Variable wird mit der vorher definierten verbunden
        Dateiloc = os.path.dirname(pdfs[0]) # Ordner der Datei bestimmen
        NewPdfOrder = list(List.get(0, tk.END)) # Die vom Nutzer fesgelegte Reihenfolge der PDF's in der Liste
        result = [Dateiloc + '\\' + pdf for pdf in NewPdfOrder] # Der Dateipfad wird wieder an die PDF's angehängt
        window.destroy() # Fenster wird geschlossen

    button = tk.Button(window, text=current_language[21], command=finish, bg="#0088FF", fg="#ffffff", pady=10, font=("Arial", 15, "bold")) # Button wird erstellt
    button.pack(fill=tk.X, pady=0) # Button wird an das Fenster angepasst

    window.attributes("-topmost", True) # Fenster wird beim Start nach ganz oben gepusht 
    window.after(20, lambda: window.attributes("-topmost", False))

    window.mainloop() # Fenster wird aufrecht erhalten

    return result


# Begrüßungsnachricht
print(current_language[0])

Tool = input(current_language[1]) # Eingabefeld Menüauswahl
if Tool == str(1):
    FolderPath = input(current_language[5]) # Eingabefeld [Ordnerpfad]
    if os.path.isdir(str(FolderPath)): # Checkt ob der Dateiordnerpfad valide ist
        Result_Name = input(current_language[6]) # Eingabefeld [Neuer Dateiname]
        if not re.search(r'[<>:"/\\|?*]', Result_Name) and not Result_Name=='': # Checkt ob der Name valide ist
            jpegs = glob.glob(str(FolderPath) +"\\*.jpg") # Alle jpeg Datei-pfade werden aufgelistet
            pngs = glob.glob(str(FolderPath) +"\\*.png") # Alle png Datei-pfade werden aufgelistet
            images = jpegs + pngs # Alle Bild Datei-pfade werden zusammengefasst
            pdfs = glob.glob(str(FolderPath) +"\\*.pdf") # Alle pdf Datei-pfade werden aufgelistet
            if len(images)>0 and len(images) + len(pdfs) > 1: # Es wird geprüft ob Bilder im Merge enthalten sind
                ID = 0 # Ladebalken
                Comp_images = compress(input(current_language[7]), images) # Bilder werden zu WEBP Dateien konvertiert
                image_names = []
                for img in images: 
                    image_names.append(os.path.splitext(os.path.basename(img))[0]) #Bildnamen werden zwischengespeichert
                if Comp_images is not None:
                    for img in images:
                        os.remove(img) # Entfernen der unkomprimierten Bilder
                    images = Comp_images # Falls sich für komprimieren entschieden wird, werden die Bilder ausgetauscht
                for img in images:
                    ID+=1 # Ladebalken
                    Loading_Bar = '_'*(ID-1) + '/' + '|'*(len(images)-ID) # Ladebalken
                    sys.stdout.write(f"\rPNG/JPG -> PDF {Loading_Bar}") # Ladebalken
                    sys.stdout.flush() # Ladebalken
                    image_to_pdf_pillow(img, FolderPath + "\\" + str(image_names[ID-1]) + ".pdf") # Bilder werden in PDFs konvertiert
                    os.remove(img)
                Loading_Bar = '_'*(ID)  # Ladebalken
                sys.stdout.write(f"\rPNG/JPG -> PDF {Loading_Bar}") # Ladebalken
                sys.stdout.flush() # Ladebalken
                print('\n')
                pdfs = glob.glob(str(FolderPath) +"\\*.pdf")
            if len(pdfs) > 1: # Überprüft ob mehr als eine Pdf enthalten sind
                print(current_language[4])
                sorted_pdfs = sortPdfs(pdfs) # Gibt die aktualisierte Reihenfolge der Pdfs zurück
                if len(sorted_pdfs) > 0: # Checkt ob Änderungen vorgenommen/Das Fenster artgerecht geschlossen wurde
                    pdfs = sorted_pdfs
                print(current_language[8])
                time.sleep(0.2) # Sichert ab, dass die Nachricht übermittelt wird, wegen lag
                merger = PdfWriter() # Merger wird geladen
                for pdf in pdfs: # PDFs werden zusammengefügt
                    merger.append(pdf)
                    os.remove(pdf)
                os.makedirs(os.path.join(str(FolderPath), "Merged_PDF"), exist_ok=True) #Erstellt den Ordner fuer das Ergebnis
                merger.write(str(FolderPath) + "\\Merged_PDF\\"+ str(Result_Name) + ".pdf") #Die zusammengefügte PDF wird gespeichert
                merger.close() # Merge-prozess wird beendet
                print(current_language[9])
                time.sleep(0.2) # Sichert ab, dass die Nachricht übermittelt wird, wegen lag
                input_pdf = str(FolderPath) + "\\Merged_PDF\\"+ str(Result_Name) + ".pdf" # Dateipfade defenieren
                output_pdf = str(FolderPath) + "\\Merged_PDF\\"+ str(Result_Name) + ".pdf"
                with pikepdf.open(input_pdf, allow_overwriting_input=True) as pdf: #Optimierung der Zusammengefügten PDF durch pikepdf
                    pdf.save(output_pdf) # PDF nochmals speichern
                RestartPrompt(current_language[10])
            elif len(pdfs) == 1:
                RestartPrompt(current_language[11])
            else:
                RestartPrompt(current_language[12])
        else:
            RestartPrompt(current_language[13])
    else:
        RestartPrompt(current_language[14])
elif Tool == str(1) + '?':
    RestartPrompt(current_language[15])
elif Tool == str(2):
    Dateipfad = input(current_language[19]).replace("\"", "") # Dateipfad Eingabe
    Dateiloc = os.path.dirname(Dateipfad) # Ordner der Datei bestimmen
    Dateiname = os.path.splitext(os.path.basename(Dateipfad))[0] # Aus dem Pfad wird der Dateiname extrahiert
    if Dateipfad.lower().endswith((".jpg", ".jpeg", ".png")): # Überprüfen ob es sich um eine png oder jpeg Datei handelt
        convert_to_webp(Dateipfad,Dateiloc+'\\ZK.webp') # Zu Webp konvertieren
        image_to_pdf_pillow(Dateiloc+'\\ZK.webp',Dateiloc+'\\'+ Dateiname +'.pdf') # Zu PDF konvertieren
        os.remove(Dateiloc+'\\ZK.webp') # Webp Datei wieder entfernen
        RestartPrompt(current_language[10])
    else:
        RestartPrompt(current_language[16])
elif Tool == str(2) + '?':
    RestartPrompt(current_language[17])
elif Tool == str(3):
    RestartPrompt(current_language[18])
else:
    RestartPrompt(current_language[22])