from pathlib import Path
from PIL import Image, ImageTk, ImageChops
import importlib.util
import math
import time
import tkinter as tk
from tkinter import ttk


ROOT = Path(__file__).resolve().parents[2]
APP = Path(__file__).resolve().parent
GENERATED = APP / 'generated'
AUTOSAVE = APP / 'autosave.racecore.json'
LIVE_CONFIG = APP / 'live-link-ui.json'
V17_PATH = ROOT / 'outputs' / 'RaceCore_Studio_v1.7' / 'racecore_studio_v17.py'
V17_AUTOSAVE = V17_PATH.parent / 'autosave.racecore.json'

spec = importlib.util.spec_from_file_location('racecore_v17', V17_PATH)
v17 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v17)

for module in (
    v17, v17.v16, v17.v16.v15, v17.v16.v15.v14,
    v17.v16.v15.v14.v13, v17.v16.v15.v14.v13.v12,
    v17.v16.v15.v14.v13.v12.v11,
):
    module.APP = APP
    module.GENERATED = GENERATED
    module.AUTOSAVE = AUTOSAVE
    module.LIVE_CONFIG = LIVE_CONFIG

COLORS = v17.COLORS
ASSET_DIR = v17.v16.ASSET_DIR
LAYOUTS = (
    'Professioneel sponsorblok',
    'Horizontale sponsorrij',
    'Verticale sponsorstapel',
    'Motorkap sponsor-grid',
    'Achtervleugel sponsorrij',
)
LAYOUT_INFO = {
    'Professioneel sponsorblok': 'Eén grote hoofdsponsor met kleinere logo’s er strak omheen, automatisch op beide zijkanten.',
    'Horizontale sponsorrij': 'Logo’s in één rustige rij op beide zijkanten, met gelijke tussenruimte.',
    'Verticale sponsorstapel': 'Logo’s onder elkaar op beide zijkanten; ideaal naast een startnummerpaneel.',
    'Motorkap sponsor-grid': 'Een gecentreerd blok op de motorkap, verdeeld over maximaal twee rijen.',
    'Achtervleugel sponsorrij': 'Een compacte rij over de achtervleugel voor kleine partnerlogo’s.',
}


def evenly_spaced(count, span):
    if count <= 1:
        return [0]
    return [round(-span / 2 + span * index / (count - 1)) for index in range(count)]


def sponsor_layout(name, count):
    """Geef position, scale, rotation, offsetX en offsetY per sponsor terug."""
    count = max(0, min(8, int(count)))
    if not count:
        return []
    if name == 'Horizontale sponsorrij':
        return [
            {'position': 'Side panels (paired)', 'scale': 26 if count > 4 else 32,
             'rotation': 0, 'offsetX': x, 'offsetY': 0}
            for x in evenly_spaced(count, min(1250, 300 * (count - 1)))
        ]
    if name == 'Verticale sponsorstapel':
        return [
            {'position': 'Side panels (paired)', 'scale': 30 if count > 4 else 36,
             'rotation': 0, 'offsetX': 0, 'offsetY': y}
            for y in evenly_spaced(count, min(900, 205 * (count - 1)))
        ]
    if name == 'Motorkap sponsor-grid':
        columns = 1 if count == 1 else min(3, math.ceil(count / 2))
        rows = math.ceil(count / columns)
        x_values = evenly_spaced(columns, 440 if columns == 3 else 300)
        y_values = evenly_spaced(rows, 520 if rows > 1 else 0)
        result = []
        for index in range(count):
            row, column = divmod(index, columns)
            result.append({
                'position': 'Hood', 'scale': 28 if count > 4 else 34,
                'rotation': 0, 'offsetX': x_values[column], 'offsetY': y_values[row],
            })
        return result
    if name == 'Achtervleugel sponsorrij':
        return [
            {'position': 'Rear wing', 'scale': 18 if count > 4 else 23,
             'rotation': 0, 'offsetX': x, 'offsetY': 0}
            for x in evenly_spaced(count, min(1100, 260 * (count - 1)))
        ]

    # De eerste selectie wordt de hoofdsponsor. De rest vormt een rustig blok.
    positions = [
        {'position': 'Side panels (paired)', 'scale': 48, 'rotation': 0,
         'offsetX': -150, 'offsetY': -70},
        {'position': 'Side panels (paired)', 'scale': 29, 'rotation': 0,
         'offsetX': 360, 'offsetY': -235},
        {'position': 'Side panels (paired)', 'scale': 29, 'rotation': 0,
         'offsetX': 360, 'offsetY': 70},
        {'position': 'Side panels (paired)', 'scale': 25, 'rotation': 0,
         'offsetX': -350, 'offsetY': 255},
        {'position': 'Side panels (paired)', 'scale': 25, 'rotation': 0,
         'offsetX': 0, 'offsetY': 255},
        {'position': 'Side panels (paired)', 'scale': 25, 'rotation': 0,
         'offsetX': 350, 'offsetY': 255},
        {'position': 'Side panels (paired)', 'scale': 21, 'rotation': 0,
         'offsetX': -175, 'offsetY': 430},
        {'position': 'Side panels (paired)', 'scale': 21, 'rotation': 0,
         'offsetX': 175, 'offsetY': 430},
    ]
    return positions[:count]


