//! Kago-Core (籠コア)
//!
//! Kago で用いられる実装のマクロを提供する。

#![warn(
    missing_docs,
    rustdoc::missing_crate_level_docs,
    unused_results,
    clippy::complexity,
    clippy::perf,
    clippy::style
)]
#![deny(clippy::correctness, clippy::suspicious)]

mod bit_ops;
mod bounded;
mod core;
mod num;
mod num_cast;
mod num_ops;
mod prim_int;
mod sign;

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::Ident;

/// 実装
#[proc_macro]
pub fn limited_bits(tokens: TokenStream) -> TokenStream {
    limited_bits_impl(tokens.into()).into()
}

fn limited_bits_impl(_: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    let mut impls = Vec::new();
    for n in 1..=128 {
        for is_signed in [false, true].iter() {
            let flag_name = core::feature_name(n, *is_signed);
            let module_name = Ident::new(format!("bit_{}", flag_name).as_str(), Span::call_site());
            let struct_name = core::struct_name(n, *is_signed);
            let ty = core::inner_type(n, *is_signed);
            let shift = core::shift(n);
            let masking = core::masking(&shift);
            let struct_impl = core::impl_struct(&struct_name, &ty, &shift);
            let debug_impl = core::impl_debug(&struct_name, &shift);
            let add_impl = num_ops::impl_add(&struct_name);
            let sub_impl = num_ops::impl_sub(&struct_name);
            let mul_impl = num_ops::impl_mul(&struct_name, &shift);
            let div_impl = num_ops::impl_div(&struct_name, &shift);
            let rem_impl = num_ops::impl_rem(&struct_name);
            let checked_add_impl = num_ops::impl_checked_add(&struct_name);
            let checked_sub_impl = num_ops::impl_checked_sub(&struct_name);
            let checked_mul_impl = num_ops::impl_checked_mul(&struct_name, &ty, &shift);
            let checked_div_impl = num_ops::impl_checked_div(&struct_name, &ty, &shift);
            let checked_rem_impl = num_ops::impl_checked_rem(&struct_name, &ty);
            let saturating_impl = num_ops::impl_saturating(&struct_name, &ty, &masking);
            let zero_impl = num::impl_zero(&struct_name);
            let one_impl = num::impl_one(&struct_name);
            let num_impl = num::impl_num(&struct_name, &ty);
            let to_primitive_impl = num_cast::impl_to_primitive(&struct_name, &ty, &shift);
            let num_cast_impl = num_cast::impl_num_cast(&struct_name, &ty, &shift);
            let bounded_impl = bounded::impl_bounded(&struct_name, &ty, &shift);
            let not_impl = bit_ops::impl_not(&struct_name, &masking);
            let bitand_impl = bit_ops::impl_bitand(&struct_name, &masking);
            let bitor_impl = bit_ops::impl_bitor(&struct_name, &masking);
            let bitxor_impl = bit_ops::impl_bitxor(&struct_name, &masking);
            let shl_impl = bit_ops::impl_shl(&struct_name, &shift, &masking);
            let shr_impl = bit_ops::impl_shr(&struct_name, &shift, &masking);
            let prim_int_impl = prim_int::impl_prim_int(&struct_name, &ty, &shift, &masking);
            let sign_impl = sign::impl_sign(&struct_name, *is_signed);
            impls.push(quote! {
                #[cfg(feature = #flag_name)]
                pub use #module_name::#struct_name;

                #[cfg(feature = #flag_name)]
                mod #module_name {
                    #struct_impl
                    #debug_impl
                    #add_impl
                    #checked_add_impl
                    #sub_impl
                    #checked_sub_impl
                    #mul_impl
                    #checked_mul_impl
                    #div_impl
                    #checked_div_impl
                    #rem_impl
                    #checked_rem_impl
                    #saturating_impl
                    #zero_impl
                    #one_impl
                    #num_impl
                    #to_primitive_impl
                    #num_cast_impl
                    #bounded_impl
                    #not_impl
                    #bitand_impl
                    #bitor_impl
                    #bitxor_impl
                    #shl_impl
                    #shr_impl
                    #prim_int_impl
                    #sign_impl
                }
            });
        }
    }
    quote! {
        #(#impls)*
    }
}
