1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
use crate::core::GroupID;
use crate::entity::{EntityData, EntityTrait, Metadata};
use crate::BroadcastReceiver;
use compact_str::CompactString;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::iter::zip;
use std::mem::replace;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Weak};

///
/// Base trait that is automatically generated
///
pub trait Template: Clone + 'static {
    /// Returns table mapping to <offset_from_base:property_metadata>
    fn prop_desc_table__() -> &'static HashMap<usize, PropData>;

    /// Get path of this config template (module path, struct name)
    fn template_name() -> (&'static str, &'static str);

    /// Create default object
    fn default_config() -> Self;

    /// Fill defaulted values
    fn fill_default(&mut self);

    /// Returns element at index as Any
    fn elem_at_mut__(&mut self, index: usize) -> &mut dyn Any;

    /// Convenient wrapper for element value update
    fn update_elem_at__(&mut self, index: usize, value: &dyn Any, meta: &Metadata) {
        let data = self.elem_at_mut__(index);
        (meta.fn_copy_to)(value, data);
    }
}

pub struct PropData {
    pub index: usize,
    pub type_id: TypeId,
    pub meta: Arc<Metadata>,
}

///
/// May storage implement this
///
pub struct GroupContext {
    pub group_id: GroupID,
    pub template_type_id: TypeId,
    pub sources: Arc<Vec<EntityData>>,

    pub(crate) w_unregister_hook: Weak<dyn Any + Send + Sync>,
    pub(crate) source_update_fence: AtomicUsize,

    /// Path of instantiated config set.
    pub path: Arc<Vec<CompactString>>,

    /// Broadcast subscriber to receive updates from backend.
    pub(crate) update_receiver_channel: async_broadcast::InactiveReceiver<()>,
}

///
/// Primary interface that end user may interact with
///
/// Wrap `ReflectData` derivative like `Group<MyData>`
///
pub struct Group<T> {
    /// Cached local content
    __body: T,

    /// Cached update fence
    fence: usize,

    /// Property-wise contexts
    local: Vec<PropLocalContext>,

    /// List of managed properties. This act as source container
    core: Arc<GroupContext>,

    /// Unregister hook anchor.
    ///
    /// It will unregister this config set from owner storage automatically, when all
    ///  instances of config set disposed.
    _unregister_hook: Arc<dyn Any + Send + Sync>,
}

impl<T: Clone> Clone for Group<T> {
    fn clone(&self) -> Self {
        Self {
            __body: self.__body.clone(),
            fence: self.fence.clone(),
            local: self.local.clone(),
            core: self.core.clone(),
            _unregister_hook: self._unregister_hook.clone(),
        }
    }
}

impl<T: std::fmt::Debug> std::fmt::Debug for Group<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Group").field("__body", &self.__body).field("fence", &self.fence).finish()
    }
}

#[derive(Clone)]
struct PropLocalContext {
    /// Locally cached update fence.
    update_fence: usize,

    /// Updated recently
    dirty_flag: bool,
}

impl Default for PropLocalContext {
    fn default() -> Self {
        Self {
            update_fence: 0,
            dirty_flag: true, // This forces initial 'check_update()' call to return true.
        }
    }
}

/// Type alias for broadcast receiver
pub type WatchUpdate = BroadcastReceiver<()>;

impl<T: Template> Group<T> {
    #[doc(hidden)]
    pub(crate) fn create_with__(
        core: Arc<GroupContext>,
        unregister_anchor: Arc<dyn Any + Send + Sync>,
    ) -> Self {
        let mut gen = Self {
            core,
            __body: T::default_config(),
            fence: 0,
            local: vec![PropLocalContext::default(); T::prop_desc_table__().len()].into(),
            _unregister_hook: unregister_anchor,
        };

        gen.fill_default();
        gen
    }

