from pathlib import Path
import copy
import importlib.util
import sys
import tkinter as tk
from tkinter import ttk


APP = Path(__file__).resolve().parent
V24_PATH = APP / "racecore_studio_v24.py"
if str(APP) not in sys.path:
    sys.path.insert(0, str(APP))

spec = importlib.util.spec_from_file_location("racecore_v24_for_v25", V24_PATH)
v24 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v24)

from core.layer_workspace import (
    LAYER_KINDS,
    ensure_uid,
    move_uid,
    ordered_references,
    reconcile_order,
    without_uid,
)


KIND_LABELS = {
    "shape": "Vlak",
    "stripe": "Lijn",
    "text": "Tekst",
    "logo": "Logo",
}
KIND_COLOURS = {
    "shape": "#FDE68A",
    "stripe": "#A7F3D0",
    "text": "#FDBA74",
    "logo": "#F9A8D4",
}


class StudioV25(v24.StudioV24):
    """Eenvoudige vaste werkruimte met een echte globale lagenvolgorde."""

    def __init__(self):
        self.layer_order = []
        super().__init__()
        self.title("RaceCore Livery Studio v2.5 - Layer Workspace")
        self.geometry("1540x880")
        self.minsize(1240, 740)
        self.refresh_layer_sidebar()

    @staticmethod
    def _with_uid(layer, kind, normaliser):
        layer = normaliser(layer)
        ensure_uid(layer, kind)
        return layer

    @staticmethod
    def normalise_shape(layer):
        return StudioV25._with_uid(layer, "shape", v24.V14.StudioV14.normalise_shape)

    @staticmethod
    def normalise_stripe(layer):
        return StudioV25._with_uid(layer, "stripe", v24.V13.StudioV13.normalise_stripe)

    @staticmethod
    def normalise_text(layer):
        return StudioV25._with_uid(layer, "text", v24.V17.StudioV17.normalise_text)

    @staticmethod
    def normalise_layer(layer):
        return StudioV25._with_uid(layer, "logo", v24.StudioV24.normalise_layer)

    def build_layout(self):
        super().build_layout()
        header = self.winfo_children()[0]
        for child in header.winfo_children():
            if isinstance(child, tk.Label) and "Livery Studio" in child.cget("text"):
                child.configure(text="Livery Studio  v2.5")

        viewer = self.preview_label.master
        body = viewer.master
        panel = tk.Frame(body, bg="#101F3C", width=300)
        panel.pack(side="right", fill="y", padx=(14, 0), before=viewer)
        panel.pack_propagate(False)
        self.layer_sidebar = panel

        top = tk.Frame(panel, bg="#101F3C")
        top.pack(fill="x", padx=14, pady=(15, 8))
        tk.Label(
            top, text="LAGEN", font=("Segoe UI", 12, "bold"),
            fg="#22D3EE", bg="#101F3C",
        ).pack(side="left")
        self.layer_sidebar_summary = tk.StringVar(value="0 objecten")
        tk.Label(
            top, textvariable=self.layer_sidebar_summary,
            font=("Segoe UI", 8), fg="#94A3B8", bg="#101F3C",
        ).pack(side="right")

        add = tk.Menubutton(
            panel, text="+ OBJECT TOEVOEGEN", font=("Segoe UI", 9, "bold"),
            fg="#08152C", bg="#67E8F9", activebackground="#A5F3FC",
            relief="flat", pady=8, cursor="hand2",
        )
        add_menu = tk.Menu(
            add, tearoff=False, font=("Segoe UI", 10),
            bg="#F8FAFC", fg="#0F172A", activebackground="#DBEAFE",
        )
        add_menu.add_command(label="Kleurvlak", command=self.add_shape)
        add_menu.add_command(label="Streep of lijn", command=self.add_stripe)
        add_menu.add_command(label="Tekst", command=lambda: self.add_text("team"))
        add_menu.add_command(label="Startnummer", command=lambda: self.add_text("number"))
        add_menu.add_separator()
        add_menu.add_command(label="Logo uit bibliotheek", command=self.asset_library_dialog)
        add_menu.add_command(label="Eigen PNG-logo", command=self.logo_dialog)
        add.configure(menu=add_menu)
        add.pack(fill="x", padx=14, pady=(0, 10))

        tree_box = tk.Frame(panel, bg="#101F3C")
        tree_box.pack(fill="both", expand=True, padx=10)
        self.layer_sidebar_tree = ttk.Treeview(
            tree_box, columns=("kind", "status"), show="tree headings",
            selectmode="browse", height=18,
        )
        self.layer_sidebar_tree.heading("#0", text="Object")
        self.layer_sidebar_tree.heading("kind", text="Soort")
        self.layer_sidebar_tree.heading("status", text="")
        self.layer_sidebar_tree.column("#0", width=145, minwidth=105)
        self.layer_sidebar_tree.column("kind", width=60, minwidth=50, anchor="center")
        self.layer_sidebar_tree.column("status", width=44, minwidth=38, anchor="center")
        scroll = ttk.Scrollbar(
            tree_box, orient="vertical", command=self.layer_sidebar_tree.yview
        )
        self.layer_sidebar_tree.configure(yscrollcommand=scroll.set)
        self.layer_sidebar_tree.pack(side="left", fill="both", expand=True)
        scroll.pack(side="right", fill="y")
        self.layer_sidebar_tree.bind(
            "<Double-Button-1>", lambda _event: self.sidebar_edit()
        )
        self.layer_sidebar_tree.bind(
            "<Delete>", lambda _event: self.sidebar_delete()
        )
        self.layer_sidebar_tree.bind(
            "<space>", lambda _event: self.sidebar_toggle()
        )

        tk.Label(
            panel, text="Bovenste object ligt vooraan",
            font=("Segoe UI", 8), fg="#94A3B8", bg="#101F3C",
        ).pack(anchor="w", padx=14, pady=(7, 5))

        buttons = tk.Frame(panel, bg="#101F3C")
        buttons.pack(fill="x", padx=10, pady=(0, 7))
        actions = [
            ("BEWERK", self.sidebar_edit, "#334155", "#F8FAFC"),
            ("KOPIE", self.sidebar_duplicate, "#334155", "#F8FAFC"),
            ("↑", lambda: self.sidebar_move(1), "#334155", "#F8FAFC"),
            ("↓", lambda: self.sidebar_move(-1), "#334155", "#F8FAFC"),
        ]
        for title, command, bg, fg in actions:
            tk.Button(
                buttons, text=title, command=command,
                font=("Segoe UI", 8, "bold"), fg=fg, bg=bg,
                activebackground="#475569", activeforeground=fg,
                relief="flat", pady=7,
            ).pack(side="left", fill="x", expand=True, padx=2)

        buttons2 = tk.Frame(panel, bg="#101F3C")
        buttons2.pack(fill="x", padx=10, pady=(0, 13))
        actions2 = [
            ("TOON", self.sidebar_toggle, "#334155"),
            ("SLOT", self.sidebar_lock, "#334155"),
            ("VERWIJDER", self.sidebar_delete, "#7F1D1D"),
        ]
        for title, command, bg in actions2:
            tk.Button(
                buttons2, text=title, command=command,
                font=("Segoe UI", 8, "bold"), fg="#F8FAFC", bg=bg,
                activebackground="#475569", activeforeground="#F8FAFC",
                relief="flat", pady=7,
            ).pack(side="left", fill="x", expand=True, padx=2)

        self._remove_old_layer_menu()

    def _remove_old_layer_menu(self):
        for _button, menu in getattr(self, "toolbar_menus", []):
            end = menu.index("end")
            if end is None:
                continue
            for index in range(end, -1, -1):
                try:
                    if menu.type(index) == "command" and menu.entrycget(index, "label") == "Centraal laagoverzicht":
                        menu.delete(index)
                except tk.TclError:
                    pass

    def layer_collections(self):
        return {
            "shape": self.shape_layers,
            "stripe": self.stripe_layers,
            "text": self.text_layers,
            "logo": self.logo_layers,
        }

    def reconcile_layer_order(self):
        collections = self.layer_collections()
        for kind in LAYER_KINDS:
            normaliser = {
                "shape": self.normalise_shape,
                "stripe": self.normalise_stripe,
                "text": self.normalise_text,
                "logo": self.normalise_layer,
            }[kind]
            for index, layer in enumerate(collections[kind]):
                collections[kind][index] = normaliser(layer)
        self.layer_order = reconcile_order(collections, self.layer_order)
        return collections

    def snapshot(self):
        self.reconcile_layer_order()
        state = super().snapshot()
        state["schemaVersion"] = 5
        state["layerOrder"] = list(self.layer_order)
        return state

    def apply_state(self, state, record=False):
        self.layer_order = list(copy.deepcopy(state).get("layerOrder", []))
        super().apply_state(state, record)
        self.reconcile_layer_order()
        self.refresh_layer_sidebar()

    def new_blank_project(self):
        self.layer_order = []
        super().new_blank_project()
        self.refresh_layer_sidebar()

    def _after_layer_commit(self):
        self.reconcile_layer_order()
        self.refresh_layer_sidebar()

    def commit_layers(self, select=None, label="Lagen bijgewerkt"):
        super().commit_layers(select, label)
        self._after_layer_commit()

    def commit_stripes(self, select=None, label="Strepen bijgewerkt"):
        super().commit_stripes(select, label)
        self._after_layer_commit()

    def commit_shapes(self, select=None, label="Kleurvlakken bijgewerkt"):
        super().commit_shapes(select, label)
        self._after_layer_commit()

    def commit_texts(self, select=None, label="Tekst bijgewerkt"):
        super().commit_texts(select, label)
        self._after_layer_commit()

    def refresh_layer_sidebar(self, select_uid=None):
        tree = getattr(self, "layer_sidebar_tree", None)
        if not tree or not tree.winfo_exists():
            return
        current = select_uid
        if not current and tree.selection():
            current = tree.selection()[0].removeprefix("layer:")
        collections = self.reconcile_layer_order()
        references = ordered_references(collections, self.layer_order)
        self._layer_sidebar_refs = {reference.uid: reference for reference in references}
        desired_iids = [f"layer:{reference.uid}" for reference in reversed(references)]
        existing_iids = list(tree.get_children())
        rebuild = existing_iids != desired_iids
        if rebuild:
            tree.delete(*existing_iids)
        visible = locked = 0
        for reference in reversed(references):
            layer = collections[reference.kind][reference.index]
            visible += int(layer.get("visible", True))
            locked += int(layer.get("locked", False))
            status = ""
            if not layer.get("visible", True):
                status = "○"
            elif layer.get("locked", False):
                status = "🔒"
            else:
                status = "●"
            iid = f"layer:{reference.uid}"
            options = {
                "text": layer.get("name", "Naamloos object"),
                "values": (KIND_LABELS[reference.kind], status),
                "tags": (
                    reference.kind,
                    "hidden" if not layer.get("visible", True) else "shown",
                ),
            }
            if rebuild:
                tree.insert("", "end", iid=iid, **options)
            else:
                tree.item(iid, **options)
        for kind, colour in KIND_COLOURS.items():
            tree.tag_configure(kind, foreground=colour)
        tree.tag_configure("hidden", foreground="#64748B")
        self.layer_sidebar_summary.set(
            f"{len(references)} objecten · {visible} aan · {locked} op slot"
        )
        if current and tree.exists(f"layer:{current}"):
            iid = f"layer:{current}"
            tree.selection_set(iid)
            tree.focus(iid)
            tree.see(iid)

    def selected_sidebar_reference(self):
        tree = getattr(self, "layer_sidebar_tree", None)
        if not tree or not tree.selection():
            self.status.set("Kies eerst een object in Lagen")
            return None
        uid = tree.selection()[0].removeprefix("layer:")
        self.reconcile_layer_order()
        reference = getattr(self, "_layer_sidebar_refs", {}).get(uid)
        if reference is None:
            self.refresh_layer_sidebar(uid)
            reference = getattr(self, "_layer_sidebar_refs", {}).get(uid)
        return reference

    def sidebar_edit(self):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        layer = self.layer_collections()[reference.kind][reference.index]
        if layer.get("locked", False):
            self.status.set("Ontgrendel dit object eerst")
            return
        if reference.kind == "shape":
            v24.V14.ShapePlacementEditor(self, reference.index)
        elif reference.kind == "stripe":
            v24.V13.StripePlacementEditor(self, reference.index)
        elif reference.kind == "text":
            v24.V17.TextPlacementEditor(self, reference.index)
        elif not Path(layer.get("path", "")).is_file():
            self.status.set("Het afbeeldingsbestand van dit logo ontbreekt")
        else:
            v24.LogoPlacementEditorV24(self, reference.index)

    def sidebar_duplicate(self):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        collection = self.layer_collections()[reference.kind]
        layer = copy.deepcopy(collection[reference.index])
        layer.pop("layerUid", None)
        ensure_uid(layer, reference.kind)
        layer["name"] = f"{layer.get('name', KIND_LABELS[reference.kind])} kopie"
        layer["locked"] = False
        if reference.kind in {"shape", "stripe", "text"}:
            layer["centerX"] = min(4096, int(layer.get("centerX", 2000)) + 70)
            layer["centerY"] = min(4096, int(layer.get("centerY", 2000)) + 70)
        else:
            layer["offsetX"] = int(layer.get("offsetX", 0)) + 70
            layer["offsetY"] = int(layer.get("offsetY", 0)) + 70
        collection.insert(reference.index + 1, layer)
        old_position = self.layer_order.index(reference.uid)
        self.layer_order.insert(old_position + 1, layer["layerUid"])
        self.commit_kind(reference.kind, reference.index + 1, "Object gedupliceerd")
        self.refresh_layer_sidebar(layer["layerUid"])

    def sidebar_toggle(self):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        layer = self.layer_collections()[reference.kind][reference.index]
        layer["visible"] = not layer.get("visible", True)
        self.commit_kind(reference.kind, reference.index, "Zichtbaarheid aangepast")
        self.refresh_layer_sidebar(reference.uid)

    def sidebar_lock(self):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        layer = self.layer_collections()[reference.kind][reference.index]
        layer["locked"] = not layer.get("locked", False)
        self.commit_kind(reference.kind, reference.index, "Vergrendeling aangepast")
        self.refresh_layer_sidebar(reference.uid)

    def sidebar_delete(self):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        collection = self.layer_collections()[reference.kind]
        if collection[reference.index].get("locked", False):
            self.status.set("Een vergrendeld object kan niet worden verwijderd")
            return
        collection.pop(reference.index)
        self.layer_order = without_uid(self.layer_order, reference.uid)
        self.commit_kind(reference.kind, None, "Object verwijderd")

    def sidebar_move(self, delta):
        reference = self.selected_sidebar_reference()
        if reference is None:
            return
        layer = self.layer_collections()[reference.kind][reference.index]
        if layer.get("locked", False):
            self.status.set("Ontgrendel dit object eerst")
            return
        changed = move_uid(self.layer_order, reference.uid, delta)
        if changed == self.layer_order:
            return
        self.layer_order = changed
        self.record_state()
        self.autosave()
        self.schedule_preview()
        self.refresh_layer_sidebar(reference.uid)
        self.status.set("Laagvolgorde aangepast")

    def _compose_state_layers(
        self, canvas, state, exclude_stripe=None, exclude_shape=None,
        exclude_logo=None, exclude_text=None, carbon_top_only=False,
    ):
        if carbon_top_only:
            has_vector_foreground = any(
                index != excluded and bool(item.get("visible", True))
                for items, excluded in (
                    (state.get("shapeLayers", []), exclude_shape),
                    (state.get("stripeLayers", []), exclude_stripe),
                    (state.get("textLayers", []), exclude_text),
                )
                for index, item in enumerate(items)
            )
            has_logo_foreground = any(
                index != exclude_logo
                and bool(item.get("visible", True))
                and v24.logo_above_carbon(item)
                for index, item in enumerate(state.get("logoLayers", []))
            )
            # Tijdens het slepen van het enige voorgrondlogo is deze laag leeg.
            # Vermijd dan een tweede volledige 4K-opbouw enkel om een leeg
            # alfakanaal terug te geven.
            if not has_vector_foreground and not has_logo_foreground:
                return canvas.convert("RGBA")
        # CreativeLayerEngine plakt lokaal en in-place. Werk daarom altijd op
        # één eigen RGBA-kopie; de materiaalcache blijft zo schoon en iedere
        # laag vermijdt een volledige 4096x4096-tussenafbeelding.
        canvas = canvas.convert("RGBA").copy()
        source = self.pipeline.source.load_rgba()
        editable = self.pipeline.masks.editable(
            canvas.size, source, state.get("hiddenOfficialDecalGroups", ())
        )
        collections = {
            "shape": copy.deepcopy(state.get("shapeLayers", [])),
            "stripe": copy.deepcopy(state.get("stripeLayers", [])),
            "text": copy.deepcopy(state.get("textLayers", [])),
            "logo": copy.deepcopy(state.get("logoLayers", [])),
        }
        order = reconcile_order(collections, state.get("layerOrder", []))
        excluded = {
            "shape": exclude_shape,
            "stripe": exclude_stripe,
            "text": exclude_text,
            "logo": exclude_logo,
        }
        normalisers = {
            "shape": self.normalise_shape,
            "stripe": self.normalise_stripe,
            "text": self.normalise_text,
            "logo": self.normalise_layer,
        }
        assets = {
            "shape": v24.V14.shape_asset,
            "stripe": v24.V13.stripe_asset,
            "text": v24.V17.text_asset,
        }
        for reference in ordered_references(collections, order):
            if excluded[reference.kind] == reference.index:
                continue
            layer = collections[reference.kind][reference.index]
            if reference.kind == "logo":
                if carbon_top_only and not v24.logo_above_carbon(layer):
                    continue
                canvas = self.layer_engine.logos(
                    canvas, [layer], editable, self.normalise_layer,
                    v24.V12.PLACEMENTS,
                )
            else:
                canvas = self.layer_engine.vector_layers(
                    canvas, [layer], editable, normalisers[reference.kind],
                    assets[reference.kind], v24.V13.stripe_instances,
                )
        return canvas


if __name__ == "__main__":
    StudioV25().mainloop()
