#!/usr/bin/env python3
import os
import subprocess
import time
import tkinter as tk
from tkinter import messagebox, scrolledtext
from PIL import Image, ImageTk
import os
import sys
import fcntl  # Nur für Linux/Unix-Systeme verfügbar

# --- DOPPELSTART-SCHUTZ ---
# Wir definieren eine versteckte Sperrdatei im Heimatverzeichnis
LOCK_FILE = os.path.expanduser("~/.dxo_workflow.lock")

try:
    # Datei öffnen (oder erstellen, falls nicht vorhanden)
    lock_f = open(LOCK_FILE, "w")
    # Versuchen, die Datei exklusiv zu sperren (LOCK_EX) ohne zu warten (LOCK_NB)
    fcntl.flock(lock_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    # Wenn die Datei bereits von einer anderen Instanz gesperrt ist, bricht das ab
    print("Skript läuft bereits im Hintergrund! Zweiter Start abgebrochen.")
    sys.exit(0)
# --------------------------


WIN_IP = "xxx.xxx.xxx.xxx"
WIN_MAC = "xx:xx:xx:xx:xx:xx"

MOUNT_POINT = "/home/xxx/mnt/DXO-PC"
FOTOS_EXIST = "/home/xxx/mnt/Fotos/exist.txt"
DXO_EXIST = "/home/xxx/mnt/DXO-PC/exist/exist.txt"
RAW_KOPIE_DIR = "/home/xxx/mnt/Fotos/DT-Import/RAW-Kopie"
DNG_TARGET_DIR = "/home/xxx/mnt/Fotos/DT-Import/DNG"


class FotoWorkflowGUI:

    def __init__(self, root):
        self.root = root
        self.root.title("DxO-PC Foto-Transfer Manager")
        self.root.geometry("950x600")
        self.root.resizable(False, False)

        # Abfangen des Schließen-Kreuzes (X)
        self.root.protocol("WM_DELETE_WINDOW", self.unmount_and_exit)

        # Hintergrundbild laden
        script_dir = os.path.dirname(os.path.abspath(__file__))
        bg_path = os.path.join(script_dir, "background.png")

        if os.path.exists(bg_path):
            img = Image.open(bg_path)
            img = img.resize((950, 600), Image.Resampling.LANCZOS)
            self.bg_image = ImageTk.PhotoImage(img)
            bg_label = tk.Label(self.root, image=self.bg_image)
            bg_label.place(x=0, y=0, relwidth=1, relheight=1)
        else:
            self.root.configure(bg="#181818")

        # Layout-Spalten definieren
        self.root.grid_columnconfigure(0, minsize=430)
        self.root.grid_columnconfigure(1, weight=1)

        # Titel
        title_label = tk.Label(
            root,
            text="DxO-PC Workflow Steuerung",
            font=("Helvetica", 16, "bold"),
            fg="#ffffff",
            bg="#181818",
        )
        title_label.grid(row=0, column=1, sticky="w", padx=20, pady=(30, 15))

        # Button-Frame
        btn_frame = tk.Frame(root, bg="#181818", bd=0)
        btn_frame.grid(row=1, column=1, sticky="ew", padx=20, pady=5)

        # Buttons
        self.btn1 = tk.Button(
            btn_frame,
            text="1) Austausch-RAW senden & XMP löschen",
            command=self.send_raws,
            bg="#2c3e50",
            fg="white",
            font=("Helvetica", 10, "bold"),
            height=2,
            relief=tk.FLAT,
        )
        self.btn1.pack(fill=tk.X, pady=4)

        self.btn2 = tk.Button(
            btn_frame,
            text="2) DNG vom DxO-PC holen & RAWs dort löschen",
            command=self.get_dngs,
            bg="#117a65",
            fg="white",
            font=("Helvetica", 10, "bold"),
            height=2,
            relief=tk.FLAT,
        )
        self.btn2.pack(fill=tk.X, pady=4)

        self.btn3 = tk.Button(
            btn_frame,
            text="3) DxO-PC unmounten, schlafen legen & Beenden",
            command=self.unmount_and_exit,
            bg="#922b21",
            fg="white",
            font=("Helvetica", 10, "bold"),
            height=2,
            relief=tk.FLAT,
        )
        self.btn3.pack(fill=tk.X, pady=4)

        # Status & Log-Bereich
        log_label = tk.Label(
            root,
            text="Live-Statusausgabe:",
            font=("Helvetica", 10, "bold"),
            fg="#aaaaaa",
            bg="#181818",
        )
        log_label.grid(row=2, column=1, sticky="w", padx=20, pady=(15, 2))

        self.log_area = scrolledtext.ScrolledText(
            root,
            width=55,
            height=12,
            bg="#0d0d0d",
            fg="#00ff00",
            insertbackground="white",
            font=("Courier", 10),
            bd=0,
        )
        self.log_area.grid(row=3, column=1, sticky="nsew", padx=20, pady=(0, 30))

        # HIER NEU: Automatischer Start-Ablauf beim Öffnen der GUI
        self.root.after(100, self.auto_start_sequence)

    def log(self, text):
        self.log_area.insert(tk.END, text + "\n")
        self.log_area.see(tk.END)
        self.root.update_idletasks()

    def auto_start_sequence(self):
        """Weckt den PC bei Bedarf auf und mountet ihn anschließend."""
        self.log("=== START-SEQUENCE INITIERT ===")

        # Erst prüfen, ob er eh schon online und gemountet ist
        ret = subprocess.call(["mountpoint", "-q", MOUNT_POINT])
        if ret == 0:
            self.log("--> DxO-PC ist bereits online und gemountet. Bereit!")
            return

        # Wenn nicht gemountet, schicke das Weck-Signal (WOL)
        self.log(f"Sende Wake-on-LAN 'Magic Packet' an {WIN_MAC}...")
        try:
            # etherwake benötigt unter Ubuntu sudo, wakeonlan nicht.
            # Wir nutzen das flexiblere 'wakeonlan' Paket falls installiert,
            # oder senden es über die Shell.
            subprocess.run(["wakeonlan", WIN_MAC], capture_output=True, text=True)
            # Fallback falls wakeonlan nicht da ist, versuchen wir etherwake:
            # subprocess.run(["sudo", "etherwake", WIN_MAC])
        except Exception as e:
            self.log(f"WOL-Signal konnte nicht gesendet werden: {str(e)}")

        self.log("Warte auf Rückmeldung vom DxO-PC (Ping)...")

        # Max. 30 Sekunden auf den PC warten (er wacht ja aus dem Ruhezustand auf, das geht fix)
        pc_online = False
        for i in range(1, 31):
            self.log(f"  Versuch {i}/30: Pinge {WIN_IP}...")
            ping_ret = subprocess.call(["ping", "-c", "1", "-W", "1", WIN_IP], stdout=subprocess.DEVNULL)

            if ping_ret == 0:
                self.log("--> DxO-PC antwortet! Er ist online.")
                pc_online = True
                break
            time.sleep(1)

        if pc_online:
            # Kurz 2 Sekunden extra schlafen, damit der Windows-Samba-Dienst nach dem Aufwachen bereit ist
            time.sleep(2)
            self.check_and_mount()
        else:
            self.log("\n[WARNUNG] DxO-PC hat nicht rechtzeitig geantwortet.")
            self.log("Bitte prüfe, ob er Strom hat, oder mounte ihn gleich manuell.")

    def check_and_mount(self):
        self.log("Mounte den DXO PC...")
        try:
            res = subprocess.run(["mount", MOUNT_POINT], capture_output=True, text=True)
            if res.returncode == 0:
                self.log("--> DXO-PC erfolgreich im System eingebunden. Bereit für Aktion 1!")
            else:
                self.log(f"ERROR: DXO-PC konnte nicht gemountet werden!\n{res.stderr}")
        except Exception as e:
            self.log(f"Fehler beim Mounten: {str(e)}\n")



    def check_exists(self):
        return os.path.exists(FOTOS_EXIST) and os.path.exists(DXO_EXIST)

    def send_raws(self):
        self.log("\n=== AKTION 1: RAWs senden & XMPs löschen ===")
        if not self.check_exists():
            self.log(
                "ERROR: Verschieben klappt nicht. Prüfe ob externe Platte und DXO-PC gemounted sind!"
            )
            messagebox.showerror(
                "Fehler", "Externe Platte oder DXO-PC nicht bereit!"
            )
            return

        self.log("Lösche alle *.xmp im RAW-Kopie-Ordner...")
        cmd_rm = f"rm -v {RAW_KOPIE_DIR}/*.xmp"
        res_rm = subprocess.run(cmd_rm, shell=True, capture_output=True, text=True)
        if res_rm.stdout:
            self.log(res_rm.stdout)

        self.log("Verschiebe die RAWs auf den DXO-PC...")
        cmd_mv = f"mv -v {RAW_KOPIE_DIR}/*.* {MOUNT_POINT}/"
        res_mv = subprocess.run(cmd_mv, shell=True, capture_output=True, text=True)

        if res_mv.stdout:
            self.log(res_mv.stdout)

        if res_mv.returncode != 0:
            self.log(":( RAW-Kopien verschieben klappt nicht (Fehler aufgetreten)")
            if res_mv.stderr:
                self.log(res_mv.stderr)
        else:
            self.log(":) RAW-Kopien erfolgreich auf DXO-PC verschoben.")

    def get_dngs(self):
        self.log("\n=== AKTION 2: DNGs holen & RAWs auf DxO-PC löschen ===")
        if not self.check_exists():
            self.log(
                "ERROR: Verschieben klappt nicht. Prüfe ob externe Platte und DXO-PC gemounted sind!"
            )
            messagebox.showerror(
                "Fehler", "Externe Platte oder DXO-PC nicht bereit!"
            )
            return

        self.log("Verschiebe DNG-Dateien vom DXO-PC auf die externe Platte...")
        cmd_mv = f"mv -v {MOUNT_POINT}/DNG/*.dng {DNG_TARGET_DIR}/"
        res_mv = subprocess.run(cmd_mv, shell=True, capture_output=True, text=True)

        if res_mv.stdout:
            self.log(res_mv.stdout)

        err_dng = False
        if res_mv.returncode != 0:
            self.log(":( Beim DNG-Dateien verschieben gab es ein Problem.")
            if res_mv.stderr:
                self.log(res_mv.stderr)
            err_dng = True
        else:
            self.log(":) DNG-Dateien auf ext. Platte verschoben.")

        self.log(
            "Lösche die verbleibenden RAW-Kopien (*.CR?) auf dem DXO-PC..."
        )
        cmd_rm = f"rm -v {MOUNT_POINT}/*.CR?"
        res_rm = subprocess.run(cmd_rm, shell=True, capture_output=True, text=True)

        if res_rm.stdout:
            self.log(res_rm.stdout)

        if not err_dng:
            self.log("\n--> Starte Darktable-Import für den DNG-Ordner...")
            try:
                subprocess.Popen(["darktable", DNG_TARGET_DIR])
                self.log(":) Darktable wurde mit dem DNG-Ordner aufgerufen.")
            except Exception as e:
                self.log(
                    f"Fehler: Darktable konnte nicht gestartet werden: {str(e)}"
                )

    def unmount_and_exit(self):
        self.log("\n=== AKTION: Beenden & Aushängen ===")

        cred_path = os.path.expanduser("~/.smbcredentialsdxo")
        win_user = None
        win_pass = None

        if os.path.exists(cred_path):
            try:
                with open(cred_path, "r") as f:
                    for line in f:
                        line = line.strip()
                        if line.startswith("username="):
                            win_user = line.split("=", 1)[1]
                        elif line.startswith("password="):
                            win_pass = line.split("=", 1)[1]
            except Exception as e:
                self.log(
                    f"Warnung: Konnte Credentials-Datei nicht lesen: {str(e)}"
                )

        if os.path.exists(DXO_EXIST):
            answer = messagebox.askyesno(
                "Aushängen?",
                "Der DXO PC ist noch eingehängt. Soll ich das Aushängen vor dem Beenden für Dich übernehmen?",
            )

            if answer:
                res = subprocess.run(
                    ["umount", MOUNT_POINT], capture_output=True, text=True
                )
                if res.returncode == 0:
                    self.log("--> DXO PC wurde erfolgreich ausgehängt.")
                else:
                    messagebox.showerror(
                        "Fehler",
                        f"DXO PC konnte nicht ausgehängt werden!\n{res.stderr}",
                    )
                    return

        pc_aus = messagebox.askyesno(
            "DxO-PC ausschalten?",
            "Soll der DxO-PC (Windows 11) jetzt in den Ruhezustand versetzt werden?",
        )

        if pc_aus:
            if not win_user or not win_pass:
                self.log(
                    "ERROR: Keine Zugangsdaten in .smbcredentialsdxo gefunden! Ruhezustand abgebrochen."
                )
                messagebox.showerror(
                    "Fehler",
                    "Zugangsdaten konnten nicht aus .smbcredentialsdxo geladen werden.",
                )
                return

            self.log(
                f"Sende spezifischen Ruhezustand-Befehl an Windows-PC ({WIN_IP})..."
            )
            try:
                user_auth = f"{win_user}%{win_pass}"
                cmd_suspend = [
                    "net",
                    "rpc",
                    "shutdown",
                    "-I",
                    WIN_IP,
                    "-U",
                    user_auth,
                    "-C",
                    "rundll32.exe powrprof.dll,SetSuspendState 1,0,0",
                ]
                res = subprocess.run(
                    cmd_suspend, capture_output=True, text=True
                )
                if res.returncode == 0:
                    self.log(":) Ruhezustand-Signal an Windows gesendet.")
                    messagebox.showinfo(
                        "Gute Nacht",
                        "Der DxO-PC wird in den Ruhezustand versetzt.",
                    )
                else:
                    self.log(
                        f"Fehler von Windows empfangen:\n{res.stderr}\n{res.stdout}"
                    )
            except Exception as e:
                self.log(
                    f"Fehler: Konnte Windows-PC nicht in den Ruhezustand versetzen: {str(e)}"
                )

        self.log("Skript beendet. Bis zum nächsten Mal!")
        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = FotoWorkflowGUI(root)
    root.mainloop()