    /// Fetch underlying object's updates and apply to local cache. Returns true if there was
    /// any update available.
    pub fn update(&mut self) -> bool {
        let Self { local, .. } = self;

        // Forces initial update always return true.
        let mut has_update = self.fence == 0;

        // Perform quick check: Does update fence value changed?
        match self.core.source_update_fence.load(Ordering::Relaxed) {
            v if v == self.fence => return false,
            v => self.fence = v,
        }

        debug_assert_eq!(
            local.len(),
            self.core.sources.len(),
            "Logic Error: set was not correctly initialized!"
        );

        for ((index, local), source) in zip(zip(0..local.len(), &mut *local), &*self.core.sources) {
            // Perform quick check to see if given config entity has any update.
            match source.get_update_fence() {
                v if v == local.update_fence => continue,
                v => local.update_fence = v,
            }

            has_update = true;
            local.dirty_flag = true;

            let (meta, value) = source.get_value();
            self.__body.update_elem_at__(index, value.as_any(), &*meta);
        }

        has_update
    }

    #[deprecated(since = "0.4.0", note = "use `clear_flag` instead")]
    pub fn check_elem_update<U: 'static>(&mut self, e: *const U) -> bool {
        self.consume_update(e)
    }

    /// Check element update from its address, and clears dirty flag on given element.
    /// This is only meaningful when followed by [`Group::update`] call.
    pub fn consume_update<U: 'static>(&mut self, e: *const U) -> bool {
        let Some(index) = self.get_index_by_ptr(e) else { return false };
        replace(&mut self.local[index].dirty_flag, false)
    }

    /// Get index of element based on element address.
    #[doc(hidden)]
    pub fn get_index_by_ptr<U: 'static>(&self, e: *const U) -> Option<usize> {
        if let Some(prop) = self.get_prop_by_ptr(e) {
            Some(prop.index)
        } else {
            None
        }
    }

    /// Get property descriptor by element address. Provides primitive guarantee for type safety.
    #[doc(hidden)]
    pub fn get_prop_by_ptr<U: 'static>(&self, e: *const U) -> Option<&PropData> {
        let ptr = e as *const u8 as isize;
        let base = &self.__body as *const _ as *const u8 as isize;

        match ptr - base {
            v if v < 0 => None,
            v if v >= std::mem::size_of::<T>() as isize => None,
            v => {
                if let Some(prop) = T::prop_desc_table__().get(&(v as usize)) {
                    debug_assert_eq!(prop.type_id, TypeId::of::<U>());
                    debug_assert!(prop.index < self.local.len());
                    Some(prop)
                } else {
                    None
                }
            }
        }
    }

    /// Commit changes on element to core context, then it will be propagated to all other groups
    /// which shares same core context.
    pub fn commit_elem<U: Clone + EntityTrait + Send>(&self, e: &U, notify: bool) {
        // Create new value pointer from input argument.
        let cloned_value = Arc::new(e.clone()) as Arc<dyn EntityTrait>;

        // Replace source argument with created ptr
        let elem = &(*self.core.sources)[self.get_index_by_ptr(e).unwrap()];
        elem.__apply_value(cloned_value);
        elem.__notify_value_change(notify)
    }

    /// Clones new update receiver channel. Given channel will be notified whenever call to
    /// `update()` method meaningful. However, as the event can be generated manually even
    /// if there's no actual update, it's not recommended to make critical logics rely on
    /// this signal.
    pub fn watch_update(&self) -> WatchUpdate {
        self.core.update_receiver_channel.clone().activate()
    }

    /// Spawns new broadcast receiver, and forcibly generates update event.
    ///
    /// This is useful when you want to make sure that first monitoring event is always triggered,
    /// however, note that this method will incur all other watchdogs to be notified as well.
    pub fn watch_update_with_event_broadcast(&self) -> WatchUpdate {
        let rx = self.watch_update();
        let _ = rx.new_sender().try_broadcast(());
        rx
    }

    /// Mark all elements dirty. Next call to [`Group::update()`] may not return true if there
    /// wasn't any actual update, however, every call to [`Group::clear_flag()`] for
    /// each elements will return true.
    pub fn mark_all_elem_dirty(&mut self) {
        // Raising dirty flag does not incur remote reload.
        self.local.iter_mut().for_each(|e| e.dirty_flag = true);
    }

    /// Mark this group dirty. Next call to `update()` method will return true, regardless of
    /// whether there's any actual update.
    pub fn mark_group_dirty(&mut self) {
        self.fence = 0;
    }

    /// Mark given element dirty.
    pub fn mark_dirty<U: 'static>(&mut self, elem: *const U) {
        let index = self.get_index_by_ptr(elem).unwrap();
        self.local[index].dirty_flag = true;
    }

    /// Get generated metadata of given element
    pub fn get_metadata<U: 'static>(&self, elem: *const U) -> Arc<Metadata> {
        self.get_prop_by_ptr(elem).unwrap().meta.clone()
    }

    /// Get instance path of `self`. This value is same as the list of tokens that you have
    /// provided to [`crate::Storage::create_group`] method.
    pub fn get_path(&self) -> Arc<Vec<CompactString>> {
        self.core.path.clone()
    }
}

