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
/*
 * librePvZ-resources: resource loading for librePvZ.
 * Copyright (c) 2022  Ruifeng Xie
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//! Dynamic resource support. For simplicity, we use shared global state for registry.

use std::any::TypeId;
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use bevy::prelude::*;
use bevy::reflect::erased_serde::{
    Serialize as ErasedSerialize,
    Deserializer as ErasedDeserializer,
    serialize as erased_serde_serialize,
    Error,
};
use bevy::reflect::{GetTypeRegistration, TypeRegistryArc};
use bevy::utils::HashMap;
use bincode::{Decode, Encode};
use bincode::config::Configuration;
use bincode::de::{Decoder, DecoderImpl};
use bincode::de::read::Reader;
use bincode::enc::{Encoder, EncoderImpl};
use bincode::enc::write::Writer;
use bincode::error::{DecodeError, EncodeError};
use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::{DeserializeOwned, DeserializeSeed, MapAccess, Visitor, Error as _};
use serde::ser::{SerializeMap, Error as _};

/// Type registry for dynamic content (de)serialization.
#[allow(missing_debug_implementations)]
pub struct DynamicRegistry {
    // TODO: use type_path instead
    readable_name_to_id: RwLock<HashMap<Box<str>, TypeId>>,
    readable_name_from_id: RwLock<BTreeMap<TypeId, Box<str>>>,
    type_registry: TypeRegistryArc,
}

static GLOBAL_REGISTRY: OnceCell<DynamicRegistry> = OnceCell::new();

impl DynamicRegistry {
    /// Initialize the global dynamic registry. This saves the [`TypeRegistry`] provided by Bevy in
    /// the global [`DynamicRegistry`]. Panics if the global registry is already initialized.
    pub fn initialize(type_registry: TypeRegistryArc) {
        GLOBAL_REGISTRY.set(DynamicRegistry {
            readable_name_to_id: RwLock::new(HashMap::new()),
            readable_name_from_id: RwLock::new(BTreeMap::new()),
            type_registry,
        }).ok().expect("DynamicRegistry must not be initialized more than once")
    }

    /// Initialize the global dynamic registry, without a Bevy app.
    pub fn initialize_without_bevy() {
        DynamicRegistry::initialize(TypeRegistryArc::default())
    }

    /// Get the global dynamic registry. Panics if called before initialization of the registry.
    pub fn global() -> &'static DynamicRegistry {
        GLOBAL_REGISTRY.get().expect("DynamicRegistry: must initialize before use")
    }

    /// Get a shared reference to Bevy's type registry (already wrapped in `Arc`).
    pub fn get_bevy_type_registry(&self) -> &TypeRegistryArc { &self.type_registry }

    /// Get the [`ReflectAnyResource`] for the type registered as the given name.
    pub fn resource_by_name(&self, name: &str) -> Option<ReflectAnyResource> {
        let id = self.readable_name_to_id.read().get(name).copied()?;
        self.type_registry.read().get_type_data::<ReflectAnyResource>(id).copied()
    }

    /// Register a type for dynamic (de)serialization.
    pub fn register_dynamic<T: AnyResource + GetTypeRegistration>(&self, name: &str) {
        self.type_registry.write().register::<T>();
        self.readable_name_from_id.write().insert(TypeId::of::<T>(), name.into());
        let old = self.readable_name_to_id.write().insert(name.into(), TypeId::of::<T>());
        assert!(
            old.is_none(),
            "DynamicResource: name '{name}' is already taken by {}, cannot overwrite it with {}",
            // TODO: consider what it means to use `type_path` in place of previous `type_name`
            self.type_registry.read().get_type_info(old.unwrap()).unwrap().type_path(),
            std::any::type_name::<T>(),
        );
    }
}

/// Resource types with dynamic (de)serialization. Serialization through [`bincode`] is fixed to
/// using the [`standard`](bincode::config::standard) configuration, and cannot be changed at
/// runtime, due to the current design of [`bincode`] API.
pub trait AnyResource: Reflect + ErasedSerialize + Send + Sync + 'static {
    /// Convert to a [`Reflect`] trait object.
    fn as_reflect(&self) -> &dyn Reflect;
    /// Convert to a mutable [`Reflect`] trait object.
    fn as_reflect_mut(&mut self) -> &mut dyn Reflect;
    /// Deserialize from an [`erased`](bevy::reflect::erased_serde) deserializer.
    fn erased_deserialize(src: &mut dyn ErasedDeserializer) -> Result<Box<dyn AnyResource>, Error> where Self: Sized;
    /// Encode as [`bincode`] to a given [`Writer`] using standard configuration.
    fn erased_encode(&self, writer: &mut dyn Writer) -> Result<(), EncodeError>;
    /// Decode as [`bincode`] from a given [`Reader`] using standard configuration.
    fn erased_decode(reader: &mut dyn Reader) -> Result<Box<dyn AnyResource>, DecodeError> where Self: Sized;
}

/// [`TypeData`](bevy::reflect::TypeData) providing support for [`AnyResource`] trait.
#[derive(Copy, Clone)]
#[allow(missing_debug_implementations)]
#[allow(clippy::type_complexity)]
pub struct ReflectAnyResource {
    get: fn(&dyn Reflect) -> &dyn AnyResource,
    get_mut: fn(&mut dyn Reflect) -> &mut dyn AnyResource,
    get_boxed: fn(Box<dyn Reflect>) -> Box<dyn AnyResource>,
    erased_deserialize: fn(&mut dyn ErasedDeserializer) -> Result<Box<dyn AnyResource>, Error>,
    erased_decode: fn(&mut dyn Reader) -> Result<Box<dyn AnyResource>, DecodeError>,
}

impl ReflectAnyResource {
    /// Try to downcast a `&dyn Reflect` type to `&dyn AnyResource`.
    pub fn get<'a>(&self, val: &'a dyn Reflect) -> &'a dyn AnyResource { (self.get)(val) }
    /// Try to downcast a `&mut dyn Reflect` type to `&mut dyn AnyResource`.
    pub fn get_mut<'a>(&self, val: &'a mut dyn Reflect) -> &'a mut dyn AnyResource { (self.get_mut)(val) }
    /// Try to downcast a `Box<dyn Reflect>` type to `Box<dyn AnyResource>`.
    pub fn get_boxed(&self, val: Box<dyn Reflect>) -> Box<dyn AnyResource> { (self.get_boxed)(val) }
    /// Deserialize using [`serde`] into a trait object.
    pub fn erased_deserialize(&self, src: &mut dyn ErasedDeserializer) -> Result<Box<dyn AnyResource>, Error> {
        (self.erased_deserialize)(src)
    }
    /// Deserialize using [`bincode`] into a trait object.
    pub fn erased_decode(&self, reader: &mut dyn Reader) -> Result<Box<dyn AnyResource>, DecodeError> {
        (self.erased_decode)(reader)
    }
}

const BINCODE_CONFIG: Configuration = bincode::config::standard();

struct Proxy<'a, T: ?Sized>(&'a mut T);

impl<'a> Writer for Proxy<'a, dyn Writer + 'a> {
    #[inline(always)]
    fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> { self.0.write(bytes) }
}

impl<'a> Reader for Proxy<'a, dyn Reader + 'a> {
    #[inline(always)]
    fn read(&mut self, bytes: &mut [u8]) -> Result<(), DecodeError> { self.0.read(bytes) }
    #[inline(always)]
    fn peek_read(&mut self, n: usize) -> Option<&[u8]> { self.0.peek_read(n) }
    #[inline(always)]
    fn consume(&mut self, n: usize) { self.0.consume(n) }
}

impl<T> AnyResource for T
    where T: Reflect + Serialize + DeserializeOwned + Encode + Decode + Send + Sync + 'static {
    fn as_reflect(&self) -> &dyn Reflect { self }
    fn as_reflect_mut(&mut self) -> &mut dyn Reflect { self }
    fn erased_deserialize(src: &mut dyn ErasedDeserializer) -> Result<Box<dyn AnyResource>, Error> where Self: Sized {
        T::deserialize(src).map(|x| Box::new(x) as _)
    }
    fn erased_encode(&self, writer: &mut dyn Writer) -> Result<(), EncodeError> {
        // hopefully this gets inlined, and the double reference is optimised away
        T::encode(self, &mut EncoderImpl::new(Proxy(writer), BINCODE_CONFIG))
    }
    fn erased_decode(reader: &mut dyn Reader) -> Result<Box<dyn AnyResource>, DecodeError> where Self: Sized {
        // hopefully this gets inlined, and the double reference is optimised away
        T::decode(&mut DecoderImpl::new(Proxy(reader), BINCODE_CONFIG)).map(|x| Box::new(x) as _)
    }
}

impl Debug for dyn AnyResource {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.debug(f) }
}

struct Wrapper<'a, T: ?Sized>(&'a T);

impl<'a, T: AnyResource + ?Sized> Serialize for Wrapper<'a, T> {
    #[inline(always)]
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        erased_serde_serialize(self, serializer)
    }
}

fn serialize_any_resource<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where T: AnyResource + ?Sized, S: Serializer {
    let g = DynamicRegistry::global().readable_name_from_id.read();
    let name = g.get(&value.type_id()).map(Box::as_ref)
        .ok_or_else(|| S::Error::custom(format_args!(
            "type '{}' does not support dynamic serialization", value.reflect_type_path())))?;
    let mut map = serializer.serialize_map(Some(2))?;
    map.serialize_entry(name, &Wrapper(value))?;
    map.end()
}

impl Serialize for dyn AnyResource {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serialize_any_resource(self, serializer)
    }
}

impl<'de> DeserializeSeed<'de> for ReflectAnyResource {
    type Value = Box<dyn AnyResource>;
    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
        self.erased_deserialize(&mut <dyn ErasedDeserializer>::erase(deserializer))
            .map_err(D::Error::custom)
    }
}

impl<'de> Deserialize<'de> for Box<dyn AnyResource> {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct DynResVisitor;
        impl<'de> Visitor<'de> for DynResVisitor {
            type Value = Box<dyn AnyResource>;
            fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
                formatter.write_str("a dynamic resource")
            }
            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
                let name = map.next_key::<String>()?.ok_or_else(||
                    A::Error::custom("type tag for DynamicResource required"))?;
                let reg = DynamicRegistry::global();
                let reflect = reg.resource_by_name(&name).ok_or_else(|| A::Error::custom(
                    format_args!("type {} not registered for dynamic deserialization", name)))?;
                let result = map.next_value_seed(reflect)?;
                if map.next_key::<String>()?.is_none() { Ok(result) } else {
                    Err(A::Error::custom(format_args!("too many entries for DynamicResource '{}'", name)))
                }
            }
        }
        deserializer.deserialize_map(DynResVisitor)
    }
}

fn encode_any_resource<T, E>(value: &T, encoder: &mut E) -> Result<(), EncodeError>
    where T: AnyResource + ?Sized, E: Encoder {
    let g = DynamicRegistry::global().readable_name_from_id.read();
    let name = g.get(&value.type_id()).map(Box::as_ref)
        .ok_or_else(|| EncodeError::OtherString(format!(
            "type '{}' does not support dynamic serialization", value.reflect_type_path())))?;
    name.encode(encoder)?;
    value.erased_encode(encoder.writer())
}

impl Encode for dyn AnyResource {
    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
        encode_any_resource(self, encoder)
    }
}

impl Decode for Box<dyn AnyResource> {
    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
        let name = String::decode(decoder)?;
        let reg = DynamicRegistry::global();
        let reflect = reg.resource_by_name(&name)
            .ok_or_else(|| DecodeError::OtherString(
                format!("type {} not registered for dynamic deserialization", name)
            ))?;
        reflect.erased_decode(decoder.reader())
    }
}

/// Resource, but without a statically-known type.
#[repr(transparent)]
pub struct DynamicResource<T: ?Sized>(Box<T>);

impl<T: ?Sized> From<Box<T>> for DynamicResource<T> {
    fn from(value: Box<T>) -> Self { DynamicResource(value) }
}

impl<T: ErasedResource + ?Sized> Debug for DynamicResource<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.debug(f)
    }
}

/// Allow dynamic (de)serialization for trait objects.
pub trait ErasedResource: AnyResource {
    /// Readable name for this type of trait objects as dynamic resource (for diagnostics).
    const RESOURCE_TYPE: &'static str;
    /// Try convert from a fully-erased dynamic resource to this trait object.
    fn try_from_erased(erased: Box<dyn AnyResource>) -> Result<DynamicResource<Self>, String>;
}

/// Mark a trait available for dynamic (de)serialization.
/// ```
/// # use bevy::reflect::reflect_trait;
/// # use libre_pvz_resources::dynamic::AnyResource;
/// # use libre_pvz_resources::mark_trait_as_dynamic_resource;
/// #[reflect_trait]
/// pub trait DoThing: AnyResource {
///     fn do_thing(&self) -> String;
/// }
/// mark_trait_as_dynamic_resource!(DoThing, ReflectDoThing);
/// ```
#[macro_export]
macro_rules! mark_trait_as_dynamic_resource {
    ($trait_name:ident, $reflect_trait:ty $(,)?) => {
        $crate::mark_trait_as_dynamic_resource! {
            $trait_name, $reflect_trait, stringify!($trait_name),
        }
    };
    ($trait_name:ident, $reflect_trait:ty, $readable_name:expr $(,)?) => {
        impl $crate::dynamic::ErasedResource for dyn $trait_name {
            const RESOURCE_TYPE: &'static str = $readable_name;
            fn try_from_erased(erased: Box<dyn $crate::dynamic::AnyResource>)
                    -> Result<$crate::dynamic::DynamicResource<Self>, String> {
                let reg = $crate::dynamic::DynamicRegistry::global().get_bevy_type_registry().read();
                let report_error = |real_type: &str| {
                    let expected = Self::RESOURCE_TYPE;
                    format!("{real_type} is not an instance of {expected}")
                };
                let reflect = reg.get_type_data::<$reflect_trait>(erased.type_id())
                    .ok_or_else(|| report_error(erased.reflect_type_path()))?;
                reflect.get_boxed(erased.into_reflect())
                    .map($crate::dynamic::DynamicResource::from)
                    .map_err(|e| report_error(e.reflect_type_path()))
            }
        }
    }
}

impl<T: ?Sized> Deref for DynamicResource<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target { self.0.as_ref() }
}

impl<T: ErasedResource + ?Sized> Serialize for DynamicResource<T> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serialize_any_resource(self.0.as_ref(), serializer)
    }
}

impl<'de, T: ErasedResource + ?Sized> Deserialize<'de> for DynamicResource<T> {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let erased = Box::<dyn AnyResource>::deserialize(deserializer)?;
        T::try_from_erased(erased).map_err(D::Error::custom)
    }
}

impl<T: ErasedResource + ?Sized> Encode for DynamicResource<T> {
    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
        encode_any_resource(self.0.as_ref(), encoder)
    }
}

impl<T: ErasedResource + ?Sized> Decode for DynamicResource<T> {
    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
        let erased = Box::<dyn AnyResource>::decode(decoder)?;
        T::try_from_erased(erased).map_err(DecodeError::OtherString)
    }
}