use std::rc::Rc;

use rug::Float;

use super::{Environment, Error, Node, Precedence};

pub type ConstantValue = Float;

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Constant {
    value: ConstantValue,
}

impl From<ConstantValue> for Constant {
    fn from(value: ConstantValue) -> Self {
        Self::new(value)
    }
}

impl Node for Constant {
    fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, Error> {
        Ok(Rc::new(self.clone().into()))
    }

    fn as_string(&self, _env: Option<&Environment>) -> String {
        if self.value.is_zero() {
            return "0".to_string();
        }

        if self.value < 1. && self.value > 0.001 {
            return self.value.to_f64().to_string();
        }

        self.value
            .to_string_radix(10, Some(8))
            .trim_end_matches('0')
            .trim_end_matches('.')
            .to_string()
    }

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

impl Constant {
    pub fn new(value: ConstantValue) -> Self {
        Self { value }
    }

    pub fn new_from_float(value: impl Into<f64>, env: &Environment) -> Self {
        Self {
            value: Float::with_val_64(env.get_float_precision(), value.into()),
        }
    }

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

    pub fn set_value(&mut self, value: ConstantValue) {
        self.value = value;
    }
}
