use std::rc::Rc;

use super::{Environment, Error, Node, NodeEnum, Precedence};
use crate::environment::EnvironmentInternalSymbolKey;

#[derive(Debug, Clone, PartialEq)]
pub struct Symbol {
    value: EnvironmentInternalSymbolKey,
}

impl Node for Symbol {
    fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
        if let Some(value) = env.get(&self.value) {
            // TODO: Detect cyclic references and make sure they get
            // deleted at some point
            Ok(value.clone())
        } else {
            Ok(Rc::new(self.clone().into()))
        }
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        if let Some(env) = env {
            if let Some(str) = env.id_to_str(&self.value) {
                str.clone()
            } else {
                env.id_to_str(&self.value)
                    .cloned()
                    .unwrap_or(format!("{{#SYMBOL {:?}}}", self.value))
            }
        } else {
            format!("{{#SYMBOL {:?}}}", self.value)
        }
    }

    fn precedence(&self) -> Precedence {
        Precedence::Primary
    }
}

impl Symbol {
    pub fn new(value: EnvironmentInternalSymbolKey) -> Self {
        Self { value }
    }

    pub fn new_from_str(str: impl Into<String>, env: &mut Environment) -> Self {
        let str = str.into();
        if let Some(value) = env.str_to_id(&str) {
            Self::new(value.clone())
        } else {
            let id = env.get_new_id();
            env.insert_str_to_id(str.clone(), id);
            Self::new(id)
        }
    }

    pub fn get_value(&self) -> EnvironmentInternalSymbolKey {
        self.value
    }
}

impl PartialOrd for Symbol {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        other.value.partial_cmp(&self.value)
    }
}

impl Eq for Symbol {}

impl Ord for Symbol {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        other.value.cmp(&self.value)
    }
}
