use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, punctuated::Punctuated, Token};

use super::{
    common::{Attributes, TypeRepr},
    document::DocumentItemAttributes,
};

#[derive(Clone)]
pub enum BitfieldDefaultValue {
    Fields(Punctuated<syn::Ident, Token![|]>),
    Empty(Token![_]),
}

#[derive(Clone)]
pub enum BitfieldRange {
    Single(syn::LitInt),
    Range(syn::LitInt, syn::LitInt),
}

#[derive(Clone)]
pub struct BitfieldTypeField {
    pub attributes: Attributes,
    pub name: syn::Ident,
    pub range: BitfieldRange,
}

#[derive(Clone)]
pub struct BitfieldType {
    pub attributes: Attributes,
    pub name: syn::Ident,
    pub repr: TypeRepr,
    pub fields: Punctuated<BitfieldTypeField, Token![,]>,
}

impl DocumentItemAttributes for BitfieldType {
    fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
        self.attributes.extend(attrs);
    }
}

impl syn::parse::Parse for BitfieldDefaultValue {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        if input.peek(Token![_]) {
            let empty = input.parse()?;
            Ok(Self::Empty(empty))
        } else {
            let items = input.parse_terminated(syn::Ident::parse, Token![|])?;
            Ok(Self::Fields(items))
        }
    }
}

impl syn::parse::Parse for BitfieldRange {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let start = input.parse()?;
        if input.peek(Token![..]) {
            input.parse::<Token![..]>()?;
            let end = input.parse()?;
            Ok(Self::Range(start, end))
        } else {
            Ok(Self::Single(start))
        }
    }
}

impl syn::parse::Parse for BitfieldTypeField {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let attributes = input.parse()?;
        let name = input.parse()?;
        input.parse::<Token![:]>()?;
        let range = input.parse()?;
        Ok(Self {
            attributes,
            name,
            range,
        })
    }
}

impl syn::parse::Parse for BitfieldType {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let fields;
        let attributes = input.parse()?;
        let name = input.parse()?;
        let repr = input.parse()?;
        braced!(fields in input);
        let fields = fields.parse_terminated(BitfieldTypeField::parse, Token![,])?;

        Ok(Self {
            attributes,
            name,
            repr,
            fields,
        })
    }
}

impl ToTokens for BitfieldRange {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Range(start, end) => tokens.extend(quote!(#start..#end)),
            Self::Single(offset) => tokens.extend(quote!(#offset)),
        }
    }
}

impl ToTokens for BitfieldType {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self {
            attributes,
            name,
            repr,
            fields,
        } = self;
        tokens.extend(quote! {
            #attributes
            bitfield #name #repr {
                #fields
            }
        })
    }
}

impl ToTokens for BitfieldTypeField {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self {
            attributes,
            name,
            range,
        } = self;
        tokens.extend(quote! {
            #attributes
            #name: #range
        })
    }
}
