use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{
    parse_macro_input, Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList,
    Token, Type, Variant,
};

/// Derive the type Serializable.
///
/// For simple case, it is just a concatenation of the Serializable fields.
///
/// ## Until
///
/// For list of data, we can serialize data until we find some specific data. To do so we need
/// the type of the repeated data `D`, the type of the symbole at the end of the list `U`, and
/// the specific value of the symbole at the end of the list `end`.
///
/// For example, we can deserialize a `Vec<u8>` until we find `0x0000`:
///
/// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]`
///
/// In this example, `D` is `u8`, `U` is `u16` and `end` is `0x0000`.
///
/// To define a field using this method, the type of field must implement the trait
/// `SerializableUntil<D, U>`, `D` and `U` must implement `Serializable`, and the field
/// must be marked with the attribute `#[until(D, U, end)]`:
///
/// ```
/// pub use androscalpel_serializer::*;
///
/// #[derive(Serializable, PartialEq, Debug)]
/// struct Example {
///     #[until(u8, u16, 0)]
///     a: Vec<u8>,
/// }
///
/// assert_eq!(
///     Example::deserialize_from_slice(&[0, 1, 2, 3, 0, 0]).unwrap(),
///     Example { a: vec![0, 1, 2, 3] }
/// );
/// ```
///
/// ## Enums
///
/// Enums are a litle tricky.
/// To distinguish the enum variant, there binary representation must be prefixed by
/// the serialization of a specific type. This type is the same for the whole enum,
/// and is defined with `prefix_type`, e.g. `#[prefix_type(u8)]`.
///
/// Each variant (except the default one) must define the prefix they match with `prefix`,
/// e.g. `#[prefix(0x42)]`.
///
/// An enum can define ONE variant as the default variant. This variant is a catch all,
/// and MUST be have named field or unnamed field (no unit variant!) and:
/// - The first field of named fields variant must be named `prefix` and of the type
/// `prefix_type`.
/// - The first field of unnamed fields variant must be of the type `prefix_type`.
///
/// The first field of the default variant store the prefix of the variant.
///
/// ```
/// pub use androscalpel_serializer::*;
///
/// #[derive(Serializable, PartialEq, Debug)]
/// #[prefix_type(u8)]
/// enum Example {
///     #[prefix(0)]
///     Zero,
///     #[prefix(1)]
///     One(u32),
///     #[prefix(2)]
///     Two { a: u16, b: u32 },
///     #[default_variant]
///     Dft(u8, u32)
/// }
///
/// assert_eq!(
///     Example::deserialize_from_slice(&[0u8]).unwrap(),
///     Example::Zero
/// );
/// assert_eq!(
///     Example::deserialize_from_slice(&[1u8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(),
///     Example::One(0x01234567)
/// );
/// assert_eq!(
///     Example::deserialize_from_slice(&[2u8, 0xDCu8, 0xFEu8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(),
///     Example::Two { a: 0xFEDC, b: 0x01234567 }
/// );
/// assert_eq!(
///     Example::deserialize_from_slice(&[42u8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(),
///     Example::Dft(42, 0x01234567)
/// );
/// ```
///
/// ## Prefix and suffix
///
/// To define a constant prefix before a serialised field or suffixe after, use `#[prefix(<prefix>)]`
/// and `#[suffix(<suffix>)]`. The prefix/suffix must be `u8`s `Iterator`.
///
/// ```
/// pub use androscalpel_serializer::*;
///
/// #[derive(Serializable, PartialEq, Debug)]
/// struct Example {
///     #[prefix([0x42])]
///     #[suffix([0x66, 0x66])]
///     a: u32,
/// }
///
/// assert_eq!(
///     Example::deserialize_from_slice(&[0x42, 3, 2, 1, 0, 0x66, 0x66]).unwrap(),
///     Example { a: 0x00010203 }
/// );
/// ```
///
///
#[proc_macro_derive(
    Serializable,
    attributes(until, prefix, prefix_type, suffix, default_variant)
)]
pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    let params = ParamsStruct::parse(&input.attrs);
    let implem_serialize = get_implem_serialize(&input.data, &params);
    let implem_deserialize = get_implem_deserialize(&input.data, &params);
    let implem_size = get_implem_size(&input.data, &params);
    let expanded = quote! {
        impl androscalpel_serializer::Serializable for #name {
            #[allow(clippy::single_element_loop, clippy::let_and_return)]
            fn serialize(&self, output: &mut dyn std::io::Write) -> androscalpel_serializer::Result<()> {
                #implem_serialize
            }

            #[allow(clippy::single_element_loop, clippy::let_and_return, clippy::needless_return)]
            fn deserialize(input: &mut dyn androscalpel_serializer::ReadSeek) -> androscalpel_serializer::Result<Self> {
                #implem_deserialize
            }

            #[allow(clippy::identity_op)]
            fn size(&self) -> usize {
                #implem_size
            }
        }
    };
    proc_macro::TokenStream::from(expanded)
}

