use std::{
    borrow::Borrow,
    cmp::Ordering,
    convert::TryFrom,
    fmt,
    hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
    ops::Deref,
    sync::Arc,
};
use crate::{
    ast::{DisplayEnv, IdentEnv},
    pos::{BytePos, Span},
};
#[derive(Clone, Eq, Default)]
pub struct Symbol(Arc<SymbolInner>);
#[derive(Debug, Default, Eq, PartialEq, Hash)]
struct SymbolInner {
    global: bool,
    location: Option<u32>,
    name: NameBuf,
}
#[derive(Debug, Default, Eq, PartialEq, Hash)]
pub struct SymbolData<N = NameBuf> {
    pub global: bool,
    pub location: Option<(u32, u32)>,
    pub name: N,
}
#[cfg(feature = "serde")]
mod serialization {
    use super::*;
    use crate::serde::de::DeserializeState;
    use crate::serde::ser::SerializeState;
    use crate::serde::{Deserialize, Deserializer, Serialize, Serializer};
    use crate::serialization::SeSeed;
    impl<'de> Deserialize<'de> for Symbol {
        fn deserialize<D>(deserializer: D) -> Result<Symbol, D::Error>
        where
            D: Deserializer<'de>,
        {
            use std::borrow::Cow;
            Cow::<str>::deserialize(deserializer).map(|s| Symbol::from(&s[..]))
        }
    }
    impl<'de, Id, T> DeserializeState<'de, crate::serialization::Seed<Id, T>> for Symbol {
        fn deserialize_state<D>(
            seed: &mut crate::serialization::Seed<Id, T>,
            deserializer: D,
        ) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            use crate::serde::de::DeserializeSeed;
            use crate::serialization::SharedSeed;
            let seed = SharedSeed::new(seed);
            seed.deserialize(deserializer)
                .map(|s: String| Symbol::from(&s[..]))
        }
    }
    impl Serialize for Symbol {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            serializer.collect_str(self)
        }
    }
    impl SerializeState<SeSeed> for Symbol {
        fn serialize_state<S>(&self, serializer: S, seed: &SeSeed) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            {
                crate::serialization::shared::serialize(self, serializer, seed)
            }
        }
    }
}
impl Deref for Symbol {
    type Target = SymbolRef;
    fn deref(&self) -> &SymbolRef {
        unsafe { &*(&*self.0.name.0 as *const str as *const SymbolRef) }
    }
}
impl Borrow<SymbolRef> for Symbol {
    fn borrow(&self) -> &SymbolRef {
        &**self
    }
}
impl AsRef<str> for Symbol {
    fn as_ref(&self) -> &str {
        self.as_pretty_str()
    }
}
impl fmt::Debug for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", &**self)
    }
}
impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_pretty_str())
    }
}
impl PartialEq for Symbol {
    fn eq(&self, other: &Symbol) -> bool {
        **self == **other
    }
}
impl PartialEq<SymbolRef> for Symbol {
    fn eq(&self, other: &SymbolRef) -> bool {
        **self == *other
    }
}
impl PartialEq<Symbol> for SymbolRef {
    fn eq(&self, other: &Symbol) -> bool {
        *self == **other
    }
}
impl PartialOrd for Symbol {
    fn partial_cmp(&self, other: &Symbol) -> Option<Ordering> {
        (**self).partial_cmp(other)
    }
}
impl Ord for Symbol {
    fn cmp(&self, other: &Symbol) -> Ordering {
        (**self).cmp(other)
    }
}
impl Hash for Symbol {
    fn hash<H: Hasher>(&self, h: &mut H) {
        (**self).hash(h)
    }
}
impl From<String> for Symbol {
    fn from(name: String) -> Symbol {
        Symbol::from(&*name)
    }
}
impl From<(Symbol, Span<BytePos>)> for Symbol {
    fn from((name, _): (Symbol, Span<BytePos>)) -> Symbol {
        name
    }
}
impl<N> From<SymbolData<N>> for Symbol
where
    N: Into<NameBuf>,
{
    fn from(name: SymbolData<N>) -> Symbol {
        Symbol(Arc::new(SymbolInner::new(name)))
    }
}
impl From<&'_ str> for Symbol {
    fn from(name: &str) -> Symbol {
        Symbol(Arc::new(SymbolInner::new(SymbolData::<NameBuf>::from(
            name,
        ))))
    }
}
impl<'a, N> From<&'a str> for SymbolData<N>
where
    N: From<&'a str>,
{
    fn from(mut name: &'a str) -> SymbolData<N> {
        let global = name.starts_with('@');
        let location = match name
            .bytes()
            .rposition(|b| (b < b'0' || b > b'9') && b != b'_')
        {
            Some(i) if i != 0 && name.as_bytes()[i] == b'@' => {
                let loc = &name[(i + 1)..];
                let mut iter = loc.split('_');
                let line = iter.next();
                let col = iter.next();
                let opt = line
                    .and_then(|line| line.parse::<u32>().ok())
                    .and_then(|line| {
                        col.and_then(|col| col.parse::<u32>().ok())
                            .map(|col| (line, col))
                    });
                name = &name[..i];
                opt
            }
            _ => None,
        };
        if global {
            name = &name[1..];
        }
        SymbolData {
            global,
            location,
            name: name.into(),
        }
    }
}
#[derive(Eq)]
#[cfg_attr(feature = "serde_derive", derive(SerializeState))]
#[cfg_attr(
    feature = "serde_derive",
    serde(serialize_state = "crate::serialization::SeSeed")
)]
pub struct SymbolRef(str);
impl fmt::Debug for SymbolRef {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:p}:{}", self, &self.0)
    }
}
impl fmt::Display for SymbolRef {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.0)
    }
}
impl PartialEq for SymbolRef {
    fn eq(&self, other: &SymbolRef) -> bool {
        self.ptr() == other.ptr()
    }
}
impl PartialOrd for SymbolRef {
    fn partial_cmp(&self, other: &SymbolRef) -> Option<Ordering> {
        self.ptr().partial_cmp(&other.ptr())
    }
}
impl Ord for SymbolRef {
    fn cmp(&self, other: &SymbolRef) -> Ordering {
        self.ptr().cmp(&other.ptr())
    }
}
impl Hash for SymbolRef {
    fn hash<H: Hasher>(&self, h: &mut H) {
        self.ptr().hash(h)
    }
}
impl Symbol {
    pub fn strong_count(sym: &Symbol) -> usize {
        Arc::strong_count(&sym.0)
    }
    pub fn is_global(&self) -> bool {
        self.0.global
    }
    pub fn is_primitive(&self) -> bool {
        self.0.name.0.starts_with('#')
    }
    pub fn name_eq(&self, other: &Symbol) -> bool {
        self.name() == other.name()
    }
    pub fn name(&self) -> &Name {
        Name::new(self.as_pretty_str())
    }
    pub fn as_pretty_str(&self) -> &str {
        &self.0.name.0[self.0.global as usize
            ..self
                .0
                .location
                .map_or_else(|| self.0.name.len(), |l| l as usize)]
    }
    pub fn as_data(&self) -> SymbolData<&Name> {
        SymbolData::from(&self.0.name.0[..])
    }
}
impl SymbolRef {
    #[inline]
    pub fn new<N: ?Sized + AsRef<str>>(n: &N) -> &SymbolRef {
        unsafe { &*(Name::new(n) as *const Name as *const SymbolRef) }
    }
    pub fn name_eq(&self, other: &SymbolRef) -> bool {
        self.name() == other.name()
    }
    pub fn is_global(&self) -> bool {
        self.0.as_bytes().first() == Some(&b'@')
    }
    pub fn as_pretty_str(&self) -> &str {
        let mut s = &self.0;
        if let Some(b'@') = s.as_bytes().first() {
            s = &s[1..];
        }
        Name::new(s).as_pretty_str()
    }
    pub fn as_str(&self) -> &str {
        &self.0
    }
    pub fn name(&self) -> &Name {
        Name::new(Name::new(&self.0).as_pretty_str())
    }
    pub fn raw_name(&self) -> &Name {
        Name::new(&self.0)
    }
    pub fn declared_name(&self) -> &str {
        self.name().declared_name()
    }
    pub fn definition_name(&self) -> &str {
        Name::new(&self.0).definition_name()
    }
    fn ptr(&self) -> *const () {
        self.0.as_bytes().as_ptr() as *const ()
    }
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "serde_derive", derive(DeserializeState))]
#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "S"))]
#[cfg_attr(feature = "serde_derive", serde(de_parameters = "S"))]
pub struct NameBuf(String);
#[derive(Debug, Eq, Hash, Ord, PartialOrd)]
pub struct Name(str);
impl PartialEq for Name {
    fn eq(&self, other: &Name) -> bool {
        self.0.as_ptr() == other.0.as_ptr() || self.0 == other.0
    }
}
impl ToOwned for Name {
    type Owned = NameBuf;
    fn to_owned(&self) -> NameBuf {
        NameBuf::from(self)
    }
}
pub struct Components<'a>(&'a str);
impl<'a> Iterator for Components<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.0.is_empty() {
            None
        } else {
            Some(match self.0.find('.') {
                Some(i) => {
                    let (before, after) = self.0.split_at(i);
                    self.0 = &after[1..];
                    before
                }
                None => {
                    let s = self.0;
                    self.0 = "";
                    s
                }
            })
        }
    }
}
impl<'a> From<&'a str> for &'a Name {
    fn from(s: &'a str) -> &'a Name {
        Name::new(s)
    }
}
impl Name {
    #[inline]
    pub fn new<N: ?Sized + AsRef<str>>(n: &N) -> &Name {
        unsafe { &*(n.as_ref() as *const str as *const Name) }
    }
    pub fn as_pretty_str(&self) -> &str {
        Self::strip_position_suffix(&self.0)
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn as_str(&self) -> &str {
        &self.0
    }
    pub fn components(&self) -> Components {
        Components(&self.0)
    }
    pub fn module(&self) -> &Name {
        let s = self.0.trim_end_matches(|c| c != '.');
        Name::new(s.trim_end_matches('.'))
    }
    pub fn name(&self) -> &Name {
        self.0
            .rfind('.')
            .map_or(self, |i| Name::new(&self.0[i + 1..]))
    }
    pub fn declared_name(&self) -> &str {
        let name = self.definition_name();
        name.rsplit('.').next().unwrap_or(name)
    }
    pub fn definition_name(&self) -> &str {
        Self::strip_position_suffix(if self.0.as_bytes().get(0) == Some(&b'@') {
            &self.0[1..]
        } else {
            &self.0
        })
    }
    fn strip_position_suffix(name: &str) -> &str {
        let x = match name
            .bytes()
            .rposition(|b| (b < b'0' || b > b'9') && b != b'_')
        {
            Some(i) if name.as_bytes()[i] == b'@' => &name[..i],
            _ => name,
        };
        x
    }
}
impl NameBuf {
    #[inline]
    pub fn new<T>(name: T) -> NameBuf
    where
        T: Into<String>,
    {
        NameBuf(name.into())
    }
}
impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.0)
    }
}
impl fmt::Display for NameBuf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
impl AsRef<Name> for str {
    fn as_ref(&self) -> &Name {
        Name::new(self)
    }
}
impl AsRef<Name> for String {
    fn as_ref(&self) -> &Name {
        Name::new(self)
    }
}
impl AsRef<Name> for Name {
    fn as_ref(&self) -> &Name {
        self
    }
}
impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        &self.0
    }
}
impl AsRef<str> for NameBuf {
    fn as_ref(&self) -> &str {
        &*self.0
    }
}
impl AsRef<Name> for NameBuf {
    fn as_ref(&self) -> &Name {
        self
    }
}
impl Borrow<Name> for NameBuf {
    fn borrow(&self) -> &Name {
        self
    }
}
impl Deref for NameBuf {
    type Target = Name;
    fn deref(&self) -> &Name {
        Name::new(self)
    }
}
impl<'a> From<&'a str> for NameBuf {
    fn from(name: &'a str) -> NameBuf {
        NameBuf(String::from(name))
    }
}
impl From<String> for NameBuf {
    fn from(name: String) -> NameBuf {
        NameBuf(name)
    }
}
impl From<NameBuf> for String {
    fn from(name: NameBuf) -> String {
        name.0
    }
}
impl<'a> From<&'a Name> for NameBuf {
    fn from(name: &'a Name) -> NameBuf {
        NameBuf::from(&name.0)
    }
}
impl SymbolInner {
    fn new<N>(data: SymbolData<N>) -> SymbolInner
    where
        N: Into<NameBuf>,
    {
        let SymbolData {
            global,
            location,
            name,
        } = data;
        let mut name: NameBuf = name.into();
        if global {
            name.0.insert(0, '@');
        }
        let inner_location = location.map(|(x, y)| {
            let loc = u32::try_from(name.len()).unwrap();
            use std::fmt::Write;
            write!(name.0, "@{}_{}", x, y).unwrap();
            loc
        });
        SymbolInner {
            global,
            location: inner_location,
            name,
        }
    }
}
#[derive(Debug, Default)]
pub struct Symbols {
    indexes:
        hashbrown::HashMap<SymbolData<&'static Name>, Symbol, BuildHasherDefault<fnv::FnvHasher>>,
}
impl Symbols {
    pub fn new() -> Symbols {
        Symbols {
            indexes: Default::default(),
        }
    }
    pub fn simple_symbol<N>(&mut self, name: N) -> Symbol
    where
        N: Into<NameBuf> + AsRef<Name>,
    {
        self.symbol(SymbolData {
            global: false,
            location: None,
            name,
        })
    }
    pub fn symbol<N>(&mut self, name: SymbolData<N>) -> Symbol
    where
        N: Into<NameBuf> + AsRef<Name>,
    {
        let name_ref = SymbolData {
            global: name.global,
            location: name.location,
            name: name.name.as_ref(),
        };
        let mut hasher = self.indexes.hasher().build_hasher();
        name_ref.hash(&mut hasher);
        let hash = hasher.finish();
        match self
            .indexes
            .raw_entry_mut()
            .from_hash(hash, |key| *key == name_ref)
        {
            hashbrown::hash_map::RawEntryMut::Occupied(entry) => entry.get().clone(),
            hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
                let SymbolData {
                    global,
                    location,
                    name,
                } = name;
                let mut name: NameBuf = name.into();
                if global {
                    name.0.insert(0, '@');
                }
                let inner_location = location.map(|(x, y)| {
                    let loc = u32::try_from(name.len()).unwrap();
                    use std::fmt::Write;
                    write!(name.0, "@{}_{}", x, y).unwrap();
                    loc
                });
                let key = unsafe { &*(name.definition_name() as *const str as *const Name) };
                let s = Symbol(Arc::new(SymbolInner {
                    global,
                    location: inner_location,
                    name,
                }));
                entry
                    .insert_hashed_nocheck(
                        hash,
                        SymbolData {
                            global,
                            location,
                            name: key,
                        },
                        s,
                    )
                    .1
                    .clone()
            }
        }
    }
    pub fn contains_name<N>(&mut self, name: N) -> bool
    where
        N: AsRef<Name>,
    {
        let s = SymbolData::<&Name>::from(name.as_ref().as_str());
        self.indexes.contains_key(&s)
    }
    pub fn len(&self) -> usize {
        self.indexes.len()
    }
    pub fn is_empty(&self) -> bool {
        self.indexes.is_empty()
    }
}
#[derive(Debug)]
pub struct SymbolModule<'a> {
    symbols: &'a mut Symbols,
    module: NameBuf,
}
impl<'a> SymbolModule<'a> {
    pub fn new(module: String, symbols: &'a mut Symbols) -> SymbolModule<'a> {
        SymbolModule {
            symbols,
            module: NameBuf(module),
        }
    }
    pub fn simple_symbol<N>(&mut self, name: N) -> Symbol
    where
        N: Into<NameBuf> + AsRef<Name>,
    {
        self.symbols.simple_symbol(name)
    }
    pub fn symbol<N>(&mut self, name: SymbolData<N>) -> Symbol
    where
        N: Into<NameBuf> + AsRef<Name>,
    {
        self.symbols.symbol(name)
    }
    pub fn contains_name<N>(&mut self, name: N) -> bool
    where
        N: AsRef<Name>,
    {
        self.symbols.contains_name(name.as_ref())
    }
    pub fn scoped_symbol(&mut self, name: &str) -> Symbol {
        let len = self.module.0.len();
        self.module.0.push('.');
        self.module.0.push_str(name);
        let symbol = self.symbols.symbol(SymbolData {
            global: false,
            location: None,
            name: &*self.module,
        });
        self.module.0.truncate(len);
        symbol
    }
    pub fn module(&self) -> &Name {
        &self.module
    }
    pub fn len(&self) -> usize {
        self.symbols.len()
    }
    pub fn is_empty(&self) -> bool {
        self.symbols.is_empty()
    }
    pub fn symbols(&mut self) -> &mut Symbols {
        self.symbols
    }
}
impl DisplayEnv for Symbols {
    type Ident = Symbol;
    fn string<'a>(&'a self, ident: &'a Self::Ident) -> &'a str {
        ident.as_ref()
    }
}
impl IdentEnv for Symbols {
    fn from_str(&mut self, s: &str) -> Symbol {
        self.symbol(SymbolData::<&Name>::from(s))
    }
}
impl<'s> DisplayEnv for SymbolModule<'s> {
    type Ident = Symbol;
    fn string<'a>(&'a self, ident: &'a Self::Ident) -> &'a str {
        self.symbols.string(ident)
    }
}
impl<'a> IdentEnv for SymbolModule<'a> {
    fn from_str(&mut self, s: &str) -> Symbol {
        self.symbol(SymbolData::<&Name>::from(s))
    }
}
impl<P> From<crate::pos::Spanned<Symbol, P>> for Symbol {
    fn from(s: crate::pos::Spanned<Symbol, P>) -> Self {
        s.value
    }
}