from pathlib import Path
import importlib.util
import sys

from PIL import Image, ImageDraw, ImageTk


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

spec = importlib.util.spec_from_file_location("racecore_v26_for_v27", V26_PATH)
v26 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v26)

from core.preview_interaction import logo_assets, topmost_hit, vector_assets


class StudioV27(v26.StudioV26):
    """Direct selecteren en verslepen op de zichtbare UV-preview."""

    def __init__(self):
        self.preview_selected_uid = None
        self.preview_selected_instance = 0
        self.direct_drag = None
        super().__init__()
        self.title("RaceCore Livery Studio v2.7 - Direct Canvas")

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

        self.preview_label.configure(cursor="hand2", takefocus=True)
        self.preview_label.bind("<ButtonPress-1>", self.preview_press)
        self.preview_label.bind("<B1-Motion>", self.preview_drag)
        self.preview_label.bind("<ButtonRelease-1>", self.preview_release)

        viewer = self.preview_label.master
        self.direct_hint = v26.v25.tk.Label(
            viewer,
            text="KLIK OBJECT  •  SLEEP OM TE VERPLAATSEN",
            font=("Segoe UI", 8, "bold"), fg="#A5F3FC", bg="#0B1730",
            padx=10, pady=6,
        )
        self.direct_hint.place(x=20, y=18)
        self.direct_hint.lift()

    def _sidebar_selection_changed(self, event=None):
        tree = getattr(self, "layer_sidebar_tree", None)
        if tree and tree.selection():
            uid = tree.selection()[0].removeprefix("layer:")
            if uid != self.preview_selected_uid:
                self.preview_selected_uid = uid
                self.preview_selected_instance = 0
        super()._sidebar_selection_changed(event)
        self.show_selection_overlay()

    def show_preview(self, label):
        super().show_preview(label)
        self.show_selection_overlay()

    def preview_image_metrics(self):
        path = self.pipeline.paths.generated / "preview.png"
        if not path.exists():
            return None
        with Image.open(path) as opened:
            image_width, image_height = opened.size
        label_width = max(1, self.preview_label.winfo_width())
        label_height = max(1, self.preview_label.winfo_height())
        return (
            image_width, image_height,
            (label_width - image_width) / 2,
            (label_height - image_height) / 2,
        )

    def event_to_texture(self, event):
        metrics = self.preview_image_metrics()
        if metrics is None:
            return None
        image_width, image_height, offset_x, offset_y = metrics
        local_x, local_y = event.x - offset_x, event.y - offset_y
        if not (0 <= local_x < image_width and 0 <= local_y < image_height):
            return None
        return (
            local_x * 4096 / image_width,
            local_y * 4096 / image_height,
        )

    def reference_assets(self, reference):
        layer = self.layer_collections()[reference.kind][reference.index]
        if reference.kind == "logo":
            return logo_assets(layer, v26.v25.v24.V12.PLACEMENTS)
        normalisers = {
            "shape": self.normalise_shape,
            "stripe": self.normalise_stripe,
            "text": self.normalise_text,
        }
        asset_functions = {
            "shape": v26.v25.v24.V14.shape_asset,
            "stripe": v26.v25.v24.V13.stripe_asset,
            "text": v26.v25.v24.V17.text_asset,
        }
        return vector_assets(
            layer, (4096, 4096), normalisers[reference.kind],
            asset_functions[reference.kind], v26.v25.v24.V13.stripe_instances,
        )

    def hit_preview_object(self, x, y):
        collections = self.reconcile_layer_order()
        references = list(reversed(v26.v25.ordered_references(
            collections, self.layer_order
        )))
        candidates = []
        for reference in references:
            layer = collections[reference.kind][reference.index]
            if not layer.get("visible", True) or layer.get("locked", False):
                continue
            candidates.append((reference, self.reference_assets(reference)))
        return topmost_hit(candidates, x, y)

    def preview_press(self, event):
        point = self.event_to_texture(event)
        if point is None:
            return
        hit = self.hit_preview_object(*point)
        if hit is None:
            self.status.set("Geen verplaatsbaar object geraakt")
            return
        reference, placed = hit
        self.preview_selected_uid = reference.uid
        self.preview_selected_instance = placed.instance_index
        iid = f"layer:{reference.uid}"
        self.layer_sidebar_tree.selection_set(iid)
        self.layer_sidebar_tree.focus(iid)
        self.layer_sidebar_tree.see(iid)
        self.inline_properties.load_selected()
        self.preview_label.focus_set()

        layer = self.layer_collections()[reference.kind][reference.index]
        origin = self.drag_origin(reference.kind, layer, placed.instance_index)
        self.direct_drag = {
            "uid": reference.uid,
            "kind": reference.kind,
            "instance": placed.instance_index,
            "start": point,
            "origin": origin,
            "moved": False,
        }
        self.show_selection_overlay()
        self.status.set("Object geselecteerd • sleep om te verplaatsen")

    @staticmethod
    def drag_origin(kind, layer, instance_index):
        if kind != "logo":
            return int(layer.get("centerX", 0)), int(layer.get("centerY", 0))
        if layer.get("pairMode") == "separate":
            offsets = layer.get("pairOffsets", [])
            if instance_index < len(offsets):
                raw = offsets[instance_index]
                return int(raw.get("x", 0)), int(raw.get("y", 0))
            return 0, 0
        return int(layer.get("offsetX", 0)), int(layer.get("offsetY", 0))

    def preview_drag(self, event):
        if self.direct_drag is None:
            return
        point = self.event_to_texture(event)
        if point is None:
            return
        reference = self.resolve_layer_uid(self.direct_drag["uid"])
        if reference is None:
            self.direct_drag = None
            return
        layer = self.layer_collections()[reference.kind][reference.index]
        if layer.get("locked", False):
            self.direct_drag = None
            return

        delta_x = int(round(point[0] - self.direct_drag["start"][0]))
        delta_y = int(round(point[1] - self.direct_drag["start"][1]))
        origin_x, origin_y = self.direct_drag["origin"]
        instance = self.direct_drag["instance"]
        if reference.kind == "logo":
            self.move_logo(layer, instance, origin_x, origin_y, delta_x, delta_y)
        else:
            layer["centerX"] = max(0, min(4096, origin_x + delta_x))
            direction = -1 if layer.get("paired", False) and instance > 0 else 1
            layer["centerY"] = max(0, min(4096, origin_y + direction * delta_y))

        self.direct_drag["moved"] = self.direct_drag["moved"] or bool(delta_x or delta_y)
        self.schedule_preview()
        self.show_selection_overlay()
        self.status.set(
            f"Verplaatsen: {delta_x:+d} horizontaal, {delta_y:+d} verticaal"
        )

    @staticmethod
    def move_logo(layer, instance, origin_x, origin_y, delta_x, delta_y):
        if layer.get("pairMode") == "separate":
            offsets = list(layer.get("pairOffsets", []))
            while len(offsets) <= instance:
                offsets.append({"x": 0, "y": 0})
            offsets[instance] = {
                "x": max(-4096, min(4096, origin_x + delta_x)),
                "y": max(-4096, min(4096, origin_y + delta_y)),
            }
            layer["pairOffsets"] = offsets
            return
        direction = -1 if instance and layer.get("mirrorPairedOffsets") else 1
        layer["offsetX"] = max(-4096, min(4096, origin_x + direction * delta_x))
        layer["offsetY"] = max(-4096, min(4096, origin_y + direction * delta_y))

    def preview_release(self, _event):
        drag = self.direct_drag
        self.direct_drag = None
        if not drag or not drag["moved"]:
            return
        self.record_state()
        self.autosave()
        self.schedule_preview()
        self.refresh_layer_sidebar(drag["uid"])
        self.status.set("Object verplaatst • Undo maakt de hele sleepactie ongedaan")

    def selected_preview_asset(self):
        if not self.preview_selected_uid:
            return None
        reference = self.resolve_layer_uid(self.preview_selected_uid)
        if reference is None:
            return None
        assets = self.reference_assets(reference)
        if not assets:
            return None
        index = min(self.preview_selected_instance, len(assets) - 1)
        return assets[index]

    def show_selection_overlay(self):
        path = self.pipeline.paths.generated / "preview.png"
        placed = self.selected_preview_asset()
        if not path.exists() or placed is None:
            return
        try:
            with Image.open(path) as opened:
                image = opened.convert("RGB")
            draw = ImageDraw.Draw(image)
            scale_x, scale_y = image.width / 4096, image.height / 4096
            left, top, right, bottom = placed.bounds
            box = (
                round(left * scale_x), round(top * scale_y),
                round(right * scale_x), round(bottom * scale_y),
            )
            draw.rectangle(box, outline="#22D3EE", width=3)
            radius = 5
            for x, y in ((box[0], box[1]), (box[2], box[1]),
                         (box[0], box[3]), (box[2], box[3])):
                draw.ellipse(
                    (x - radius, y - radius, x + radius, y + radius),
                    fill="#F8FAFC", outline="#22D3EE", width=2,
                )
            self.preview_photo = ImageTk.PhotoImage(image)
            self.preview_label.configure(image=self.preview_photo, text="")
            self.direct_hint.lift()
        except (OSError, ValueError):
            pass


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