/// Parsed Parameters for the `#[until(D, U, end)]` attribute.
/// `D` is the type of the repeated data, `U` is the type of `end`,
/// `end` is the object that mark the end of the repetition of `U`s.
struct UntilParams(Ident, Ident, TokenStream);
impl Parse for UntilParams {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let data_type = input.parse()?;
        input.parse::<Token![,]>()?;
        let end_type = input.parse()?;
        input.parse::<Token![,]>()?;
        let until = input.parse()?;
        Ok(Self(data_type, end_type, until))
    }
}

/// Parsed Parameters for the `#[prefix(val)]` attribute of an enum variant.
struct PrefixParamsVariant(TokenStream);
impl Parse for PrefixParamsVariant {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let value = input.parse()?;
        Ok(Self(value))
    }
}

/// Parsed Parameters for the `#[prefix_type(T)]` attribute.
struct PrefixTypeParams(TokenStream);
impl Parse for PrefixTypeParams {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let data_type = input.parse()?;
        Ok(Self(data_type))
    }
}

/// All the attributes parameters for a struct/enum
#[derive(Default)]
struct ParamsStruct {
    pub prefix_type: Option<PrefixTypeParams>,
}

/// Parsed Parameters for the `#[prefix(val)]` attribute of an struct field.
struct PrefixParamsField(TokenStream);
impl Parse for PrefixParamsField {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let value = input.parse()?;
        Ok(Self(value))
    }
}

/// Parsed Parameters for the `#[suffix(val)]` attribute of an struct field.
struct SuffixParamsField(TokenStream);
impl Parse for SuffixParamsField {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let value = input.parse()?;
        Ok(Self(value))
    }
}

/// All the attributes parameters for a field
#[derive(Default)]
struct ParamsField {
    pub until: Option<UntilParams>,
    pub prefix: Option<PrefixParamsField>,
    pub suffix: Option<SuffixParamsField>,
}

/// All the attributes parameters for a variant
#[derive(Default)]
struct ParamsVariant {
    pub prefix: Option<PrefixParamsVariant>,
    pub default_variant: Option<()>,
}

impl ParamsStruct {
    fn parse(attrs: &[Attribute]) -> Self {
        let mut params = ParamsStruct::default();
        for attr in attrs {
            match &attr.meta {
                Meta::List(MetaList { path, .. }) if path.is_ident("until") => {
                    panic!("Structur/Enum cannot take the attribut 'until'")
                }
                Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => {
                    panic!("Structur/Enum cannot take the attribut 'prefix'")
                }
                Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix_type") => {
                    params.prefix_type = Some(syn::parse2(tokens.clone()).unwrap());
                }
                _ => (),
            }
        }
        params
    }
}
impl ParamsField {
    fn parse(attrs: &[Attribute]) -> Self {
        let mut params = ParamsField::default();
        for attr in attrs {
            match &attr.meta {
                Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => {
                    params.until = Some(syn::parse2(tokens.clone()).unwrap())
                }
                Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => {
                    params.prefix = Some(syn::parse2(tokens.clone()).unwrap())
                }
                Meta::List(MetaList { path, tokens, .. }) if path.is_ident("suffix") => {
                    params.suffix = Some(syn::parse2(tokens.clone()).unwrap())
                }
                Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
                    panic!("Fields cannot take the attribut 'prefix_type'")
                }
                _ => (),
            }
        }
        params
    }
}
impl ParamsVariant {
    fn parse(attrs: &[Attribute]) -> Self {
        let mut params = ParamsVariant::default();
        for attr in attrs {
            match &attr.meta {
                Meta::List(MetaList { path, .. }) if path.is_ident("until") => {
                    panic!("Variant cannot take the attribut 'until'")
                }
                Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => {
                    params.prefix = Some(syn::parse2(tokens.clone()).unwrap());
                }
                Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
                    panic!("Variant cannot take the attribut 'prefix_type'")
                }
                Meta::Path(path) if path.is_ident("default_variant") => {
                    params.default_variant = Some(());
                }
                _ => (),
            }
        }
        params
    }
}

