use crate::filtration::ScenarioFiltration;
use fasteval::{Compiler, Evaler, Instruction, Slab};
use ordered_float::OrderedFloat;

pub struct Function {
    instruction: Instruction,
    slab: Slab,
    expr_str: String,
}

impl Clone for Function {
    fn clone(&self) -> Self {
        Self::new(&self.expr_str).expect("Failed to re-compile on clone")
    }
}

impl Function {
    pub fn new(expr_str: &str) -> Result<Self, String> {
        let parser = fasteval::Parser::new();
        let mut slab = Slab::new();
        let expr = parser
            .parse(expr_str, &mut slab.ps)
            .map_err(|e| format!("Parse Error: {:?}", e))?;
        let instruction = expr.from(&slab.ps).compile(&slab.ps, &mut slab.cs);
        Ok(Self {
            instruction,
            slab,
            expr_str: expr_str.to_string(),
        })
    }

    pub fn eval(
        &self,
        t: OrderedFloat<f64>,
        filtration: &mut ScenarioFiltration,
    ) -> Result<f64, fasteval::Error> {
        if t != filtration.cache.time {
            filtration.refresh_cache(t);
        }
        self.instruction
            .eval(&self.slab, &mut filtration.cache.values)
    }
}
