use super::repr::IOValue;

pub static MAGIC: &str = "$____Preserves_Serde_Magic";

struct IOValueVisitor;
impl<'de> serde::de::Visitor<'de> for IOValueVisitor {
    type Value = IOValue;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a magic encoding of an embedded Preserves Value")
    }
    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error {
        let b = unsafe { Box::from_raw(v as *mut IOValue) };
        Ok(*b)
    }
}

#[inline(always)]
pub fn output_value<S: serde::Serializer>(serializer: S, v: IOValue) ->
    Result<S::Ok, S::Error>
{
    serializer.serialize_newtype_struct(MAGIC, &(Box::into_raw(Box::new(v)) as u64))
}

#[inline(always)]
pub fn input_value<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<IOValue, D::Error>
{
    deserializer.deserialize_newtype_struct(MAGIC, IOValueVisitor)
}

//---------------------------------------------------------------------------

#[inline]
pub fn receive_output_value<T: ?Sized>(name: &'static str, magic_value: &T) -> Option<IOValue> {
    if name == MAGIC {
        let b = unsafe { Box::from_raw(*((magic_value as *const T) as *const u64) as *mut IOValue) };
        let v: IOValue = *b;
        Some(v)
    } else {
        None
    }
}

#[inline]
pub fn transmit_input_value<F>(name: &'static str, f: F) -> Result<Option<u64>, crate::error::Error>
where F: FnOnce() -> Result<IOValue, crate::error::Error>
{
    if name == MAGIC {
        let b: Box<IOValue> = Box::new(f()?);
        Ok(Some(Box::into_raw(b) as u64))
    } else {
        Ok(None)
    }
}