/// Return the match statement an enum variant.
fn get_enum_match(variant: &Variant) -> TokenStream {
    match variant.fields {
        Fields::Named(ref fields) => {
            let recurse = fields.named.iter().map(|f| {
                let name = &f.ident;
                quote! { #name }
            });
            quote_spanned! { variant.span() =>
                {#(#recurse,)*}
            }
        }
        Fields::Unnamed(ref fields) => {
            let names: Vec<_> = fields
                .unnamed
                .iter()
                .enumerate()
                .map(|(i, _)| format_ident!("field{}", i))
                .collect();
            quote_spanned! { variant.span() =>
                (#(#names,)*)
            }
        }
        Fields::Unit => quote! {},
    }
}

/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::size`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
    let params = ParamsField::parse(&f.attrs);
    let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
        quote_spanned! { f.span() =>
            #stream.iter().collect::<Vec<_>>().len()
        }
    } else {
        quote_spanned! { f.span() => 0 }
    };
    let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix {
        quote_spanned! { f.span() =>
            #stream.iter().collect::<Vec<_>>().len()
        }
    } else {
        quote_spanned! { f.span() => 0 }
    };
    let main_stream = match (&f.ty, params) {
        (
            _,
            ParamsField {
                until: Some(UntilParams(d, u, until)),
                ..
            },
        ) => quote_spanned! { f.span() =>
            androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until)
        },
        (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
            #field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::<usize>()
        },
        (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
            androscalpel_serializer::Serializable::size(&#field_ref)
        },
    };
    quote_spanned! { f.span() => #prefix_stream + #main_stream + #suffix_stream }
}

/// Return the implementation of the [`androscalpel_serializer::Serializable::size`].
fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {
            Fields::Named(ref fields) => {
                let recurse = fields.named.iter().map(|f| {
                    let name = &f.ident;
                    get_implem_size_for_field(f, quote! { self.#name })
                });
                quote! {
                    0 #(+ #recurse)*
                }
            }
            Fields::Unnamed(ref fields) => {
                let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
                    let index = Index::from(i);
                    get_implem_size_for_field(f, quote! { self.#index })
                });
                quote! {
                    0 #(+ #recurse)*
                }
            }
            Fields::Unit => quote!(0),
        },
        Data::Enum(ref data) => {
            let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
                "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
            );
            let recurse = data.variants.iter().map(|v| {
                let ident = &v.ident;
                let params = ParamsVariant::parse(&v.attrs);
                // let prefix = params.prefix.expect(
                //     "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
                // );
                // let PrefixParamsVariant(val) = prefix;
                let prefix_quote = match params {
                    ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) },
                    ParamsVariant {prefix: None, default_variant: Some(()) } => quote! { 0 }, // The prefix is in the first field
                    ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"),
                    ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"),
                };
                let match_ = get_enum_match(v);
                let body = match v.fields {
                    Fields::Named(ref fields) => {
                        let recurse = fields.named.iter().map(|f| {
                            let name = &f.ident;
                            get_implem_size_for_field(f, quote! { *#name })
                        });
                        quote_spanned! { v.span() =>
                            //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )*
                            #prefix_quote #(+#recurse )*
                        }
                    }
                    Fields::Unnamed(ref fields) => {
                        let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
                            let ident = format_ident!("field{}", i);
                            get_implem_size_for_field(f, quote! { *#ident })
                        });
                        quote_spanned! { v.span() =>
                            //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )*
                            #prefix_quote #(+#recurse )*
                        }
                    }
                    //Fields::Unit => quote! { <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) },
                    Fields::Unit => quote! { #prefix_quote },
                };

                quote_spanned! { v.span() =>
                    Self::#ident #match_ => { #body },
                }
            });
            quote! { match self {
                #(#recurse)*
            }}
        }
        Data::Union(_) => unimplemented!(),
    }
}

/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::serialize`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
    let params = ParamsField::parse(&f.attrs);
    // TODO: Improve error handling
    let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
        quote_spanned! { f.span() =>
            for byte in #stream {
                <u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?;
            }
        }
    } else {
        quote_spanned! { f.span() => }
    };
    let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix {
        quote_spanned! { f.span() =>
            for byte in #stream {
                <u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?;
            }
        }
    } else {
        quote_spanned! { f.span() => }
    };
    let main_stream = match (&f.ty, params) {
        (
            _,
            ParamsField {
                until: Some(UntilParams(d, u, until)),
                ..
            },
        ) => quote_spanned! { f.span() =>
            androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?;
        },
        (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
            for x in #field_ref {
                androscalpel_serializer::Serializable::serialize(&x, output)?;
            }
        },
        (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
            androscalpel_serializer::Serializable::serialize(&#field_ref, output)?;
        },
    };
    quote_spanned! { f.span() =>
        #prefix_stream
        #main_stream
        #suffix_stream
    }
}
/// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`].
fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {
            Fields::Named(ref fields) => {
                let recurse = fields.named.iter().map(|f| {
                    let name = &f.ident;
                    get_implem_serialize_for_field(f, quote! { self.#name })
                });
                quote! {
                    #(#recurse)*
                    Ok(())
                }
            }
            Fields::Unnamed(ref fields) => {
                let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
                    let index = Index::from(i);
                    get_implem_serialize_for_field(f, quote! { self.#index })
                });
                quote! {
                    #(#recurse)*
                    Ok(())
                }
            }
            Fields::Unit => quote!(0),
        },
        Data::Enum(ref data) => {
            let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
                "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
            );
            let recurse = data.variants.iter().map(|v| {
                let ident = &v.ident;
                let params = ParamsVariant::parse(&v.attrs);
                //let prefix = params.prefix.expect(
                //    "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
                //);
                //let PrefixParamsVariant(val) = prefix;
	        let prefix_quote = match params {
                    ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; },
                    ParamsVariant {prefix: None, default_variant: Some(()) } => quote! {},
                    ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"),
                    ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"),
                };
                let match_ = get_enum_match(v);
                let body = match v.fields {
                    Fields::Named(ref fields) => {
                        if params.default_variant.is_some() &&  fields
                                .named
                                .first()
                                .unwrap()
                                .ident
                                .as_ref()
                                .expect(
                                    "Default variant must have a least one field for the prefix"
                                ) != "prefix" {
                            panic!(
                                "The first field of a named field variant must be named `prefix` and of the type of the prefix of the enum"
                            );
                        }
                        
                        let recurse = fields.named.iter().map(|f| {
                            let name = &f.ident;
                            get_implem_serialize_for_field(f, quote! { *#name })
                        });
                        quote_spanned! { v.span() =>
                            //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
                            #prefix_quote
                            #(#recurse)*
                        }
                    }
                    Fields::Unnamed(ref fields) => {
                        let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
                            let ident = format_ident!("field{}", i);
                            get_implem_serialize_for_field(f, quote! { *#ident })
                        });
                        quote_spanned! { v.span() =>
                            //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
                            #prefix_quote
                            #(#recurse)*
                        }
                    }
                    Fields::Unit => quote! {
                        //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
                        #prefix_quote
                    },
                };

                quote_spanned! { v.span() =>
                    Self::#ident #match_ => { #body },
                }
            });
            quote! {
                match self {
                    #(#recurse)*
                }
                Ok(())
            }
        }
        Data::Union(_) => unimplemented!(),
    }
}

