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

ROOT=Path(__file__).resolve().parents[2]
BASE_PATH=ROOT/'outputs'/'RaceCore_Studio_UI_v0.7'/'racecore_studio.py'
spec=importlib.util.spec_from_file_location('racecore_v07',BASE_PATH); base=importlib.util.module_from_spec(spec); spec.loader.exec_module(base)

APP=Path(__file__).resolve().parent; GENERATED=APP/'generated'; AUTOSAVE=APP/'autosave.racecore.json'
LIVE_CONFIG=APP/'live-link-ui.json'

PRESETS={
    'Clean Center': {'include':['center-stripe-left','center-stripe-right'],'islands':['roof-and-engine-cover']},
    'Center + Sills': {'include':['center-stripe-left','center-stripe-right','side-sill-signature'],'islands':['roof-and-engine-cover','left-side-sill','right-side-sill']},
    'Full Seam Proof': {'include':['center-stripe-left','center-stripe-right','side-sill-signature'],'islands':None},
}

class StudioV08(base.Studio):
    def __init__(self):
        self.history=[]; self.future=[]; self.loading=False
        super().__init__()
        self.title('RaceCore Livery Studio v0.8 - Ferrari 296')
        if AUTOSAVE.exists(): self.load_path(AUTOSAVE,record=False)
        else: self.record_state()

    def build_layout(self):
        self.preset=tk.StringVar(value='Clean Center')
        header=tk.Frame(self,bg=base.NAVY,height=72); header.pack(fill='x'); header.pack_propagate(False)
        tk.Label(header,text='RACECORE',font=('Segoe UI',20,'bold'),fg=base.CYAN,bg=base.NAVY).pack(side='left',padx=(28,12))
        tk.Label(header,text='Livery Studio  v0.8',font=('Segoe UI',16),fg=base.TEXT,bg=base.NAVY).pack(side='left')
        tk.Label(header,text='Ferrari 296 LMGT3 EVO',font=('Segoe UI',11),fg=base.MUTED,bg=base.NAVY).pack(side='right',padx=28)
        toolbar=tk.Frame(self,bg='#0b1730',height=42); toolbar.pack(fill='x'); toolbar.pack_propagate(False)
        for text,cmd in [('Open',self.open_project),('Save',self.save_project),('Undo',self.undo),('Redo',self.redo)]:
            tk.Button(toolbar,text=text,command=cmd,font=('Segoe UI',9),fg=base.TEXT,bg='#1e3152',activebackground='#334b70',relief='flat',padx=14).pack(side='left',padx=(8 if text=='Open' else 2,0),pady=6)
        tk.Label(toolbar,text='Autosave aan',font=('Segoe UI',9),fg='#86efac',bg='#0b1730').pack(side='right',padx=18)

        body=tk.Frame(self,bg=base.NAVY); body.pack(fill='both',expand=True,padx=20,pady=(12,14))
        controls=tk.Frame(body,bg=base.PANEL,width=320); controls.pack(side='left',fill='y',padx=(0,14)); controls.pack_propagate(False)
        viewer=tk.Frame(body,bg='#050b18'); viewer.pack(side='left',fill='both',expand=True)
        self.preview_label=tk.Label(viewer,text='Preview wordt opgebouwd...',font=('Segoe UI',12),fg=base.MUTED,bg='#050b18'); self.preview_label.pack(fill='both',expand=True,padx=12,pady=12)
        self.section(controls,'LAK'); self.colour_row_v08(controls,'Basiskleur',self.primary); self.colour_row_v08(controls,'Accentkleur',self.accent)
        self.section(controls,'DESIGN PRESET')
        combo=ttk.Combobox(controls,textvariable=self.preset,values=list(PRESETS),state='readonly',font=('Segoe UI',10)); combo.pack(fill='x',padx=20,pady=(0,10)); combo.bind('<<ComboboxSelected>>',lambda e:self.changed())
        tk.Label(controls,text='Clean Center voorkomt losse UV-fragmenten rond spiegel en spatbord.',wraplength=270,justify='left',font=('Segoe UI',9),fg=base.MUTED,bg=base.PANEL).pack(anchor='w',padx=20)
        self.section(controls,'VECTOR STRIPES')
        widthrow=tk.Frame(controls,bg=base.PANEL); widthrow.pack(fill='x',padx=20)
        tk.Label(widthrow,text='Lijnbreedte',font=('Segoe UI',10),fg=base.TEXT,bg=base.PANEL).pack(side='left')
        self.width_label=tk.Label(widthrow,text='100%',font=('Segoe UI',10,'bold'),fg=base.CYAN,bg=base.PANEL); self.width_label.pack(side='right')
        tk.Scale(controls,from_=50,to=170,orient='horizontal',variable=self.width,command=self.width_changed,bg=base.PANEL,fg=base.TEXT,troughcolor='#253656',highlightthickness=0,activebackground=base.CYAN,showvalue=False).pack(fill='x',padx=16)
        tk.Label(controls,text='✓ Nummerplaten   ✓ Class stickers\n✓ Car stickers       ✓ Displaypanelen',justify='left',font=('Segoe UI',10),fg='#86efac',bg=base.PANEL).pack(anchor='w',padx=20,pady=16)
        tk.Frame(controls,bg=base.PANEL).pack(fill='both',expand=True)
        tk.Button(controls,text='PREVIEW',command=lambda:self.generate(False),font=('Segoe UI',11,'bold'),fg=base.TEXT,bg='#334155',relief='flat',pady=11).pack(fill='x',padx=18,pady=8)
        tk.Button(controls,text='APPLY TO LMU',command=lambda:self.generate(True),font=('Segoe UI',11,'bold'),fg=base.NAVY,bg=base.CYAN,relief='flat',pady=13).pack(fill='x',padx=18,pady=(0,20))
        footer=tk.Frame(self,bg='#0b1730',height=44); footer.pack(fill='x'); footer.pack_propagate(False)
        tk.Label(footer,textvariable=self.status,font=('Segoe UI',10),fg=base.MUTED,bg='#0b1730').pack(side='left',padx=22)
        tk.Label(footer,text='LIVE LINK  ●',font=('Segoe UI',10,'bold'),fg='#4ade80',bg='#0b1730').pack(side='right',padx=22)

    def colour_row_v08(self,parent,label,var):
        row=tk.Frame(parent,bg=base.PANEL); row.pack(fill='x',padx=20,pady=6)
        tk.Label(row,text=label,font=('Segoe UI',10),fg=base.TEXT,bg=base.PANEL).pack(side='left')
        button=tk.Button(row,textvariable=var,font=('Consolas',9,'bold'),width=11,relief='flat',command=lambda:self.pick_v08(var,button)); button.pack(side='right')
        self.update_swatch(button,var.get())

    def update_swatch(self,button,colour):
        r,g,b=base.rgb(colour); fg='#000000' if (r*299+g*587+b*114)>150000 else '#ffffff'; button.configure(bg=colour,fg=fg,activebackground=colour,activeforeground=fg)

    def pick_v08(self,var,button):
        value=colorchooser.askcolor(var.get(),title='Kies RaceCore-kleur')[1]
        if value: var.set(value.upper()); self.update_swatch(button,var.get()); self.changed()

    def width_changed(self,_=None): self.width_label.configure(text=f'{self.width.get()}%'); self.schedule_preview()
    def changed(self): self.record_state(); self.autosave(); self.schedule_preview()

    def snapshot(self): return {'schemaVersion':1,'primary':self.primary.get(),'accent':self.accent.get(),'width':self.width.get(),'preset':self.preset.get()}
    def apply_state(self,state,record=False):
        self.loading=True; self.primary.set(state['primary']); self.accent.set(state['accent']); self.width.set(state['width']); self.preset.set(state.get('preset','Clean Center')); self.loading=False
        if record:self.record_state(); self.generate(False)
    def record_state(self):
        if self.loading:return
        s=self.snapshot()
        if not self.history or self.history[-1]!=s:self.history.append(s); self.future.clear()
    def undo(self):
        if len(self.history)<2:return
        self.future.append(self.history.pop()); self.apply_state(self.history[-1]); self.autosave()
    def redo(self):
        if not self.future:return
        s=self.future.pop(); self.history.append(s); self.apply_state(s); self.autosave()
    def autosave(self): AUTOSAVE.write_text(json.dumps(self.snapshot(),indent=2),encoding='utf-8')
    def save_project(self):
        path=filedialog.asksaveasfilename(defaultextension='.racecore.json',filetypes=[('RaceCore project','*.racecore.json')])
        if path: Path(path).write_text(json.dumps(self.snapshot(),indent=2),encoding='utf-8'); self.status.set('Project opgeslagen')
    def open_project(self):
        path=filedialog.askopenfilename(filetypes=[('RaceCore project','*.racecore.json'),('JSON','*.json')])
        if path:self.load_path(Path(path),record=True)
    def load_path(self,path,record=True):
        try:self.apply_state(json.loads(path.read_text(encoding='utf-8')),record=record); self.autosave()
        except Exception as e:messagebox.showerror('RaceCore',str(e))

    def _worker(self,apply):
        try:
            GENERATED.mkdir(parents=True,exist_ok=True); cfg=json.loads(base.SEAM_PROJECT.read_text(encoding='utf-8'))
            source=Image.open(base.SOURCE).convert('RGB'); size=source.size; body=base.load_l('carbon.png'); protected=Image.new('L',size,0)
            for n in ['number_plates.png','class_stickers.png','car_stickers.png','display_panels.png']:protected=ImageChops.lighter(protected,base.load_l(n))
            editable=ImageChops.subtract(body,protected); gray=source.convert('L'); shade=Image.merge('RGB',(gray,gray,gray))
            canvas=Image.composite(ImageEnhance.Brightness(ImageChops.multiply(Image.new('RGB',size,base.rgb(self.primary.get())),shade)).enhance(2.15),source,editable)
            lm=Image.new('L',size,0); preset=PRESETS[self.preset.get()]; scale=self.width.get()/100
            for graph in cfg['seamGraph']:
                if graph['designId'] not in preset['include']:continue
                for segment in graph['segments']:
                    if preset['islands'] is not None and segment['island'] not in preset['islands']:continue
                    ImageDraw.Draw(lm).line([tuple(p) for p in segment['points']],fill=255,width=max(2,round(segment['width']*scale)),joint='curve')
            lm=ImageChops.multiply(lm,editable); canvas=Image.composite(Image.new('RGB',size,base.rgb(self.accent.get())),canvas,lm); canvas=Image.composite(source,canvas,protected)
            canvas.save(GENERATED/'customskin.tga',format='TGA',compression=None); Image.new('RGB',size,(0,0,0)).save(GENERATED/'customskin_region.tga',format='TGA',compression=None)
            p=canvas.copy(); p.thumbnail((880,680)); p.save(GENERATED/'preview.png',optimize=True); self.autosave()
            label='Preview gereed'
            if apply:
                result=subprocess.run([sys.executable,str(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))

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