use proc_macro2::TokenStream;
use quote::quote;
use syn::{Ident, LitInt};

pub fn impl_not(struct_name: &Ident, masking: &TokenStream) -> TokenStream {
    quote! {
        impl std::ops::Not for #struct_name {
            type Output = Self;
            fn not(self) -> Self {
                Self::from_bits((!self.0) #masking)
            }
        }
    }
}

pub fn impl_bitand(struct_name: &Ident, masking: &TokenStream) -> TokenStream {
    quote! {
        impl std::ops::BitAnd for #struct_name {
            type Output = Self;
            fn bitand(self, rhs: Self) -> Self {
                Self::from_bits((self.0 & rhs.0) #masking)
            }
        }
    }
}

pub fn impl_bitor(struct_name: &Ident, masking: &TokenStream) -> TokenStream {
    quote! {
        impl std::ops::BitOr for #struct_name {
            type Output = Self;
            fn bitor(self, rhs: Self) -> Self {
                Self::from_bits((self.0 | rhs.0) #masking)
            }
        }
    }
}

pub fn impl_bitxor(struct_name: &Ident, masking: &TokenStream) -> TokenStream {
    quote! {
        impl std::ops::BitXor for #struct_name {
            type Output = Self;
            fn bitxor(self, rhs: Self) -> Self {
                Self::from_bits((self.0 ^ rhs.0) #masking)
            }
        }
    }
}

pub fn impl_shl(struct_name: &Ident, shift: &Option<LitInt>, masking: &TokenStream) -> TokenStream {
    let rhs = if let Some(shift) = shift {
        quote! { rhs >> #shift }
    } else {
        quote! { rhs }
    };
    quote! {
        impl std::ops::Shl<usize> for #struct_name {
            type Output = Self;
            fn shl(self, rhs: usize) -> Self {
                Self::from_bits((self.0 << (#rhs)) #masking)
            }
        }
    }
}

pub fn impl_shr(struct_name: &Ident, shift: &Option<LitInt>, masking: &TokenStream) -> TokenStream {
    let rhs = if let Some(shift) = shift {
        quote! { rhs >> #shift }
    } else {
        quote! { rhs }
    };
    quote! {
        impl std::ops::Shr<usize> for #struct_name {
            type Output = Self;
            fn shr(self, rhs: usize) -> Self {
                Self::from_bits((self.0 >> (#rhs)) #masking)
            }
        }
    }
}