/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::deserialize`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
    let params = ParamsField::parse(&f.attrs);
    let ty = &f.ty;
    // TODO: Improve error handling
    let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
        quote_spanned! { f.span() =>
            for byte in #stream {
                if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
                    return Err(androscalpel_serializer::Error::DeserializationError(
                        "Prefix do not match #stream".into()
                    ));
                }
            }
        }
    } else {
        quote_spanned! { f.span() => }
    };
    let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix {
        quote_spanned! { f.span() =>
            for byte in #stream {
                if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
                    return Err(androscalpel_serializer::Error::DeserializationError(
                        "Suffix do not match #stream".into()
                    ));
                }
            }
        }
    } else {
        quote_spanned! { f.span() => }
    };
    match (ty, params) {
        (
            _,
            ParamsField {
                until: Some(UntilParams(d, u, until)),
                ..
            },
        ) => quote_spanned! { f.span() =>
            #field_ref {
                #prefix_stream
                let val = <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?;
                #suffix_stream
                val
            },
        },
        (Type::Array(arr), ParamsField { until: None, .. }) => {
            let len = &arr.len;
            let arr_ty = &arr.elem;
            quote_spanned! { f.span() =>
                #field_ref {
                    #prefix_stream
                    let mut vec_ = vec![];
                    for _ in 0..(#len) {
                        vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?);
                    }
                    #suffix_stream
                    vec_.try_into().unwrap()
                },
            }
        }
        (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
            #field_ref {
                #prefix_stream
                let val = <#ty as androscalpel_serializer::Serializable>::deserialize(input)?;
                #suffix_stream
                val
            },
        },
    }
}

/// Return the implementation of the [`androscalpel_serializer::Serializable::deserialize`].
fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {
            Fields::Named(ref fields) => {
                let recurse = fields.named.iter().map(|f| {
                    let name = &f.ident;
                    get_implem_deserialize_for_field(f, quote! { #name: })
                });
                quote! {
                    Ok(
                        Self {
                            #(#recurse)*
                        }
                    )
                }
            }
            Fields::Unnamed(ref fields) => {
                let recurse = fields
                    .unnamed
                    .iter()
                    .map(|f| get_implem_deserialize_for_field(f, quote! {}));
                quote! {
                    Ok(Self(#(#recurse)*))
                }
            }
            Fields::Unit => quote!(0),
        },
        Data::Enum(ref data) => {
            let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
                "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
            );
            let recurse = data.variants.iter().map(|v| {
                let v_ident = &v.ident;
                let v_params = ParamsVariant::parse(&v.attrs);
                // Serialization of default value at the end
                if v_params.default_variant.is_some() {
                    return quote! {};
                }
                let PrefixParamsVariant(val) = v_params.prefix.expect(
                    "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
                );
                match v.fields {
                    Fields::Named(ref fields) => {
                        let recurse = fields.named.iter().map(|f| {
                            let name = &f.ident;
                            get_implem_deserialize_for_field(f, quote! { #name: })
                        });
                        quote_spanned! { v.span() =>
                            if #val == prefix {
                                return Ok(Self::#v_ident {#(#recurse)*});
                            }
                        }
                    }
                    Fields::Unnamed(ref fields) => {
                        let recurse = fields
                            .unnamed
                            .iter()
                            .map(|f| get_implem_deserialize_for_field(f, quote! {}));
                        quote_spanned! { v.span() =>
                            if #val == prefix {
                                return Ok(Self::#v_ident (#(#recurse)*));
                            }
                        }
                    }
                    Fields::Unit => quote_spanned! { v.span() =>
                        if #val == prefix {
                            return Ok(Self::#v_ident);
                        }
                    },
                }
            });
            let mut default_variant = None;
            for v in &data.variants {
                let v_params = ParamsVariant::parse(&v.attrs);
                if v_params.default_variant.is_some() && v_params.prefix.is_some() {
                    panic!("A variant cannot be both the default variant and check a prefix value");
                }
                if v_params.default_variant.is_some() && default_variant.is_some() {
                    panic!("An enum can only have one variant marked as `default_variant`");
                }
                if v_params.default_variant.is_some() {
                    default_variant = Some(v);
                }
            }
            let default_quote = match default_variant {
                None => quote! {
                    Err(androscalpel_serializer::Error::SerializationError(format!(
                        "Found prefix {:?} that did not match any variant.", prefix
                    )))
                },
                Some(v) => {
                    let v_ident = &v.ident;
                    match v.fields {
                        Fields::Named(ref fields) => {
                            // Skip 1 because the first field is the prefix
                            // TODO: check first field name is `prefix`?
                            let recurse = fields.named.iter().skip(1).map(|f| {
                                let name = &f.ident;
                                get_implem_deserialize_for_field(f, quote! { #name: })
                            });
                            quote_spanned! { v.span() =>
                                return Ok(Self::#v_ident {prefix, #(#recurse)*});
                            }
                        }
                        Fields::Unnamed(ref fields) => {
                            let recurse = fields
                                .unnamed
                                .iter()
                                .skip(1)
                                .map(|f| get_implem_deserialize_for_field(f, quote! {}));
                            quote_spanned! { v.span() =>
                                return Ok(Self::#v_ident (prefix, #(#recurse)*));
                            }
                        }
                        Fields::Unit => quote_spanned! { v.span() =>
                            return Ok(Self::#v_ident);
                        },
                    }
                }
            };
            quote! {
                let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?;
                #(#recurse)*

                //Err(androscalpel_serializer::Error::SerializationError(format!(
                //    "Found prefix {:?} that did not match any variant.", prefix
                //)))
                #default_quote
            }
        }
        Data::Union(_) => unimplemented!(),
    }
}