class LogoPlacementEditorV18(v17.v16.v15.v14.LogoPlacementEditorV14):
    """Toon slimme, lokaal gespiegeld uitgelijnde sponsorparen correct."""
    def compose_display_preview(self, display_w, display_h):
        key = (display_w, display_h)
        if key != self.display_cache_key:
            self.display_cache_key = key
            self.display_background = self.background.resize(key, Image.Resampling.LANCZOS)
            self.display_editable = self.editable_mask.resize(key, Image.Resampling.LANCZOS)
        canvas = self.display_background.copy()
        bounds = None
        placements = v17.v16.v15.v14.v13.v12.PLACEMENTS[self.position.get()]
        for index, (anchor_x, anchor_y, base_rotation) in enumerate(placements):
            item = self.transformed_logo(base_rotation)
            dx, dy = self.layer['offsetX'], self.layer['offsetY']
            if index and self.layer.get('mirrorPairedOffsets'):
                dx, dy = -dx, -dy
            x, y = round(anchor_x + dx - item.width / 2), round(anchor_y + dy - item.height / 2)
            shown = item.resize((max(1, round(item.width * self.display_scale)),
                                 max(1, round(item.height * self.display_scale))),
                                Image.Resampling.LANCZOS)
            overlay = Image.new('RGBA', key, (0, 0, 0, 0))
            overlay.alpha_composite(shown, (round(x * self.display_scale),
                                            round(y * self.display_scale)))
            alpha = ImageChops.multiply(overlay.getchannel('A'), self.display_editable)
            overlay.putalpha(alpha)
            canvas = Image.alpha_composite(canvas, overlay)
            if bounds is None:
                bounds = (x, y, x + item.width, y + item.height)
        return canvas, bounds


