mod named;
mod newtype;

use crate::types::{Element, Field, Fields};

use proc_macro2::TokenStream;
use quote::quote;

pub fn impl_write(element: Element) -> TokenStream {
    match element {
        Element::Enum {
            name: ele_name,
            variants,
        } => {
            let branches = variants.iter().map(|variant| match variant {
                Fields::Named { name, fields, .. } => {
                    let bindings = fields.iter().map(|field| match field {
                        Field::Attribute { bind, name, .. }
                        | Field::Child { bind, name, .. }
                        | Field::Text { bind, name, .. }
                        | Field::FlattenText { bind, name, .. } => quote!( #name: #bind ),
                    });
                    quote!( #ele_name::#name { #( #bindings ),* } )
                }
                Fields::Newtype { name, .. } => quote!( #ele_name::#name(__inner) ),
            });

            let read = variants.iter().map(|variant| match variant {
                Fields::Named {
                    tag, name, fields, ..
                } => named::write(tag, quote!( #ele_name::#name ), fields),
                Fields::Newtype { name, .. } => newtype::write(quote!( #ele_name::#name )),
            });

            quote! {
                match self {
                    #( #branches => { #read }, )*
                }
            }
        }

        Element::Struct {
            name: ele_name,
            fields,
        } => match fields {
            Fields::Named {
                tag, name, fields, ..
            } => {
                let bindings = fields.iter().map(|field| match field {
                    Field::Attribute { bind, name, .. }
                    | Field::Child { bind, name, .. }
                    | Field::Text { bind, name, .. }
                    | Field::FlattenText { bind, name, .. } => quote!( #name: #bind ),
                });

                let read = named::write(&tag, quote!(#name), &fields);

                quote! {
                    let #ele_name { #( #bindings ),* } = self;

                    #read
                }
            }
            Fields::Newtype { name, .. } => {
                let read = newtype::write(quote!(#name));

                quote! {
                    let __inner = &self.0;

                    #read
                }
            }
        },
    }
}