impl<T> std::ops::Deref for Group<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.__body
    }
}

impl<T> std::ops::DerefMut for Group<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.__body
    }
}

#[test]
fn _verify_send_impl() {
    #[derive(Clone, Default)]
    struct Example {}
    impl Template for Example {
        fn prop_desc_table__() -> &'static HashMap<usize, PropData> {
            unimplemented!()
        }

        fn fill_default(&mut self) {
            unimplemented!()
        }

        fn elem_at_mut__(&mut self, _: usize) -> &mut dyn Any {
            unimplemented!()
        }

        fn template_name() -> (&'static str, &'static str) {
            unimplemented!()
        }

        fn default_config() -> Self {
            Self::default()
        }
    }

    fn _assert_send<T: Send + Sync>() {}
    _assert_send::<Group<Example>>();
}

impl<T> Group<T> {
    #[doc(hidden)]
    pub fn __macro_as_mut(&mut self) -> &mut Self {
        //! Use coercion to get mutable reference to self regardless of its expression.
        self
    }
}

#[cfg(any())]
mod emulate_generation {
    use futures::executor;
    use lazy_static::lazy_static;
    use std::thread;

    use super::*;
    use crate::*;

    #[derive(Default, Clone)]
    struct MyStruct {
        my_int: i32,
        my_string: String,
    }

    impl Template for MyStruct {
        fn prop_desc_table__() -> &'static HashMap<usize, PropData> {
            use entity::{MetadataProps, MetadataValInit};

            lazy_static! {
                static ref TABLE: Arc<HashMap<usize, PropData>> = {
                    let mut s = HashMap::new();

                    {
                        type Type = i32;

                        let offset = unsafe {
                            let owner = 0 as *const MyStruct;
                            &(*owner).my_int as *const _ as *const u8 as usize
                        };
                        let identifier = "#ident_as_string";
                        let varname = "#varname_or_ident";
                        let doc_string = "#doc_str";
                        let index = 1;
                        let default_value: Type = 13;

                        let init = MetadataValInit::<Type> {
                            fn_validate: |_, _| -> Option<bool> { Some(true) },
                            v_default: default_value,
                            v_one_of: Default::default(),
                            v_max: Default::default(),
                            v_min: Default::default(),
                        };

                        let props = MetadataProps {
                            description: doc_string,
                            varname,
                            disable_import: false,
                            disable_export: false,
                            hidden: false,
                        };

                        let meta = Metadata::create_for_base_type(identifier, init, props);

                        let prop_data =
                            PropData { index, type_id: TypeId::of::<Type>(), meta: Arc::new(meta) };

                        s.insert(offset, prop_data);
                    }

                    Arc::new(s)
                };
            }

            &*TABLE
        }

        fn elem_at_mut__(&mut self, index: usize) -> &mut dyn Any {
            match index {
                0 => &mut self.my_int,
                1 => &mut self.my_string,
                _ => panic!(),
            }
        }

        fn fill_default(&mut self) {}
    }

    #[test]
    fn try_compile() {
        println!("{}", std::env::var("MY_VAR").unwrap());
        let (st, work) = Storage::new();
        thread::spawn(move || futures::executor::block_on(work));

        let mut group: Group<MyStruct> =
            executor::block_on(st.create_group(["RootCategory".into()].to_vec())).unwrap();

        assert!(group.update());
        assert!(!group.update());
        assert!(group.clear_flag(&group.my_string));
        assert!(!group.clear_flag(&group.my_string));
    }
}