class StudioV18(v17.StudioV17):
    def __init__(self):
        migrate = not AUTOSAVE.exists() and V17_AUTOSAVE.exists()
        super().__init__()
        self.title('RaceCore Livery Studio v1.8 - Smart Sponsor Layouts')
        if migrate:
            self.load_path(V17_AUTOSAVE, record=False)
            self.autosave()

    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  v1.8')
        toolbar = self.winfo_children()[1]
        # Houd de uitgebreide werkbalk ook op kleinere schermen bruikbaar.
        for child in toolbar.winfo_children():
            if isinstance(child, tk.Button):
                child.configure(font=('Segoe UI', 8, 'bold'), padx=7)
        tk.Button(
            toolbar, text='Sponsorplan', command=self.sponsor_planner,
            font=('Segoe UI', 8, 'bold'), fg=COLORS.NAVY, bg='#86EFAC',
            activebackground='#BBF7D0', relief='flat', padx=7,
        ).pack(side='left', padx=(0, 7), pady=6)

    def usable_assets(self):
        return [asset for asset in self.assets
                if (ASSET_DIR / asset.get('filename', '')).exists()]

    def edit_layer(self):
        index = self.selected()
        if index is None:
            self.status.set('Kies eerst een logo-laag')
            return
        layer = self.normalise_layer(self.logo_layers[index])
        if layer['locked']:
            self.status.set('Ontgrendel deze laag eerst')
            return
        if not Path(layer['path']).exists():
            self.status.set('Het afbeeldingsbestand van deze laag ontbreekt')
            return
        LogoPlacementEditorV18(self, index)

    def compose_logos(self, canvas, exclude=None):
        editable = self.editable_mask(canvas.size)
        placements = v17.v16.v15.v14.v13.v12.PLACEMENTS
        for layer_index, raw in enumerate(self.logo_layers):
            if layer_index == exclude:
                continue
            layer = self.normalise_layer(raw)
            if not layer['visible'] or not Path(layer['path']).exists():
                continue
            logo = Image.open(layer['path']).convert('RGBA')
            max_width = round(720 * layer['scale'] / 100)
            logo.thumbnail((max_width, round(max_width * .65)), Image.Resampling.LANCZOS)
            if layer['opacity'] < 100:
                alpha = logo.getchannel('A').point(
                    lambda value: value * layer['opacity'] // 100
                )
                logo.putalpha(alpha)
            for placement_index, (center_x, center_y, base_rotation) in enumerate(
                    placements[layer['position']]):
                dx, dy = layer['offsetX'], layer['offsetY']
                if placement_index and layer.get('mirrorPairedOffsets'):
                    dx, dy = -dx, -dy
                item = logo.rotate(base_rotation + layer['rotation'], expand=True,
                                   resample=Image.Resampling.BICUBIC)
                canvas = self.paste_item(canvas, item, center_x + dx,
                                         center_y + dy, editable)
        return canvas

    def sponsor_planner(self):
        win = tk.Toplevel(self)
        win.title('RaceCore Slimme Sponsorindeling')
        win.geometry('980x650')
        win.minsize(860, 590)
        win.configure(bg=COLORS.PANEL)
        win.transient(self)
        win.grab_set()

        top = tk.Frame(win, bg=COLORS.PANEL)
        top.pack(fill='x', padx=24, pady=(18, 10))
        tk.Label(top, text='SLIMME SPONSORINDELING', font=('Segoe UI', 15, 'bold'),
                 fg=COLORS.CYAN, bg=COLORS.PANEL).pack(side='left')
        tk.Label(top, text='Kies maximaal 8 logo’s', font=('Segoe UI', 9),
                 fg=COLORS.MUTED, bg=COLORS.PANEL).pack(side='right')

        content = tk.Frame(win, bg=COLORS.PANEL)
        content.pack(fill='both', expand=True, padx=24)
        left = tk.Frame(content, bg=COLORS.PANEL)
        left.pack(side='left', fill='both', expand=True, padx=(0, 14))
        right = tk.Frame(content, bg='#101F3C', width=330)
        right.pack(side='right', fill='y')
        right.pack_propagate(False)

        tk.Label(left, text='1. Selecteer de sponsorlogo’s', font=('Segoe UI', 10, 'bold'),
                 fg=COLORS.TEXT, bg=COLORS.PANEL).pack(anchor='w', pady=(0, 6))
        assets = self.usable_assets()
        asset_list = tk.Listbox(
            left, selectmode='extended', exportselection=False,
            font=('Segoe UI', 10), bg='#08152C', fg=COLORS.TEXT,
            selectbackground='#16A34A', relief='flat', activestyle='none',
        )
        asset_list.pack(fill='both', expand=True)
        for asset in assets:
            favourite = '[favoriet] ' if asset.get('favourite', False) else ''
            asset_list.insert('end', f"{favourite}{asset.get('name', 'Naamloos')}  |  "
                                     f"{asset.get('category', 'Overig')}")

        layout = tk.StringVar(value=LAYOUTS[0])
        description = tk.StringVar(value=LAYOUT_INFO[LAYOUTS[0]])
        selected_text = tk.StringVar(value='Nog geen logo’s geselecteerd')
        replace_previous = tk.BooleanVar(value=True)

        tk.Label(right, text='2. Kies de indeling', font=('Segoe UI', 10, 'bold'),
                 fg=COLORS.TEXT, bg='#101F3C').pack(anchor='w', padx=18,
                                                    pady=(18, 6))
        combo = ttk.Combobox(right, textvariable=layout, values=LAYOUTS,
                             state='readonly')
        combo.pack(fill='x', padx=18)
        tk.Label(right, textvariable=description, font=('Segoe UI', 9),
                 fg='#BFDBFE', bg='#101F3C', wraplength=290,
                 justify='left').pack(anchor='w', padx=18, pady=(10, 12))
        tk.Frame(right, bg='#253656', height=1).pack(fill='x', padx=18, pady=4)
        tk.Label(right, textvariable=selected_text, font=('Segoe UI', 10, 'bold'),
                 fg='#86EFAC', bg='#101F3C', wraplength=290,
                 justify='left').pack(anchor='w', padx=18, pady=(12, 8))
        tk.Checkbutton(
            right, text='Vorig automatisch sponsorplan vervangen',
            variable=replace_previous, font=('Segoe UI', 9), fg=COLORS.TEXT,
            bg='#101F3C', selectcolor='#253656', activebackground='#101F3C',
            activeforeground=COLORS.TEXT, wraplength=285, justify='left',
        ).pack(anchor='w', padx=14, pady=5)
        tk.Label(
            right,
            text='Bij het professionele blok wordt het bovenste geselecteerde logo de hoofdsponsor. Daarna blijven alle logo’s losse lagen die je nog kunt aanpassen.',
            font=('Segoe UI', 9), fg=COLORS.MUTED, bg='#101F3C',
            wraplength=290, justify='left',
        ).pack(anchor='w', padx=18, pady=8)
        tk.Frame(right, bg='#101F3C').pack(fill='both', expand=True)

        def update_selection(_event=None):
            count = len(asset_list.curselection())
            if count > 8:
                for index in asset_list.curselection()[8:]:
                    asset_list.selection_clear(index)
                count = 8
            selected_text.set(
                f'{count} logo' + (' geselecteerd' if count == 1 else '’s geselecteerd')
                if count else 'Nog geen logo’s geselecteerd'
            )

        def update_layout(_event=None):
            description.set(LAYOUT_INFO[layout.get()])

        def open_library():
            win.destroy()
            self.asset_library_dialog()

        def build_plan():
            selected = list(asset_list.curselection())[:8]
            if not selected:
                self.status.set('Selecteer eerst minimaal één sponsorlogo')
                return
            chosen = [assets[index] for index in selected]
            placements = sponsor_layout(layout.get(), len(chosen))
            group = f'sponsorplan-{time.time_ns()}'
            if replace_previous.get():
                self.logo_layers = [layer for layer in self.logo_layers
                                    if not layer.get('smartSponsorGroup')]
            start = len(self.logo_layers)
            for asset, placement in zip(chosen, placements):
                path = ASSET_DIR / asset['filename']
                layer = self.normalise_layer({
                    'id': f"{group}-{asset.get('id', len(self.logo_layers))}",
                    'name': asset.get('name', path.stem), 'path': str(path),
                    'position': placement['position'], 'scale': placement['scale'],
                    'rotation': placement['rotation'],
                    'offsetX': placement['offsetX'], 'offsetY': placement['offsetY'],
                    'opacity': 100, 'visible': True, 'locked': False,
                    'smartSponsorGroup': group, 'smartSponsorLayout': layout.get(),
                    'mirrorPairedOffsets': placement['position'] == 'Side panels (paired)',
                })
                self.logo_layers.append(layer)
            win.destroy()
            self.commit_layers(start, f'Sponsorplan gemaakt met {len(chosen)} logo’s')

        asset_list.bind('<<ListboxSelect>>', update_selection)
        combo.bind('<<ComboboxSelected>>', update_layout)

        actions = tk.Frame(win, bg=COLORS.PANEL)
        actions.pack(fill='x', padx=24, pady=16)
        if not assets:
            asset_list.insert('end', 'Je bibliotheek is nog leeg.')
            asset_list.configure(state='disabled')
            tk.Button(actions, text='EERST LOGO’S IMPORTEREN', command=open_library,
                      font=('Segoe UI', 10, 'bold'), fg=COLORS.NAVY,
                      bg='#F9A8D4', relief='flat', pady=10).pack(
                          side='left', fill='x', expand=True, padx=(0, 5))
        else:
            tk.Button(actions, text='SPONSORPLAN MAKEN', command=build_plan,
                      font=('Segoe UI', 10, 'bold'), fg=COLORS.NAVY,
                      bg='#86EFAC', relief='flat', pady=10).pack(
                          side='left', fill='x', expand=True, padx=(0, 5))
        tk.Button(actions, text='ANNULEREN', command=win.destroy,
                  font=('Segoe UI', 10, 'bold'), fg=COLORS.TEXT,
                  bg='#475569', relief='flat', pady=10).pack(
                      side='left', fill='x', expand=True, padx=(5, 0))
        if assets:
            asset_list.focus_set()


if __name__ == '__main__':
    StudioV18().mainloop()
