use crate::{
    error::Error,
    interpreter::{
        data::{Data, ValData},
        interpreter_run::Runnable,
        scope::Scope,
        Interpreter,
    },
};
use arcis_compiler::utils::used_field::UsedField;
use num_traits::ToPrimitive;
use quote::ToTokens;

impl<'ast> Interpreter<'ast> {
    pub fn errors(&self) -> Vec<Error> {
        self.errors.to_vec()
    }
    pub fn expr_to_const_usize(
        &mut self,
        expr: impl Runnable<'ast>,
        span: impl ToTokens,
    ) -> Result<usize, Error> {
        let ptr = expr.run(self)?;
        let val = self.memory.compute(ptr)?;

        let val = match val {
            Data::Val(ValData(a, r)) => {
                if !r.can_be_used_as_size() {
                    return Err(Error::new_spanned(span, "Not `usize`."));
                }
                a
            }
            _ => return Err(Error::new_spanned(span, "Should be a number.")),
        };
        let val = val.as_constant().ok_or_else(|| {
            let bounds = val.bounds();
            let bounds = bounds.to_signed_number_pair();
            Error::new_spanned(
                &span,
                format!(
                    "Should be a constant, but varies between {} and {}.",
                    bounds.0, bounds.1
                ),
            )
        })?;
        val.to_signed_number()
            .to_usize()
            .ok_or_else(|| Error::new_spanned(span, "Negative or too big number."))
    }

    pub fn with_scope<T>(
        &mut self,
        scope: impl Into<Scope<'ast>>,
        func: impl FnOnce(&mut Self) -> T,
    ) -> T {
        let scope = scope.into();
        self.scope_info.push(scope);
        let res = func(self);
        self.scope_info.pop(scope);
        res
    }
}
