use crate::*;
use brec_consts::*;
use proc_macro2::TokenStream;
use quote::quote;

impl Crc for Block {
    fn generate(&self) -> Result<TokenStream, E> {
        let block_name = self.name();
        let referred_name = self.referred_name();
        if self.attrs.is_no_crc() {
            return Ok(quote! {

                impl brec::CrcU32 for #block_name {

                    fn crc(&self) -> [u8; 4] {
                        [0u8, 0u8, 0u8, 0u8, ]
                    }

                }

                impl brec::CrcU32 for #referred_name<'_> {

                    fn crc(&self) -> [u8; 4] {
                        [0u8, 0u8, 0u8, 0u8, ]
                    }

                }

            });
        }
        let mut hash_packet = Vec::new();
        let mut hash_referred = Vec::new();
        for field in self.fields.iter().filter(|f| !f.injected) {
            let value = field.to_bytes(true)?;
            let referred = field.to_bytes(false)?;
            hash_packet.push(quote! {
                hasher.update(#value);
            });
            hash_referred.push(quote! {
                hasher.update(#referred);
            });
        }
        Ok(quote! {

            impl brec::CrcU32 for #block_name {

                fn crc(&self) -> [u8; 4] {
                    let mut hasher = brec::crc32fast::Hasher::new();
                    #(#hash_packet)*
                    hasher.finalize().to_le_bytes()
                }

            }

            impl brec::CrcU32 for #referred_name<'_> {

                fn crc(&self) -> [u8; 4] {
                    let mut hasher = brec::crc32fast::Hasher::new();
                    #(#hash_referred)*
                    hasher.finalize().to_le_bytes()
                }

            }

        })
    }
}

impl Size for Block {
    fn generate(&self) -> TokenStream {
        let block_name = self.name();
        let mut size = 0u64;
        for field in self.fields.iter() {
            size += field.size() as u64;
        }
        if cfg!(feature = "resilient") {
            size += BLOCK_SIZE_FIELD_LEN as u64;
        }
        quote! {

            impl brec::StaticSize for #block_name {

                fn ssize() -> u64 {
                    #size
                }

            }

        }
    }
}
