import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import subprocess
import threading
import os
import datetime

class ColorProfileGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Patch generator")

        # zmienne
        self.patch_count = tk.IntVar(value=228)
        self.grey_steps = tk.IntVar(value=10)
        self.page_size = tk.StringVar(value="A4")
        self.split_pages = tk.BooleanVar(value=False)
        self.patch_size = tk.DoubleVar(value=10.0)  # mm
        self.resolution = tk.IntVar(value=360)

        self.spacing_mode = tk.StringVar(value="B&W")  # domyślnie B&W spacers
        self.randomize = tk.BooleanVar(value=True)    # domyślnie randomize = True
        self.instrument = tk.StringVar(value="i1Pro/CR30")
        self.basename = tk.StringVar(value="Tutorial")

        # interfejs
        self.create_widgets()

    def create_widgets(self):
        frame = ttk.Frame(self.root, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        # Patch count
        ttk.Label(frame, text="Patch count:").grid(row=0, column=0, sticky=tk.W)
        ttk.Entry(frame, textvariable=self.patch_count, width=10).grid(row=0, column=1, sticky=tk.W)

        # Grey steps
        ttk.Label(frame, text="Grey steps:").grid(row=1, column=0, sticky=tk.W)
        grey_entry = ttk.Entry(frame, textvariable=self.grey_steps, width=10)
        grey_entry.grid(row=1, column=1, sticky=tk.W)
        grey_entry.tooltip = self.create_tooltip(grey_entry, "Number of composite grey steps (default 10)")

        # Patch size
        ttk.Label(frame, text="Patch size (mm):").grid(row=2, column=0, sticky=tk.W)
        patch_entry = ttk.Entry(frame, textvariable=self.patch_size, width=10)
        patch_entry.grid(row=2, column=1, sticky=tk.W)

        # Resolution
        ttk.Label(frame, text="Resolution (DPI):").grid(row=3, column=0, sticky=tk.W)
        res_entry = ttk.Entry(frame, textvariable=self.resolution, width=10)
        res_entry.grid(row=3, column=1, sticky=tk.W)

        # Page size
        ttk.Label(frame, text="Page size:").grid(row=4, column=0, sticky=tk.W)
        page_combo = ttk.Combobox(frame, textvariable=self.page_size,
                                  values=["A4", "A3", "A2", "Letter", "Legal", "4x6", "11x17"])
        page_combo.grid(row=4, column=1, sticky=tk.W)

        # Split pages
        split_chk = ttk.Checkbutton(frame, text="Split into 2 pages", variable=self.split_pages)
        split_chk.grid(row=5, column=0, columnspan=2, sticky=tk.W)
        self.create_tooltip(split_chk, "Split chart into two pages (default: unchecked)")

        # Spacing mode
        ttk.Label(frame, text="Spacing:").grid(row=6, column=0, sticky=tk.W)
        spacing_combo = ttk.Combobox(frame, textvariable=self.spacing_mode, values=["NONE", "B&W", "Color"])
        spacing_combo.grid(row=6, column=1, sticky=tk.W)

        # Randomize
        random_chk = ttk.Checkbutton(frame, text="Disable randomize", variable=self.randomize,
                                     onvalue=False, offvalue=True)
        random_chk.grid(row=7, column=0, columnspan=2, sticky=tk.W)
        self.create_tooltip(random_chk, "Disable randomization of patch order (default: randomize ON)")

        # Instrument
        ttk.Label(frame, text="Instrument:").grid(row=8, column=0, sticky=tk.W)
        inst_combo = ttk.Combobox(frame, textvariable=self.instrument,
                                  values=["i1Pro/CR30", "i1Pro2", "i1Pro3+", "ColorMunki", "DTP20", "DTP22", "DTP41", "DTP51", "SpectroScan"])
        inst_combo.grid(row=8, column=1, sticky=tk.W)

        # Basename
        ttk.Label(frame, text="Basename:").grid(row=9, column=0, sticky=tk.W)
        ttk.Entry(frame, textvariable=self.basename, width=20).grid(row=9, column=1, sticky=tk.W)

        # Buttons
        ttk.Button(frame, text="Generate Patch File", command=self.generate_patch).grid(row=10, column=0, pady=5)
        ttk.Button(frame, text="Scan Generated Patch", command=self.scan_patch).grid(row=10, column=1, pady=5)

        # Log box
        self.log_text = tk.Text(frame, height=20, width=80)
        self.log_text.grid(row=11, column=0, columnspan=2, pady=5)

    def create_tooltip(self, widget, text):
        tooltip = tk.Toplevel(widget, bg="yellow", padx=1, pady=1)
        tooltip.wm_overrideredirect(True)
        tooltip.withdraw()
        label = tk.Label(tooltip, text=text, bg="yellow", justify=tk.LEFT, relief=tk.SOLID, borderwidth=1)
        label.pack()
        def enter(event): tooltip.deiconify(); tooltip.wm_geometry(f"+{event.x_root+10}+{event.y_root+10}")
        def leave(event): tooltip.withdraw()
        widget.bind("<Enter>", enter)
        widget.bind("<Leave>", leave)
        return tooltip

    def log(self, message):
        timestamp = datetime.datetime.now().strftime("[%H:%M:%S] ")
        self.log_text.insert(tk.END, timestamp + message + "\n")
        self.log_text.see(tk.END)
        with open(f"{self.basename.get()}_process.log", "a", encoding="utf-8") as f:
            f.write(timestamp + message + "\n")

    def run_command(self, cmd):
        self.log("Running: " + " ".join(cmd))
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
        for line in process.stdout:
            self.log(line.strip())
        process.wait()

    def generate_patch(self):
        def task():
            basename = self.basename.get()
            patches_total = self.patch_count.get()
            grey = self.grey_steps.get()

            # liczba do targen musi być pomniejszona o grey steps
            targen_patches = patches_total - grey
            if targen_patches <= 0:
                self.log("Error: too many grey steps for selected patch count.")
                return

            # targen
            targen_cmd = ["targen", "-v", "-d2", "-g", str(grey), "-f", str(targen_patches), basename]
            self.run_command(targen_cmd)

            # printtarg
            page = self.page_size.get()
            dpi = self.resolution.get()
            patch_mm = self.patch_size.get()
            split = self.split_pages.get()

            printtarg_cmd = ["printtarg", "-v", "-i", self.map_instrument(self.instrument.get())]

            # Spacing
            if self.spacing_mode.get() == "NONE":
                printtarg_cmd.append("-n")
            elif self.spacing_mode.get() == "B&W":
                printtarg_cmd.append("-b")
            elif self.spacing_mode.get() == "Color":
                printtarg_cmd.append("-c")

            # Randomize / no randomize
            if not self.randomize.get():
                printtarg_cmd.append("-r")

            if split:
                printtarg_cmd.append("-P")

            printtarg_cmd.extend(["-p", page, "-a", str(patch_mm / 7.0), "-T", str(dpi), basename])
            self.run_command(printtarg_cmd)

        threading.Thread(target=task).start()

    def scan_patch(self):
        def task():
            basename = self.basename.get()
            if not messagebox.askyesno("Scanner check", "Is the scanner connected?"):
                self.log("Scan aborted: scanner not connected.")
                return

            ti2_file = filedialog.askopenfilename(title="Select .ti2 file", filetypes=[("TI2 files", "*.ti2")])
            if not ti2_file:
                self.log("No TI2 file selected, using default basename.")
                ti2_file = f"{basename}.ti2"

            chartread_cmd = ["chartread", "-v", "-H", os.path.splitext(os.path.basename(ti2_file))[0]]
            self.run_command(chartread_cmd)

        threading.Thread(target=task).start()

    def map_instrument(self, name):
        mapping = {
            "i1Pro/CR30": "i1",
            "i1Pro2": "i1",
            "i1Pro3+": "p3",
            "ColorMunki": "CM",
            "DTP20": "20",
            "DTP22": "22",
            "DTP41": "41",
            "DTP51": "51",
            "SpectroScan": "SS",
        }
        return mapping.get(name, "i1")


if __name__ == "__main__":
    root = tk.Tk()
    app = ColorProfileGUI(root)
    root.mainloop()
