use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Error, Ident, Path, Result};

/// Wrapper around strings to allow better interaction with attributes
// See serde_derive/src/internals/symbol.rs for 'inspiration'
pub(crate) struct Symbol(&'static str);

/// The name of all attributes used by this derive crate
pub(crate) const JADED: Symbol = Symbol("jaded");

/// The java field to read from when deserializing
pub(crate) const FIELD_NAME: Symbol = Symbol("field");
/// The number of the annotation to use when extracting custom data
pub(crate) const ANNO: Symbol = Symbol("anno");
/// The method to use when extracting custom data
pub(crate) const EXTRACT: Symbol = Symbol("extract");

/// Whether to rename all fields to match the camelCase Java convention
pub(crate) const RENAME: Symbol = Symbol("rename");
/// The FQCN to check when deserializing a Java object
pub(crate) const CLASS: Symbol = Symbol("class");

impl PartialEq<Symbol> for Ident {
    fn eq(&self, word: &Symbol) -> bool {
        self == word.0
    }
}

impl<'a> PartialEq<Symbol> for &'a Ident {
    fn eq(&self, word: &Symbol) -> bool {
        *self == word.0
    }
}

impl PartialEq<Symbol> for Path {
    fn eq(&self, word: &Symbol) -> bool {
        self.is_ident(word.0)
    }
}

impl<'a> PartialEq<Symbol> for &'a Path {
    fn eq(&self, word: &Symbol) -> bool {
        self.is_ident(word.0)
    }
}

pub(crate) struct Attr<T> {
    value: Option<T>,
    tokens: TokenStream,
}

impl<T> Attr<T> {
    pub fn new() -> Self {
        Self {
            value: None,
            tokens: TokenStream::new(),
        }
    }
    pub fn set<S>(&mut self, att: &S, value: T) -> Result<()>
    where
        S: ToTokens,
    {
        match self.value {
            Some(_) => Err(Error::new_spanned(&self.tokens, "Duplicate attribute")),
            None => {
                self.value = Some(value);
                self.tokens = att.into_token_stream();
                Ok(())
            }
        }
    }
    pub fn get(self) -> Option<T> {
        self.value
    }
    pub fn is_set(&self) -> bool {
        self.value.is_some()
    }
    pub fn tokens(&self) -> &TokenStream {
        &self.tokens
    }
}
