Documentation
use anyhow::*;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::resource::Resource;
use bevy_reflect::{PartialReflect, TypeRegistration, TypeRegistry};

#[cfg(feature = "serde_json")]
use serde::de::DeserializeSeed;

#[derive(Resource, Deref, DerefMut)]
pub struct CodecResource(#[deref] pub Box<dyn WasvyCodec>);

impl CodecResource {
    pub fn new(codec: impl WasvyCodec) -> Self {
        Self(Box::new(codec))
    }
}

pub trait WasvyCodec: Send + Sync + 'static {
    fn encode_reflect(
        &self,
        reflect: &dyn PartialReflect,
        registry: &TypeRegistry,
    ) -> Result<Vec<u8>>;
    fn decode_reflect(
        &self,
        bytes: &[u8],
        registration: &TypeRegistration,
        registry: &TypeRegistry,
    ) -> Result<Box<dyn PartialReflect>>;
    fn decode_reflect_args(
        &self,
        params: &[u8],
        type_path: &[&str],
        registry: &TypeRegistry,
    ) -> Result<Vec<Option<Box<dyn PartialReflect>>>>;
    fn get_type(&self) -> String;
}

#[cfg(feature = "serde_json")]
#[derive(Default, Resource)]
pub struct JsonCodec;

#[cfg(feature = "serde_json")]
impl Default for CodecResource {
    fn default() -> Self {
        Self(Box::new(JsonCodec))
    }
}

#[cfg(feature = "serde_json")]
impl WasvyCodec for JsonCodec {
    fn encode_reflect(
        &self,
        reflect: &dyn PartialReflect,
        registry: &TypeRegistry,
    ) -> Result<Vec<u8>> {
        let serializer = bevy_reflect::serde::TypedReflectSerializer::new(reflect, registry);
        Ok(serde_json::to_vec(&serializer)?)
    }

    fn decode_reflect(
        &self,
        bytes: &[u8],
        registration: &TypeRegistration,
        registry: &TypeRegistry,
    ) -> Result<Box<dyn PartialReflect>> {
        let mut de = serde_json::Deserializer::from_slice(bytes);
        let reflect_deserializer =
            bevy_reflect::serde::TypedReflectDeserializer::new(registration, registry);
        let boxed_dyn_reflect = reflect_deserializer.deserialize(&mut de)?;
        Ok(boxed_dyn_reflect)
    }

    fn decode_reflect_args(
        &self,
        params: &[u8],
        type_path: &[&str],
        registry: &TypeRegistry,
    ) -> Result<Vec<Option<Box<dyn PartialReflect>>>> {
        if params.is_empty() || params.iter().all(|b| b.is_ascii_whitespace()) {
            return Ok(vec![]);
        }

        let value = serde_json::from_slice(params)?;

        let args = match value {
            serde_json::Value::Null => Vec::new(),
            serde_json::Value::Array(values) => values,
            _ => bail!("Expected JSON array for params, got {}", value),
        };

        let mut output = Vec::new();

        for (type_path, value) in type_path.iter().zip(args.iter()) {
            let registration = registry
                .get_with_type_path(type_path)
                .ok_or_else(|| anyhow::anyhow!("Type {type_path} is not registered"))?;

            let bytes = serde_json::to_vec(value)?;
            let mut de = serde_json::Deserializer::from_slice(&bytes);
            let reflect_de =
                bevy_reflect::serde::TypedReflectDeserializer::new(registration, registry);
            output.push(Some(reflect_de.deserialize(&mut de)?));
        }

        Ok(output)
    }

    fn get_type(&self) -> String {
        "json".to_string()
    }
}

#[cfg(feature = "serde_json")]
pub fn wasvy_encode<T>(value: &T) -> Result<Vec<u8>>
where
    T: ?Sized + serde::Serialize,
{
    Ok(serde_json::to_vec(&value)?)
}

#[cfg(feature = "serde_json")]
pub fn wasvy_decode<'a, T>(v: &'a [u8]) -> Result<T>
where
    T: serde::Deserialize<'a>,
{
    serde_json::from_slice(v).map_err(anyhow::Error::from)
}