Skip to main content

proof_engine/editor/
prefab.rs

1// prefab.rs — Prefab system for proof-engine editor
2// Prefabs are reusable entity templates with overrides, nested prefabs,
3// variant management, instance diff tracking, and automatic update propagation.
4
5use std::collections::HashMap;
6use std::fmt;
7
8// ─── Prefab identifier ────────────────────────────────────────────────────────
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct PrefabId(pub u32);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct PrefabInstanceId(pub u32);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct ComponentId(pub u32);
18
19// ─── Component data ──────────────────────────────────────────────────────────
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum ComponentValue {
23    Bool(bool),
24    Int(i64),
25    Float(f64),
26    Vec2([f64; 2]),
27    Vec3([f64; 3]),
28    Vec4([f64; 4]),
29    String(String),
30    EntityRef(u32),
31    PrefabRef(PrefabId),
32    List(Vec<ComponentValue>),
33    Map(Vec<(String, ComponentValue)>),
34}
35
36impl ComponentValue {
37    pub fn type_name(&self) -> &'static str {
38        match self {
39            Self::Bool(_)     => "bool",
40            Self::Int(_)      => "int",
41            Self::Float(_)    => "float",
42            Self::Vec2(_)     => "vec2",
43            Self::Vec3(_)     => "vec3",
44            Self::Vec4(_)     => "vec4",
45            Self::String(_)   => "string",
46            Self::EntityRef(_) => "entity_ref",
47            Self::PrefabRef(_) => "prefab_ref",
48            Self::List(_)     => "list",
49            Self::Map(_)      => "map",
50        }
51    }
52
53    pub fn is_same_type(&self, other: &Self) -> bool {
54        self.type_name() == other.type_name()
55    }
56
57    pub fn as_float(&self) -> Option<f64> {
58        match self {
59            Self::Float(v) => Some(*v),
60            Self::Int(v)   => Some(*v as f64),
61            Self::Bool(v)  => Some(if *v { 1.0 } else { 0.0 }),
62            _ => None,
63        }
64    }
65
66    pub fn lerp(&self, other: &Self, t: f64) -> Option<Self> {
67        match (self, other) {
68            (Self::Float(a), Self::Float(b)) => Some(Self::Float(a + (b - a) * t)),
69            (Self::Int(a), Self::Int(b)) => Some(Self::Int(*a + ((*b - *a) as f64 * t) as i64)),
70            (Self::Vec2(a), Self::Vec2(b)) => Some(Self::Vec2([
71                a[0] + (b[0] - a[0]) * t,
72                a[1] + (b[1] - a[1]) * t,
73            ])),
74            (Self::Vec3(a), Self::Vec3(b)) => Some(Self::Vec3([
75                a[0] + (b[0] - a[0]) * t,
76                a[1] + (b[1] - a[1]) * t,
77                a[2] + (b[2] - a[2]) * t,
78            ])),
79            (Self::Vec4(a), Self::Vec4(b)) => Some(Self::Vec4([
80                a[0] + (b[0] - a[0]) * t,
81                a[1] + (b[1] - a[1]) * t,
82                a[2] + (b[2] - a[2]) * t,
83                a[3] + (b[3] - a[3]) * t,
84            ])),
85            _ => None,
86        }
87    }
88}
89
90impl fmt::Display for ComponentValue {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::Bool(v)  => write!(f, "{}", v),
94            Self::Int(v)   => write!(f, "{}", v),
95            Self::Float(v) => write!(f, "{:.4}", v),
96            Self::Vec2(v)  => write!(f, "({:.3},{:.3})", v[0], v[1]),
97            Self::Vec3(v)  => write!(f, "({:.3},{:.3},{:.3})", v[0], v[1], v[2]),
98            Self::Vec4(v)  => write!(f, "({:.3},{:.3},{:.3},{:.3})", v[0],v[1],v[2],v[3]),
99            Self::String(s) => write!(f, "\"{}\"", s),
100            Self::EntityRef(id) => write!(f, "entity:{}", id),
101            Self::PrefabRef(id) => write!(f, "prefab:{}", id.0),
102            Self::List(items) => {
103                write!(f, "[")?;
104                for (i, item) in items.iter().enumerate() {
105                    if i > 0 { write!(f, ",")?; }
106                    write!(f, "{}", item)?;
107                }
108                write!(f, "]")
109            }
110            Self::Map(entries) => {
111                write!(f, "{{")?;
112                for (i, (k, v)) in entries.iter().enumerate() {
113                    if i > 0 { write!(f, ",")?; }
114                    write!(f, "{}:{}", k, v)?;
115                }
116                write!(f, "}}")
117            }
118        }
119    }
120}
121
122// ─── Component definition ─────────────────────────────────────────────────────
123
124#[derive(Debug, Clone)]
125pub struct ComponentDef {
126    pub id: ComponentId,
127    pub name: String,
128    pub category: String,
129    pub fields: Vec<FieldDef>,
130    pub version: u32,
131    pub deprecated: bool,
132    pub description: &'static str,
133}
134
135#[derive(Debug, Clone)]
136pub struct FieldDef {
137    pub name: String,
138    pub default_value: ComponentValue,
139    pub description: &'static str,
140    pub min: Option<f64>,
141    pub max: Option<f64>,
142    pub hidden_in_editor: bool,
143    pub read_only: bool,
144    pub animatable: bool,
145}
146
147impl FieldDef {
148    pub fn new(name: &str, default: ComponentValue) -> Self {
149        Self {
150            name: name.to_string(),
151            default_value: default,
152            description: "",
153            min: None,
154            max: None,
155            hidden_in_editor: false,
156            read_only: false,
157            animatable: false,
158        }
159    }
160
161    pub fn float(name: &str, default: f64) -> Self {
162        Self::new(name, ComponentValue::Float(default))
163    }
164
165    pub fn vec3(name: &str, x: f64, y: f64, z: f64) -> Self {
166        Self::new(name, ComponentValue::Vec3([x, y, z]))
167    }
168
169    pub fn bool(name: &str, default: bool) -> Self {
170        Self::new(name, ComponentValue::Bool(default))
171    }
172
173    pub fn string(name: &str, default: &str) -> Self {
174        Self::new(name, ComponentValue::String(default.to_string()))
175    }
176}
177
178// ─── Component instance ───────────────────────────────────────────────────────
179
180#[derive(Debug, Clone)]
181pub struct ComponentInstance {
182    pub def_id: ComponentId,
183    pub values: HashMap<String, ComponentValue>,
184    pub enabled: bool,
185}
186
187impl ComponentInstance {
188    pub fn new(def_id: ComponentId) -> Self {
189        Self { def_id, values: HashMap::new(), enabled: true }
190    }
191
192    pub fn get(&self, field: &str) -> Option<&ComponentValue> {
193        self.values.get(field)
194    }
195
196    pub fn set(&mut self, field: &str, value: ComponentValue) {
197        self.values.insert(field.to_string(), value);
198    }
199
200    pub fn from_def(def: &ComponentDef) -> Self {
201        let mut inst = Self::new(def.id);
202        for field in &def.fields {
203            inst.values.insert(field.name.clone(), field.default_value.clone());
204        }
205        inst
206    }
207}
208
209// ─── Prefab node ─────────────────────────────────────────────────────────────
210
211#[derive(Debug, Clone)]
212pub struct PrefabNode {
213    pub name: String,
214    pub local_position: [f64; 3],
215    pub local_rotation: [f64; 4],  // quaternion xyzw
216    pub local_scale:    [f64; 3],
217    pub components: Vec<ComponentInstance>,
218    pub children: Vec<PrefabNode>,
219    pub tags: Vec<String>,
220    pub active: bool,
221    pub static_: bool,
222    pub layer: u32,
223    pub nested_prefab: Option<(PrefabId, Vec<PrefabOverride>)>,
224}
225
226impl PrefabNode {
227    pub fn new(name: &str) -> Self {
228        Self {
229            name: name.to_string(),
230            local_position: [0.0, 0.0, 0.0],
231            local_rotation: [0.0, 0.0, 0.0, 1.0],
232            local_scale: [1.0, 1.0, 1.0],
233            components: Vec::new(),
234            children: Vec::new(),
235            tags: Vec::new(),
236            active: true,
237            static_: false,
238            layer: 0,
239            nested_prefab: None,
240        }
241    }
242
243    pub fn add_component(&mut self, inst: ComponentInstance) {
244        self.components.push(inst);
245    }
246
247    pub fn add_child(&mut self, child: PrefabNode) {
248        self.children.push(child);
249    }
250
251    pub fn find_child(&self, name: &str) -> Option<&PrefabNode> {
252        for child in &self.children {
253            if child.name == name { return Some(child); }
254            if let Some(found) = child.find_child(name) { return Some(found); }
255        }
256        None
257    }
258
259    pub fn find_child_mut(&mut self, name: &str) -> Option<&mut PrefabNode> {
260        // Linear search: first check direct children, then recurse
261        let idx = self.children.iter().position(|c| c.name == name);
262        if let Some(i) = idx {
263            return Some(&mut self.children[i]);
264        }
265        for child in &mut self.children {
266            if let Some(found) = child.find_child_mut(name) { return Some(found); }
267        }
268        None
269    }
270
271    pub fn total_node_count(&self) -> usize {
272        1 + self.children.iter().map(|c| c.total_node_count()).sum::<usize>()
273    }
274
275    pub fn depth(&self) -> u32 {
276        1 + self.children.iter().map(|c| c.depth()).max().unwrap_or(0)
277    }
278}
279
280// ─── Override ─────────────────────────────────────────────────────────────────
281
282/// Describes a single property override on a prefab instance
283#[derive(Debug, Clone)]
284pub struct PrefabOverride {
285    pub path: String,       // dot-separated path: "Root/LeftArm/Hand.Rigidbody.mass"
286    pub value: ComponentValue,
287    pub added: bool,        // true if this field/component/node was added (not in base)
288    pub removed: bool,      // true if this was removed from base
289}
290
291impl PrefabOverride {
292    pub fn set(path: &str, value: ComponentValue) -> Self {
293        Self {
294            path: path.to_string(),
295            value,
296            added: false,
297            removed: false,
298        }
299    }
300
301    pub fn add(path: &str, value: ComponentValue) -> Self {
302        Self {
303            path: path.to_string(),
304            value,
305            added: true,
306            removed: false,
307        }
308    }
309
310    pub fn remove(path: &str) -> Self {
311        Self {
312            path: path.to_string(),
313            value: ComponentValue::Bool(false),
314            added: false,
315            removed: true,
316        }
317    }
318
319    pub fn node_path(&self) -> &str {
320        if let Some(dot) = self.path.rfind('.') {
321            &self.path[..dot]
322        } else {
323            &self.path
324        }
325    }
326
327    pub fn field_path(&self) -> Option<&str> {
328        self.path.rfind('.').map(|dot| &self.path[dot+1..])
329    }
330}
331
332// ─── Prefab variant ───────────────────────────────────────────────────────────
333
334#[derive(Debug, Clone)]
335pub struct PrefabVariant {
336    pub name: String,
337    pub description: String,
338    pub overrides: Vec<PrefabOverride>,
339    pub is_default: bool,
340    pub thumbnail_path: Option<String>,
341    pub tags: Vec<String>,
342}
343
344impl PrefabVariant {
345    pub fn new(name: &str) -> Self {
346        Self {
347            name: name.to_string(),
348            description: String::new(),
349            overrides: Vec::new(),
350            is_default: false,
351            thumbnail_path: None,
352            tags: Vec::new(),
353        }
354    }
355
356    pub fn default_variant() -> Self {
357        let mut v = Self::new("Default");
358        v.is_default = true;
359        v
360    }
361
362    pub fn add_override(&mut self, ov: PrefabOverride) {
363        // Replace existing override at same path
364        if let Some(existing) = self.overrides.iter_mut().find(|o| o.path == ov.path) {
365            *existing = ov;
366        } else {
367            self.overrides.push(ov);
368        }
369    }
370
371    pub fn remove_override(&mut self, path: &str) {
372        self.overrides.retain(|o| o.path != path);
373    }
374
375    pub fn get_override(&self, path: &str) -> Option<&PrefabOverride> {
376        self.overrides.iter().find(|o| o.path == path)
377    }
378
379    pub fn override_count(&self) -> usize { self.overrides.len() }
380}
381
382// ─── Prefab ───────────────────────────────────────────────────────────────────
383
384#[derive(Debug, Clone)]
385pub struct Prefab {
386    pub id: PrefabId,
387    pub name: String,
388    pub description: String,
389    pub author: String,
390    pub version: u32,
391    pub root: PrefabNode,
392    pub variants: Vec<PrefabVariant>,
393    pub active_variant: usize,
394    pub asset_path: Option<String>,
395    pub thumbnail_path: Option<String>,
396    pub tags: Vec<String>,
397    pub created_at: u64,
398    pub modified_at: u64,
399    pub is_locked: bool,
400}
401
402impl Prefab {
403    pub fn new(id: PrefabId, name: &str) -> Self {
404        Self {
405            id,
406            name: name.to_string(),
407            description: String::new(),
408            author: String::new(),
409            version: 1,
410            root: PrefabNode::new(name),
411            variants: vec![PrefabVariant::default_variant()],
412            active_variant: 0,
413            asset_path: None,
414            thumbnail_path: None,
415            tags: Vec::new(),
416            created_at: 0,
417            modified_at: 0,
418            is_locked: false,
419        }
420    }
421
422    pub fn add_variant(&mut self, variant: PrefabVariant) {
423        self.variants.push(variant);
424    }
425
426    pub fn active_variant(&self) -> &PrefabVariant {
427        &self.variants[self.active_variant.min(self.variants.len().saturating_sub(1))]
428    }
429
430    pub fn active_variant_mut(&mut self) -> &mut PrefabVariant {
431        let idx = self.active_variant.min(self.variants.len().saturating_sub(1));
432        &mut self.variants[idx]
433    }
434
435    pub fn set_active_variant(&mut self, name: &str) -> bool {
436        if let Some(idx) = self.variants.iter().position(|v| v.name == name) {
437            self.active_variant = idx;
438            return true;
439        }
440        false
441    }
442
443    pub fn node_count(&self) -> usize { self.root.total_node_count() }
444    pub fn depth(&self) -> u32 { self.root.depth() }
445
446    pub fn variant_count(&self) -> usize { self.variants.len() }
447
448    pub fn mark_modified(&mut self, time: u64) {
449        self.modified_at = time;
450        self.version += 1;
451    }
452}
453
454// ─── Prefab instance ─────────────────────────────────────────────────────────
455
456#[derive(Debug, Clone)]
457pub struct PrefabInstance {
458    pub id: PrefabInstanceId,
459    pub prefab_id: PrefabId,
460    pub variant_name: Option<String>,
461    pub entity_id: u32,
462    pub overrides: Vec<PrefabOverride>,
463    pub position: [f64; 3],
464    pub rotation: [f64; 4],
465    pub scale:    [f64; 3],
466    pub unlinked: bool,
467    pub parent_instance: Option<PrefabInstanceId>,
468}
469
470impl PrefabInstance {
471    pub fn new(id: PrefabInstanceId, prefab_id: PrefabId, entity_id: u32) -> Self {
472        Self {
473            id,
474            prefab_id,
475            variant_name: None,
476            entity_id,
477            overrides: Vec::new(),
478            position: [0.0, 0.0, 0.0],
479            rotation: [0.0, 0.0, 0.0, 1.0],
480            scale: [1.0, 1.0, 1.0],
481            unlinked: false,
482            parent_instance: None,
483        }
484    }
485
486    pub fn add_override(&mut self, ov: PrefabOverride) {
487        if let Some(existing) = self.overrides.iter_mut().find(|o| o.path == ov.path) {
488            *existing = ov;
489        } else {
490            self.overrides.push(ov);
491        }
492    }
493
494    pub fn clear_override(&mut self, path: &str) {
495        self.overrides.retain(|o| o.path != path);
496    }
497
498    pub fn clear_all_overrides(&mut self) {
499        self.overrides.clear();
500    }
501
502    pub fn has_overrides(&self) -> bool { !self.overrides.is_empty() }
503    pub fn override_count(&self) -> usize { self.overrides.len() }
504
505    pub fn is_override_at(&self, path: &str) -> bool {
506        self.overrides.iter().any(|o| o.path == path)
507    }
508
509    pub fn effective_value<'a>(&'a self, path: &str, prefab: &'a Prefab) -> Option<&'a ComponentValue> {
510        // Instance overrides take priority
511        if let Some(ov) = self.overrides.iter().find(|o| o.path == path) {
512            if !ov.removed { return Some(&ov.value); }
513            return None;
514        }
515        // Then variant overrides
516        let variant_name = self.variant_name.as_deref().unwrap_or("Default");
517        if let Some(variant) = prefab.variants.iter().find(|v| v.name == variant_name) {
518            if let Some(ov) = variant.overrides.iter().find(|o| o.path == path) {
519                if !ov.removed { return Some(&ov.value); }
520                return None;
521            }
522        }
523        // Then default variant
524        if let Some(default) = prefab.variants.iter().find(|v| v.is_default) {
525            if let Some(ov) = default.overrides.iter().find(|o| o.path == path) {
526                if !ov.removed { return Some(&ov.value); }
527            }
528        }
529        None
530    }
531}
532
533// ─── Diff detection ───────────────────────────────────────────────────────────
534
535#[derive(Debug, Clone)]
536pub struct PrefabDiff {
537    pub path: String,
538    pub kind: DiffKind,
539    pub base_value: Option<ComponentValue>,
540    pub instance_value: Option<ComponentValue>,
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq)]
544pub enum DiffKind {
545    Modified,
546    Added,
547    Removed,
548}
549
550impl fmt::Display for PrefabDiff {
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552        match self.kind {
553            DiffKind::Modified => write!(f, "~ {} : {:?} → {:?}", self.path,
554                self.base_value, self.instance_value),
555            DiffKind::Added   => write!(f, "+ {} : {:?}", self.path, self.instance_value),
556            DiffKind::Removed => write!(f, "- {} : {:?}", self.path, self.base_value),
557        }
558    }
559}
560
561pub fn diff_instance(instance: &PrefabInstance, _prefab: &Prefab) -> Vec<PrefabDiff> {
562    let mut diffs = Vec::new();
563    for ov in &instance.overrides {
564        diffs.push(PrefabDiff {
565            path: ov.path.clone(),
566            kind: if ov.added { DiffKind::Added }
567                  else if ov.removed { DiffKind::Removed }
568                  else { DiffKind::Modified },
569            base_value: None,
570            instance_value: if ov.removed { None } else { Some(ov.value.clone()) },
571        });
572    }
573    diffs
574}
575
576// ─── Prefab library ───────────────────────────────────────────────────────────
577
578#[derive(Debug, Clone)]
579pub struct PrefabLibrary {
580    prefabs: HashMap<PrefabId, Prefab>,
581    instances: HashMap<PrefabInstanceId, PrefabInstance>,
582    next_prefab_id: u32,
583    next_instance_id: u32,
584    pub search_index: Vec<(PrefabId, String, Vec<String>)>,
585}
586
587impl PrefabLibrary {
588    pub fn new() -> Self {
589        let mut lib = Self {
590            prefabs: HashMap::new(),
591            instances: HashMap::new(),
592            next_prefab_id: 1,
593            next_instance_id: 1,
594            search_index: Vec::new(),
595        };
596        lib.populate_defaults();
597        lib
598    }
599
600    fn populate_defaults(&mut self) {
601        // Primitive shapes
602        let shapes = [
603            ("Cube",     vec!["mesh", "primitive", "shape"]),
604            ("Sphere",   vec!["mesh", "primitive", "shape"]),
605            ("Cylinder", vec!["mesh", "primitive", "shape"]),
606            ("Capsule",  vec!["mesh", "primitive", "shape"]),
607            ("Plane",    vec!["mesh", "primitive", "flat"]),
608            ("Quad",     vec!["mesh", "primitive", "flat", "ui"]),
609        ];
610        for (name, tags) in &shapes {
611            let id = PrefabId(self.next_prefab_id);
612            self.next_prefab_id += 1;
613            let mut p = Prefab::new(id, name);
614            p.tags = tags.iter().map(|s| s.to_string()).collect();
615            p.tags.push("built-in".into());
616            self.prefabs.insert(id, p);
617        }
618        // Lights
619        let lights = [
620            ("Directional Light", vec!["light", "directional"]),
621            ("Point Light",       vec!["light", "point"]),
622            ("Spot Light",        vec!["light", "spot"]),
623            ("Area Light",        vec!["light", "area"]),
624        ];
625        for (name, tags) in &lights {
626            let id = PrefabId(self.next_prefab_id);
627            self.next_prefab_id += 1;
628            let mut p = Prefab::new(id, name);
629            p.tags = tags.iter().map(|s| s.to_string()).collect();
630            p.tags.push("built-in".into());
631            self.prefabs.insert(id, p);
632        }
633        // Cameras
634        let id = PrefabId(self.next_prefab_id);
635        self.next_prefab_id += 1;
636        let mut cam = Prefab::new(id, "Camera");
637        cam.tags = vec!["camera".into(), "built-in".into()];
638        self.prefabs.insert(id, cam);
639
640        // SDF body
641        let sdf_id = PrefabId(self.next_prefab_id);
642        self.next_prefab_id += 1;
643        let mut sdf = Prefab::new(sdf_id, "SDF Body");
644        sdf.tags = vec!["sdf".into(), "body".into(), "built-in".into()];
645        self.prefabs.insert(sdf_id, sdf);
646
647        // Force field
648        let ff_id = PrefabId(self.next_prefab_id);
649        self.next_prefab_id += 1;
650        let mut ff = Prefab::new(ff_id, "Force Field");
651        ff.tags = vec!["physics".into(), "force_field".into(), "built-in".into()];
652        self.prefabs.insert(ff_id, ff);
653
654        self.rebuild_index();
655    }
656
657    fn rebuild_index(&mut self) {
658        self.search_index = self.prefabs.iter()
659            .map(|(&id, p)| (id, p.name.clone(), p.tags.clone()))
660            .collect();
661    }
662
663    pub fn register(&mut self, prefab: Prefab) -> PrefabId {
664        let id = prefab.id;
665        self.prefabs.insert(id, prefab);
666        self.rebuild_index();
667        id
668    }
669
670    pub fn create(&mut self, name: &str) -> PrefabId {
671        let id = PrefabId(self.next_prefab_id);
672        self.next_prefab_id += 1;
673        self.prefabs.insert(id, Prefab::new(id, name));
674        self.rebuild_index();
675        id
676    }
677
678    pub fn remove(&mut self, id: PrefabId) -> Option<Prefab> {
679        let p = self.prefabs.remove(&id);
680        self.rebuild_index();
681        p
682    }
683
684    pub fn get(&self, id: PrefabId) -> Option<&Prefab> {
685        self.prefabs.get(&id)
686    }
687
688    pub fn get_mut(&mut self, id: PrefabId) -> Option<&mut Prefab> {
689        self.prefabs.get_mut(&id)
690    }
691
692    pub fn instantiate(&mut self, prefab_id: PrefabId, entity_id: u32) -> Option<PrefabInstanceId> {
693        if !self.prefabs.contains_key(&prefab_id) { return None; }
694        let id = PrefabInstanceId(self.next_instance_id);
695        self.next_instance_id += 1;
696        self.instances.insert(id, PrefabInstance::new(id, prefab_id, entity_id));
697        Some(id)
698    }
699
700    pub fn destroy_instance(&mut self, id: PrefabInstanceId) {
701        self.instances.remove(&id);
702    }
703
704    pub fn instance(&self, id: PrefabInstanceId) -> Option<&PrefabInstance> {
705        self.instances.get(&id)
706    }
707
708    pub fn instance_mut(&mut self, id: PrefabInstanceId) -> Option<&mut PrefabInstance> {
709        self.instances.get_mut(&id)
710    }
711
712    pub fn instances_of(&self, prefab_id: PrefabId) -> Vec<&PrefabInstance> {
713        self.instances.values().filter(|i| i.prefab_id == prefab_id).collect()
714    }
715
716    pub fn search(&self, query: &str) -> Vec<&Prefab> {
717        let q = query.to_lowercase();
718        self.prefabs.values()
719            .filter(|p| {
720                p.name.to_lowercase().contains(&q)
721                    || p.tags.iter().any(|t| t.to_lowercase().contains(&q))
722                    || p.description.to_lowercase().contains(&q)
723            })
724            .collect()
725    }
726
727    pub fn by_tag(&self, tag: &str) -> Vec<&Prefab> {
728        self.prefabs.values()
729            .filter(|p| p.tags.iter().any(|t| t == tag))
730            .collect()
731    }
732
733    pub fn prefab_count(&self) -> usize { self.prefabs.len() }
734    pub fn instance_count(&self) -> usize { self.instances.len() }
735
736    /// Apply an override to all instances of a prefab
737    pub fn propagate_change(&mut self, prefab_id: PrefabId, path: &str, value: ComponentValue) {
738        for inst in self.instances.values_mut() {
739            if inst.prefab_id == prefab_id && !inst.unlinked {
740                // Only propagate if not already overridden by the instance
741                if !inst.is_override_at(path) {
742                    // The base change will be reflected automatically since
743                    // effective_value checks the prefab base first
744                    let _ = (path, &value);
745                }
746            }
747        }
748    }
749
750    /// Unpack (unlink) an instance from its prefab
751    pub fn unlink_instance(&mut self, id: PrefabInstanceId) {
752        if let Some(inst) = self.instances.get_mut(&id) {
753            inst.unlinked = true;
754        }
755    }
756
757    /// Re-link an unlinked instance back to its prefab source
758    pub fn relink_instance(&mut self, id: PrefabInstanceId) {
759        if let Some(inst) = self.instances.get_mut(&id) {
760            inst.unlinked = false;
761        }
762    }
763
764    /// Create a new prefab from an existing entity (reverse workflow)
765    pub fn create_from_overrides(
766        &mut self,
767        name: &str,
768        instance_id: PrefabInstanceId,
769    ) -> Option<PrefabId> {
770        let base_id = self.instances.get(&instance_id)?.prefab_id;
771        let overrides = self.instances.get(&instance_id)?.overrides.clone();
772        let new_id = self.create(name);
773        if let Some(base) = self.prefabs.get(&base_id) {
774            let mut variant = PrefabVariant::new("FromInstance");
775            for ov in overrides { variant.add_override(ov); }
776            let base_clone = base.root.clone();
777            if let Some(new_prefab) = self.prefabs.get_mut(&new_id) {
778                new_prefab.root = base_clone;
779                new_prefab.variants.push(variant);
780            }
781        }
782        Some(new_id)
783    }
784
785    pub fn stats(&self) -> PrefabLibraryStats {
786        let total_nodes: usize = self.prefabs.values().map(|p| p.node_count()).sum();
787        let total_variants: usize = self.prefabs.values().map(|p| p.variant_count()).sum();
788        PrefabLibraryStats {
789            prefab_count: self.prefabs.len(),
790            instance_count: self.instances.len(),
791            total_nodes,
792            total_variants,
793            overridden_instances: self.instances.values().filter(|i| i.has_overrides()).count(),
794            unlinked_instances: self.instances.values().filter(|i| i.unlinked).count(),
795        }
796    }
797}
798
799#[derive(Debug, Clone)]
800pub struct PrefabLibraryStats {
801    pub prefab_count: usize,
802    pub instance_count: usize,
803    pub total_nodes: usize,
804    pub total_variants: usize,
805    pub overridden_instances: usize,
806    pub unlinked_instances: usize,
807}
808
809impl fmt::Display for PrefabLibraryStats {
810    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811        write!(f,
812            "Prefabs: {} ({} nodes, {} variants) | Instances: {} ({} overridden, {} unlinked)",
813            self.prefab_count, self.total_nodes, self.total_variants,
814            self.instance_count, self.overridden_instances, self.unlinked_instances,
815        )
816    }
817}
818
819impl Default for PrefabLibrary {
820    fn default() -> Self { Self::new() }
821}
822
823// ─── Prefab editor ────────────────────────────────────────────────────────────
824
825#[derive(Debug, Clone, Copy, PartialEq, Eq)]
826pub enum PrefabEditorMode {
827    Browse,
828    EditPrefab,
829    EditInstance,
830    VariantEditor,
831    DiffView,
832}
833
834#[derive(Debug)]
835pub struct PrefabEditor {
836    pub library: PrefabLibrary,
837    pub mode: PrefabEditorMode,
838    pub selected_prefab: Option<PrefabId>,
839    pub selected_instance: Option<PrefabInstanceId>,
840    pub selected_variant: Option<String>,
841    pub search_query: String,
842    pub filter_tag: Option<String>,
843    pub sort_by: PrefabSortMode,
844    pub show_built_in: bool,
845    pub show_variants: bool,
846    pub show_instances_panel: bool,
847    pub pending_delete: Option<PrefabId>,
848    undo_stack: Vec<PrefabEditAction>,
849    redo_stack: Vec<PrefabEditAction>,
850}
851
852#[derive(Debug, Clone, Copy, PartialEq, Eq)]
853pub enum PrefabSortMode {
854    Name,
855    RecentlyModified,
856    InstanceCount,
857    NodeCount,
858}
859
860#[derive(Debug, Clone)]
861pub enum PrefabEditAction {
862    CreatePrefab(PrefabId),
863    DeletePrefab(PrefabId),
864    AddVariant { prefab: PrefabId, variant_name: String },
865    RemoveVariant { prefab: PrefabId, variant_name: String },
866    AddOverride { instance: PrefabInstanceId, ov: PrefabOverride },
867    RemoveOverride { instance: PrefabInstanceId, path: String },
868    UnlinkInstance(PrefabInstanceId),
869    RelinkInstance(PrefabInstanceId),
870    RenameNode { prefab: PrefabId, path: String, old: String, new: String },
871}
872
873impl PrefabEditor {
874    pub fn new() -> Self {
875        Self {
876            library: PrefabLibrary::new(),
877            mode: PrefabEditorMode::Browse,
878            selected_prefab: None,
879            selected_instance: None,
880            selected_variant: None,
881            search_query: String::new(),
882            filter_tag: None,
883            sort_by: PrefabSortMode::Name,
884            show_built_in: true,
885            show_variants: true,
886            show_instances_panel: true,
887            pending_delete: None,
888            undo_stack: Vec::new(),
889            redo_stack: Vec::new(),
890        }
891    }
892
893    pub fn search_results(&self) -> Vec<&Prefab> {
894        let mut results = if self.search_query.is_empty() {
895            if let Some(tag) = &self.filter_tag {
896                self.library.by_tag(tag)
897            } else {
898                self.library.prefabs.values().collect()
899            }
900        } else {
901            self.library.search(&self.search_query)
902        };
903        if !self.show_built_in {
904            results.retain(|p| !p.tags.contains(&"built-in".to_string()));
905        }
906        match self.sort_by {
907            PrefabSortMode::Name => results.sort_by(|a, b| a.name.cmp(&b.name)),
908            PrefabSortMode::RecentlyModified => results.sort_by(|a, b| b.modified_at.cmp(&a.modified_at)),
909            PrefabSortMode::InstanceCount => {
910                results.sort_by_key(|p| std::cmp::Reverse(self.library.instances_of(p.id).len()));
911            }
912            PrefabSortMode::NodeCount => results.sort_by_key(|p| std::cmp::Reverse(p.node_count())),
913        }
914        results
915    }
916
917    pub fn select_prefab(&mut self, id: PrefabId) {
918        self.selected_prefab = Some(id);
919        self.selected_instance = None;
920        self.mode = PrefabEditorMode::EditPrefab;
921    }
922
923    pub fn select_instance(&mut self, id: PrefabInstanceId) {
924        self.selected_instance = Some(id);
925        self.mode = PrefabEditorMode::EditInstance;
926        if let Some(inst) = self.library.instance(id) {
927            self.selected_prefab = Some(inst.prefab_id);
928        }
929    }
930
931    pub fn create_prefab(&mut self, name: &str) -> PrefabId {
932        let id = self.library.create(name);
933        self.undo_stack.push(PrefabEditAction::CreatePrefab(id));
934        self.redo_stack.clear();
935        self.selected_prefab = Some(id);
936        self.mode = PrefabEditorMode::EditPrefab;
937        id
938    }
939
940    pub fn delete_selected(&mut self) {
941        if let Some(id) = self.selected_prefab.take() {
942            self.undo_stack.push(PrefabEditAction::DeletePrefab(id));
943            self.redo_stack.clear();
944            self.library.remove(id);
945        }
946    }
947
948    pub fn add_override_to_selected(&mut self, ov: PrefabOverride) {
949        if let Some(iid) = self.selected_instance {
950            self.undo_stack.push(PrefabEditAction::AddOverride {
951                instance: iid, ov: ov.clone(),
952            });
953            self.redo_stack.clear();
954            if let Some(inst) = self.library.instance_mut(iid) {
955                inst.add_override(ov);
956            }
957        }
958    }
959
960    pub fn clear_override_on_selected(&mut self, path: &str) {
961        if let Some(iid) = self.selected_instance {
962            self.undo_stack.push(PrefabEditAction::RemoveOverride {
963                instance: iid, path: path.to_string(),
964            });
965            self.redo_stack.clear();
966            if let Some(inst) = self.library.instance_mut(iid) {
967                inst.clear_override(path);
968            }
969        }
970    }
971
972    pub fn undo(&mut self) {
973        if let Some(action) = self.undo_stack.pop() {
974            match &action {
975                PrefabEditAction::CreatePrefab(id) => { self.library.remove(*id); }
976                PrefabEditAction::DeletePrefab(_) => { /* TODO: restore */ }
977                _ => {}
978            }
979            self.redo_stack.push(action);
980        }
981    }
982
983    pub fn redo(&mut self) {
984        if let Some(action) = self.redo_stack.pop() {
985            self.undo_stack.push(action);
986        }
987    }
988
989    pub fn diff_view(&self) -> Option<Vec<PrefabDiff>> {
990        let iid = self.selected_instance?;
991        let inst = self.library.instance(iid)?;
992        let prefab = self.library.get(inst.prefab_id)?;
993        Some(diff_instance(inst, prefab))
994    }
995
996    pub fn stats(&self) -> PrefabLibraryStats { self.library.stats() }
997}
998
999// Needed for sort_by_key
1000use std::cmp::Reverse;
1001
1002impl Default for PrefabEditor {
1003    fn default() -> Self { Self::new() }
1004}
1005
1006// ─── Tests ───────────────────────────────────────────────────────────────────
1007
1008#[cfg(test)]
1009mod tests {
1010    use super::*;
1011
1012    #[test]
1013    fn create_and_search() {
1014        let mut lib = PrefabLibrary::new();
1015        let id = lib.create("TestRobot");
1016        if let Some(p) = lib.get_mut(id) {
1017            p.tags.push("robot".into());
1018        }
1019        let results = lib.search("robot");
1020        assert!(!results.is_empty());
1021        assert!(results.iter().any(|p| p.id == id));
1022    }
1023
1024    #[test]
1025    fn instantiate_and_override() {
1026        let mut lib = PrefabLibrary::new();
1027        let pid = lib.create("Cube");
1028        let iid = lib.instantiate(pid, 42).unwrap();
1029        lib.instance_mut(iid).unwrap().add_override(
1030            PrefabOverride::set("Root.Transform.position", ComponentValue::Vec3([1.0, 0.0, 0.0]))
1031        );
1032        assert!(lib.instance(iid).unwrap().has_overrides());
1033    }
1034
1035    #[test]
1036    fn prefab_node_hierarchy() {
1037        let mut root = PrefabNode::new("Root");
1038        root.add_child(PrefabNode::new("Child1"));
1039        let mut c2 = PrefabNode::new("Child2");
1040        c2.add_child(PrefabNode::new("Grandchild"));
1041        root.add_child(c2);
1042        assert_eq!(root.total_node_count(), 4);
1043        assert_eq!(root.depth(), 3);
1044    }
1045
1046    #[test]
1047    fn variant_override() {
1048        let mut variant = PrefabVariant::new("Big");
1049        variant.add_override(PrefabOverride::set(
1050            "Root.Transform.scale",
1051            ComponentValue::Vec3([2.0, 2.0, 2.0]),
1052        ));
1053        assert_eq!(variant.override_count(), 1);
1054        variant.remove_override("Root.Transform.scale");
1055        assert_eq!(variant.override_count(), 0);
1056    }
1057
1058    #[test]
1059    fn component_value_lerp() {
1060        let a = ComponentValue::Float(0.0);
1061        let b = ComponentValue::Float(10.0);
1062        let m = a.lerp(&b, 0.5).unwrap();
1063        assert!((m.as_float().unwrap() - 5.0).abs() < 1e-9);
1064    }
1065
1066    #[test]
1067    fn prefab_editor_search() {
1068        let ed = PrefabEditor::new();
1069        let results = ed.search_results();
1070        assert!(!results.is_empty());
1071    }
1072
1073    #[test]
1074    fn prefab_editor_create_undo() {
1075        let mut ed = PrefabEditor::new();
1076        let count_before = ed.library.prefab_count();
1077        ed.create_prefab("NewPrefab");
1078        assert_eq!(ed.library.prefab_count(), count_before + 1);
1079        ed.undo();
1080        assert_eq!(ed.library.prefab_count(), count_before);
1081    }
1082}