use super::*;

pub(super) fn resolve_const_calls_in_module(
    module: &mut ty::Module,
    evaluator: &mut ConstEvaluator,
) -> Result<(), String> {
    for declaration in module.iter_mut() {
        match declaration {
            ty::Declaration::Type(newtype) => {
                resolve_const_calls_in_type(&mut newtype.inner_type, evaluator)?;
            }
            ty::Declaration::Impl(impl_block) => {
                resolve_const_calls_in_type(&mut impl_block.callable.return_type, evaluator)?;
            }
            _ => {}
        }
    }
    Ok(())
}

pub(super) fn resolve_const_calls_in_type(
    source: &mut ty_types::Type,
    mut evaluator: &mut ConstEvaluator,
) -> Result<(), String> {
    match source {
        ty_types::Type::Array(element, size) | ty_types::Type::Vector(element, size) => {
            use crate::const_expression::{EvalContext, eval};
            let mut context = EvalContext {
                bindings: None,
                evaluator: Some(evaluator),
            };
            *size = eval(size, &mut context);
            resolve_const_calls_in_type(element, context.evaluator.unwrap())?;
        }
        ty_types::Type::Pointer(_, _, inner) | ty_types::Type::Reference(_, _, inner) => {
            resolve_const_calls_in_type(inner, evaluator)?;
        }
        ty_types::Type::Tuple(fields) => {
            for field in fields {
                resolve_const_calls_in_type(field, evaluator)?;
            }
        }
        ty_types::Type::Named(_, arguments) => {
            for argument in arguments {
                if let ty_types::GenericArgument::Const(constant) = argument {
                    use crate::const_expression::{EvalContext, eval};
                    let mut context = EvalContext {
                        bindings: None,
                        evaluator: Some(evaluator),
                    };
                    *constant = eval(constant, &mut context);
                    evaluator = context.evaluator.unwrap();
                }
            }
        }
        _ => {}
    }
    Ok(())
}
