from pathlib import Path
import importlib.util
import math
import sys
import tkinter as tk

from PIL import Image, ImageTk


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

spec = importlib.util.spec_from_file_location("racecore_v36_for_v37", V36_PATH)
v36 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v36)

from core.brush_preview import BrushPreviewSurface


class StudioV37(v36.StudioV36):
    """Vloeiende, live gemaskeerde penseelweergave zonder canvas-herbouw."""

    def __init__(self):
        self.brush_live_surface = None
        self.brush_live_metrics = None
        self.brush_publish_job = None
        self.brush_surface_builds = 0
        super().__init__()
        self.title("RaceCore Livery Studio v3.7 - Smooth Paint")

    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  v3.7")

    def start_brush_paint(self):
        if self.focus_master is None:
            self.show_preview("Schildercanvas voorbereiden...")
        super().start_brush_paint()

    def _build_brush_live_surface(self):
        if self.focus_master is None or not hasattr(self, "preview_label"):
            self.brush_live_surface = None
            self.brush_live_metrics = None
            return None
        width = max(1, self.preview_label.winfo_width())
        height = max(1, self.preview_label.winfo_height())
        base, metrics = self.focus_viewport.render(self.focus_master, width, height)
        source = self.pipeline.source.load_rgba()
        editable = self.pipeline.masks.editable(
            (4096, 4096), source,
            self.snapshot().get("hiddenOfficialDecalGroups", ()),
        )
        crop = tuple(int(round(value)) for value in metrics.crop)
        display_mask = editable.crop(crop).resize(
            (metrics.size, metrics.size), Image.Resampling.LANCZOS
        )
        self.brush_live_surface = BrushPreviewSurface(base, display_mask)
        self.brush_live_metrics = metrics
        self.brush_surface_builds += 1
        self._redraw_brush_strokes()
        return self.brush_live_surface

    def _display_brush_width(self):
        metrics = self.brush_live_metrics
        if metrics is None:
            return 2
        left, _top, right, _bottom = metrics.crop
        return max(2, round(
            self.brush_size.get() * metrics.size / max(1, right - left)
        ))

    def _display_point(self, point):
        return self._texture_point(point, self.brush_live_metrics.size)

    def _redraw_brush_strokes(self):
        if self.brush_live_surface is None or self.brush_live_metrics is None:
            return
        width = self._display_brush_width()
        colour = self.accent.get().upper()
        for stroke in self.brush_strokes:
            self.brush_live_surface.draw_stroke(
                [self._display_point(point) for point in stroke], width, colour
            )

    def _schedule_brush_publish(self):
        if self.brush_publish_job is None:
            self.brush_publish_job = self.after(16, self._publish_live_brush)

    def _publish_live_brush(self):
        self.brush_publish_job = None
        if self.panel_paint_mode != "brush" or self.brush_live_surface is None:
            return
        try:
            image = self.brush_live_surface.render()
            self._publish_preview_image(image)
            self.focus_tools.lift()
            self._lift_brush_tools()
        except (OSError, ValueError, tk.TclError):
            pass

    def show_selection_overlay(self):
        if self.panel_paint_mode != "brush":
            super().show_selection_overlay()
            return
        self._cancel_brush_publish()
        if self._build_brush_live_surface() is not None:
            self._publish_live_brush()

    def preview_press(self, event):
        if self.panel_paint_mode != "brush":
            return super().preview_press(event)
        point = self.event_to_texture(event)
        if point is not None:
            self.begin_brush_stroke(point)
        return "break"

    def preview_drag(self, event):
        if self.panel_paint_mode != "brush":
            return super().preview_drag(event)
        point = self.event_to_texture(event)
        if point is not None:
            self.extend_brush_stroke(point)
        return "break"

    def preview_release(self, event):
        if self.panel_paint_mode != "brush":
            return super().preview_release(event)
        self.brush_dragging = False
        self._update_brush_controls()
        self._publish_live_brush()
        self.status.set(
            f"{len(self.brush_strokes)} streken | schilder verder of kies OPSLAAN"
        )
        return "break"

    def begin_brush_stroke(self, point):
        point = self._clean_brush_point(point)
        self.brush_strokes.append([point])
        self.brush_dragging = True
        if self.brush_live_surface is None:
            self._build_brush_live_surface()
        if self.brush_live_surface is not None:
            self.brush_live_surface.draw_stroke(
                [self._display_point(point)], self._display_brush_width(),
                self.accent.get().upper(),
            )
        self._update_brush_controls()
        self._schedule_brush_publish()

    def extend_brush_stroke(self, point):
        if not self.brush_dragging or not self.brush_strokes:
            return
        point = self._clean_brush_point(point)
        previous = self.brush_strokes[-1][-1]
        if math.hypot(point[0] - previous[0], point[1] - previous[1]) < 2:
            return
        self.brush_strokes[-1].append(point)
        if self.brush_live_surface is None:
            self._build_brush_live_surface()
        if self.brush_live_surface is not None:
            self.brush_live_surface.draw_stroke(
                [self._display_point(previous), self._display_point(point)],
                self._display_brush_width(), self.accent.get().upper(),
            )
        self._schedule_brush_publish()

    def brush_size_changed(self, raw=None):
        if raw is not None:
            self.brush_size.set(int(round(float(raw))))
        if hasattr(self, "brush_size_label"):
            self.brush_size_label.configure(text=f"{self.brush_size.get()} px")
        if self.panel_paint_mode == "brush":
            self.show_selection_overlay()

    def undo_brush_stroke(self):
        if self.panel_paint_mode != "brush" or not self.brush_strokes:
            return
        self.brush_strokes.pop()
        self.brush_dragging = False
        self._update_brush_controls()
        self.show_selection_overlay()

    def finish_brush_paint(self):
        self._cancel_brush_publish()
        super().finish_brush_paint()
        self.brush_live_surface = None
        self.brush_live_metrics = None

    def cancel_panel_paint(self, refresh=True):
        self._cancel_brush_publish()
        self.brush_live_surface = None
        self.brush_live_metrics = None
        super().cancel_panel_paint(refresh=refresh)

    def _cancel_brush_publish(self):
        if self.brush_publish_job is not None:
            try:
                self.after_cancel(self.brush_publish_job)
            except tk.TclError:
                pass
            self.brush_publish_job = None


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