use std::rc::Rc;

use super::{Environment, Error, Node, NodeEnum, Precedence, constant::Constant};

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Set {
    values: Vec<Rc<NodeEnum>>,
}

impl Node for Set {
    fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<NodeEnum>, Error> {
        let mut values = Vec::with_capacity(self.values.len());

        if self.values.len() == 1 {
            let evaluated = self.values.first().unwrap().evaluate(env)?;

            if let NodeEnum::Range(r) = evaluated.as_ref() {
                for value in r.into_iter() {
                    values.push(Rc::new(Constant::new(value.clone()).into()));
                }

                return Ok(Self::new(values));
            }
        }

        for value in self.values.iter() {
            values.push(value.evaluate(env)?);
        }

        Ok(Self::new(values))
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        format!(
            "[{}]",
            self.values
                .iter()
                .map(|x| x.as_string(env))
                .reduce(|a, b| a + ", " + &b)
                .unwrap_or("".to_owned())
        )
    }

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

impl Set {
    pub fn new(values: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
        Rc::new(Self { values }.into())
    }

    pub fn get_values(&self) -> &Vec<Rc<NodeEnum>> {
        &self.values
    }

    pub fn set_value(&mut self, values: Vec<Rc<NodeEnum>>) {
        self.values = values;
    }
}
