use crate::{kw, Spanned};
use proc_macro2::Span;
use std::{
    fmt,
    ops::{Deref, DerefMut},
};
use syn::{
    parse::{Lookahead1, Parse, ParseStream},
    Result,
};
macro_rules! str_lit {
    ($(#[$attr:meta])* $vis:vis struct $name:ident($t:ty) $(: $kw:ident)?) => {
        #[derive(Clone, Debug)]
        $vis struct $name {
            $vis values: Vec<$t>,
        }
        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                for (i, value) in self.values.iter().enumerate() {
                    if i > 0 {
                        f.write_str(" ")?;
                    }
                    $(
                        f.write_str(stringify!($kw))?;
                    )?
                    f.write_str(&value.value())?;
                }
                Ok(())
            }
        }
        impl Parse for $name {
            fn parse(input: ParseStream<'_>) -> Result<Self> {
                let mut values = Vec::new();
                let mut first = true;
                while first || Self::peek(&input.lookahead1()) {
                    first = false;
                    values.push(input.parse()?);
                }
                Ok(Self { values })
            }
        }
        impl Spanned for $name {
            fn span(&self) -> Span {
                self.values.span()
            }
            fn set_span(&mut self, span: Span) {
                self.values.set_span(span);
            }
        }
        impl $name {
            pub fn peek(lookahead: &Lookahead1<'_>) -> bool {
                $(lookahead.peek(kw::$kw) || )? lookahead.peek(syn::LitStr)
            }
            pub fn parse_opt(input: ParseStream<'_>) -> Result<Option<Self>> {
                if Self::peek(&input.lookahead1()) {
                    input.parse().map(Some)
                } else {
                    Ok(None)
                }
            }
            pub fn value(&self) -> String {
                self.values.iter().map(|v| v.value()).collect()
            }
        }
    };
}
macro_rules! wrap_str {
    ($(#[$attr:meta])* $vis:vis struct $name:ident { $token:ident : kw::$kw:ident $(,)? }) => {
        $(#[$attr])*
        #[derive(Clone)]
        $vis struct $name {
            $vis $token: kw::$kw,
            $vis value: syn::LitStr,
        }
        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.debug_struct(stringify!($name))
                    .field("value", &self.value)
                    .finish()
            }
        }
        impl Deref for $name {
            type Target = syn::LitStr;
            #[inline]
            fn deref(&self) -> &Self::Target {
                &self.value
            }
        }
        impl DerefMut for $name {
            #[inline]
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.value
            }
        }
        impl Parse for $name {
            fn parse(input: ParseStream<'_>) -> Result<Self> {
                Ok(Self {
                    $token: input.parse()?,
                    value: input.parse()?,
                })
            }
        }
        impl Spanned for $name {
            fn span(&self) -> Span {
                let span = self.$token.span;
                span.join(self.value.span()).unwrap_or(span)
            }
            fn set_span(&mut self, span: Span) {
                self.$token.span = span;
                self.value.set_span(span);
            }
        }
    };
}
str_lit! {
    pub struct LitStr(syn::LitStr)
}
str_lit! {
    pub struct LitUnicodeStr(UnicodeStr): unicode
}
wrap_str! {
    pub struct UnicodeStr {
        unicode_token: kw::unicode,
    }
}
str_lit! {
    pub struct LitHexStr(HexStr): hex
}
wrap_str! {
    pub struct HexStr {
        hex_token: kw::hex,
    }
}