from pathlib import Path
from PIL import Image, ImageTk, ImageChops
import copy, importlib.util, json, subprocess, sys, tkinter as tk
from tkinter import filedialog, messagebox, ttk

ROOT=Path(__file__).resolve().parents[2]; APP=Path(__file__).resolve().parent; GENERATED=APP/'generated'
V09=ROOT/'outputs'/'RaceCore_Studio_UI_v0.9'/'racecore_studio_v09.py'
spec=importlib.util.spec_from_file_location('racecore_v09',V09); v09=importlib.util.module_from_spec(spec); spec.loader.exec_module(v09)
LIVE_CONFIG=APP/'live-link-ui.json'
GPU_BRIDGE=ROOT/'outputs'/'GPU_Placement_Proof_v2.0'/'gpu-placement.json'

class StudioV10(v09.StudioV09):
    def __init__(self):
        self.logo_layers=[]; self.gpu_bridge_mtime=0
        super().__init__(); self.title('RaceCore Livery Studio v1.0 - Ferrari 296')
        self.after(600,self.poll_gpu_bridge)

    def poll_gpu_bridge(self):
        try:
            if GPU_BRIDGE.exists():
                stamp=GPU_BRIDGE.stat().st_mtime_ns
                if stamp>self.gpu_bridge_mtime:
                    self.gpu_bridge_mtime=stamp; data=json.loads(GPU_BRIDGE.read_text(encoding='utf-8')); incoming=data['layer']
                    path=Path(incoming['path'])
                    if path.exists():
                        self.logo_layers.append({'id':f'gpu-{stamp}','name':path.stem,'path':str(path),'position':incoming['position'],'scale':incoming['scale'],'rotation':incoming.get('rotation',0),'visible':True})
                        self.refresh_layers(len(self.logo_layers)-1); self.record_state(); self.status.set('GPU-laag ontvangen · preview wordt opgebouwd'); self.changed()
        except Exception as err:self.status.set('GPU Bridge: '+str(err))
        self.after(600,self.poll_gpu_bridge)

    def build_layout(self):
        super().build_layout()
        # De v0.9 Sponsor/Logo-knop roept door polymorfisme deze Layer Manager aan.
        header=self.winfo_children()[0]
        for child in header.winfo_children():
            if isinstance(child,tk.Label) and 'v0.8' in child.cget('text'): child.configure(text='Livery Studio  v1.0')

    def logo_dialog(self):
        win=tk.Toplevel(self); win.title('Layer Manager'); win.geometry('650x470'); win.configure(bg=v09.v08.base.PANEL); win.transient(self); win.grab_set()
        tk.Label(win,text='LAYER MANAGER',font=('Segoe UI',14,'bold'),fg=v09.v08.base.CYAN,bg=v09.v08.base.PANEL).pack(anchor='w',padx=22,pady=(18,10))
        frame=tk.Frame(win,bg=v09.v08.base.PANEL); frame.pack(fill='both',expand=True,padx=22)
        self.layer_list=tk.Listbox(frame,font=('Segoe UI',10),bg='#08152c',fg=v09.v08.base.TEXT,selectbackground='#2563eb',relief='flat',activestyle='none')
        self.layer_list.pack(side='left',fill='both',expand=True)
        side=tk.Frame(frame,bg=v09.v08.base.PANEL,width=150); side.pack(side='right',fill='y',padx=(12,0))
        for text,cmd,colour in [
            ('+ Logo toevoegen',self.add_layer,v09.v08.base.CYAN),('Bewerken',self.edit_layer,'#334155'),('Dupliceren',self.duplicate_layer,'#334155'),
            ('Omhoog',lambda:self.move_layer(-1),'#334155'),('Omlaag',lambda:self.move_layer(1),'#334155'),('Tonen/verbergen',self.toggle_layer,'#334155'),('Verwijderen',self.delete_layer,'#7f1d1d')]:
            tk.Button(side,text=text,command=cmd,font=('Segoe UI',9,'bold'),fg=('#07152f' if colour==v09.v08.base.CYAN else '#f8fafc'),bg=colour,relief='flat',pady=8).pack(fill='x',pady=(0,6))
        tk.Button(win,text='SLUITEN & PREVIEW',command=lambda:(self.changed(),win.destroy()),font=('Segoe UI',10,'bold'),fg=v09.v08.base.NAVY,bg=v09.v08.base.CYAN,relief='flat',pady=10).pack(fill='x',padx=22,pady=16)
        self.refresh_layers()

    def refresh_layers(self,select=None):
        if not hasattr(self,'layer_list'):return
        self.layer_list.delete(0,'end')
        for i,layer in enumerate(self.logo_layers):
            eye='●' if layer.get('visible',True) else '○'; paired='  ↔' if layer['position']=='Side panels (paired)' else ''
            self.layer_list.insert('end',f"{eye}  {i+1:02d}  {layer['name']}  ·  {layer['position']}  ·  {layer['scale']}%{paired}")
        if self.logo_layers:
            idx=min(select if select is not None else len(self.logo_layers)-1,len(self.logo_layers)-1); self.layer_list.selection_set(idx); self.layer_list.see(idx)

    def selected(self):
        s=self.layer_list.curselection() if hasattr(self,'layer_list') else (); return s[0] if s else None

    def add_layer(self):
        path=filedialog.askopenfilename(filetypes=[('Transparante PNG','*.png')])
        if not path:return
        try:
            im=Image.open(path)
            if 'A' not in im.getbands():messagebox.showwarning('RaceCore','Deze PNG heeft geen transparantiekanaal.')
            self.logo_layers.append({'id':f'logo-{len(self.logo_layers)+1}','name':Path(path).stem,'path':path,'position':'Roof / engine cover','scale':55,'visible':True})
            self.refresh_layers(); self.record_state()
        except Exception as e:messagebox.showerror('RaceCore',str(e))

    def edit_layer(self):
        idx=self.selected()
        if idx is None:return
        layer=self.logo_layers[idx]; dlg=tk.Toplevel(self); dlg.title('Laag bewerken'); dlg.geometry('390x300'); dlg.configure(bg=v09.v08.base.PANEL); dlg.transient(self); dlg.grab_set()
        tk.Label(dlg,text=layer['name'],font=('Segoe UI',12,'bold'),fg=v09.v08.base.TEXT,bg=v09.v08.base.PANEL).pack(anchor='w',padx=20,pady=(18,12))
        pos=tk.StringVar(value=layer['position']); ttk.Combobox(dlg,textvariable=pos,values=list(v09.PLACEMENTS),state='readonly').pack(fill='x',padx=20,pady=6)
        scale=tk.IntVar(value=layer['scale']); tk.Scale(dlg,from_=20,to=120,variable=scale,orient='horizontal',bg=v09.v08.base.PANEL,fg=v09.v08.base.TEXT,troughcolor='#253656',highlightthickness=0).pack(fill='x',padx=16,pady=8)
        visible=tk.BooleanVar(value=layer.get('visible',True)); tk.Checkbutton(dlg,text='Laag zichtbaar',variable=visible,font=('Segoe UI',10),fg=v09.v08.base.TEXT,bg=v09.v08.base.PANEL,selectcolor='#253656').pack(anchor='w',padx=16)
        def done(): layer.update(position=pos.get(),scale=scale.get(),visible=visible.get()); self.refresh_layers(idx); self.record_state(); dlg.destroy()
        tk.Button(dlg,text='OPSLAAN',command=done,font=('Segoe UI',10,'bold'),fg=v09.v08.base.NAVY,bg=v09.v08.base.CYAN,relief='flat',pady=10).pack(fill='x',padx=20,pady=16)

    def duplicate_layer(self):
        idx=self.selected()
        if idx is None:return
        item=copy.deepcopy(self.logo_layers[idx]); item['id']=f"{item['id']}-copy"; item['name']+=' kopie'; self.logo_layers.insert(idx+1,item); self.refresh_layers(idx+1); self.record_state()
    def delete_layer(self):
        idx=self.selected()
        if idx is not None:self.logo_layers.pop(idx); self.refresh_layers(max(0,idx-1)); self.record_state()
    def toggle_layer(self):
        idx=self.selected()
        if idx is not None:self.logo_layers[idx]['visible']=not self.logo_layers[idx].get('visible',True); self.refresh_layers(idx); self.record_state()
    def move_layer(self,delta):
        idx=self.selected()
        if idx is None:return
        new=max(0,min(len(self.logo_layers)-1,idx+delta))
        if new!=idx:self.logo_layers.insert(new,self.logo_layers.pop(idx)); self.refresh_layers(new); self.record_state()

    def snapshot(self):
        s=super().snapshot(); s['logoLayers']=copy.deepcopy(self.logo_layers); s['logo']={'enabled':False,'path':'','position':'Roof / engine cover','scale':55}; return s
    def apply_state(self,state,record=False):
        self.logo_layers=copy.deepcopy(state.get('logoLayers',[])); super().apply_state(state,record)

    def new_blank_project(self):
        self.logo_layers=[]; super().new_blank_project()

    def _worker(self,apply):
        try:
            # v0.9 bouwt lak/stripes/blank canvas; legacy single-logo blijft uit.
            old_enabled=self.logo_enabled; self.logo_enabled=False
            try: super()._worker(False)
            finally:self.logo_enabled=old_enabled
            canvas=Image.open(v09.GENERATED/'customskin.tga').convert('RGBA'); size=canvas.size
            editable=ImageChops.subtract(v09.v08.base.load_l('carbon.png'),self.protected_mask(size))
            for layer_data in self.logo_layers:
                if not layer_data.get('visible',True) or not Path(layer_data['path']).exists():continue
                logo=Image.open(layer_data['path']).convert('RGBA'); maxw=round(720*layer_data['scale']/100); logo.thumbnail((maxw,round(maxw*.65)),Image.Resampling.LANCZOS)
                for cx,cy,rotation in v09.PLACEMENTS[layer_data['position']]:
                    item=logo.rotate(rotation+layer_data.get('rotation',0),expand=True,resample=Image.Resampling.BICUBIC); overlay=Image.new('RGBA',size,(0,0,0,0)); overlay.alpha_composite(item,(round(cx-item.width/2),round(cy-item.height/2)))
                    alpha=ImageChops.multiply(overlay.getchannel('A'),editable); overlay.putalpha(alpha); canvas=Image.alpha_composite(canvas,overlay)
            GENERATED.mkdir(parents=True,exist_ok=True); rgb=canvas.convert('RGB'); rgb.save(GENERATED/'customskin.tga',format='TGA',compression=None); Image.new('RGB',size,(0,0,0)).save(GENERATED/'customskin_region.tga',format='TGA',compression=None)
            preview=rgb.copy(); preview.thumbnail((880,680)); preview.save(GENERATED/'preview.png',optimize=True)
            label=f"Preview gereed · {sum(1 for x in self.logo_layers if x.get('visible',True))} zichtbare lagen"
            if apply:
                result=subprocess.run([sys.executable,str(v09.v08.base.LIVE_LINK),str(LIVE_CONFIG)],capture_output=True,text=True,timeout=60)
                if result.returncode:raise RuntimeError(result.stderr.strip() or result.stdout.strip())
                event=json.loads(result.stdout); label='Live in LMU' if event['status']=='installed' else 'LMU is al bijgewerkt'
            self.after(0,lambda:self.show_preview(label))
        except Exception as e:self.after(0,lambda text=str(e):self.show_error(text))

    def show_preview(self,label):
        path=GENERATED/'preview.png'
        if not path.exists():return
        im=Image.open(path); self.preview_photo=ImageTk.PhotoImage(im); self.preview_label.configure(image=self.preview_photo,text=''); self.status.set(label)

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