use crate::{
    error::{Error, HasSpan, SpanRange},
    interpreter::{
        data::{ArcisValue, BoolData, CollectionKind, Data, FieldId, StructKind, ValData},
        data_store::{MemoryLayer, SimpleAddress},
        fn_data::{Closure, FnData},
        function::Function,
        interpreter_backtrace::BacktraceEvent,
        interpreter_conversion::ConversionKind,
        interpreter_statics::Functions,
        library::compare::{are_different, are_equal},
        memory::Memory,
        namespace::ScopedProgressKind,
        pointer::Pointer,
        supported_type::{RustType, SupportedType},
        Interpreter,
    },
};
use arcis_compiler::{
    core::circuits::{
        arithmetic::signed_divide::SignedDivide,
        boolean::{boolean_value::BooleanValue, utils::shift_right},
    },
    utils::{number::Number, used_field::UsedField},
    ArcisFloatValue,
};
use num_traits::ToPrimitive;
use quote::{format_ident, ToTokens};
use std::{cell::Cell, collections::HashMap};

type ExprType = Result<Pointer, Error>;

impl<'ast> Interpreter<'ast> {
    fn unit_expr(&self) -> ExprType {
        Ok(Memory::SIMPLE_ADDRESS_OF_UNIT.as_pointer())
    }
    /// Conditionally runs then_block if condition is true, else_block if condition is false.
    pub fn conditionally_run(
        &mut self,
        condition: BooleanValue,
        then_block: impl Runnable<'ast>,
        else_block: Option<impl Runnable<'ast>>,
    ) -> ExprType {
        let (else_address, else_layer) = if let Some(else_block) = else_block {
            self.run_with_memory_layer(|interpreter| else_block.run(interpreter))
        } else {
            (self.unit_expr(), MemoryLayer::new())
        };
        let else_address = else_address?;
        let (then_address, then_layer) =
            self.run_with_memory_layer(|interpreter| then_block.run(interpreter));
        let then_address = then_address?;
        self.memory
            .conditional_merge(condition, then_layer, else_layer)
            .map_err(|x| Error::new(SpanRange::call_site(), x))?;

        let then_ty = self.memory.pointed_type(then_address);
        let else_ty = self.memory.pointed_type(else_address);
        if !then_ty.compatible_data_types(&else_ty) {
            return Err(Error::new(
                SpanRange::call_site(),
                format!(
                    "Type `{}` of block is not compatible with type `{}` of `else` block",
                    then_ty, else_ty
                ),
            ));
        }

        Ok(self.memory.merge_ptr(condition, then_address, else_address))
    }
    /// Runs the expression expr.
    /// Returns Ok(pointer_to_its_value) if it succeeded,
    /// Err(Error) if there is some error to send.
    pub fn run_expr(&mut self, expr: &'ast syn::Expr) -> ExprType {
        // Please keep this match in alphabetical order.
        let res = match expr {
            syn::Expr::Array(expr) => {
                if expr.elems.is_empty() {
                    return Err(Error::new_spanned(
                        expr,
                        "Please use `[expr_of_the_right_type; 0]` to create an empty array.",
                    ));
                }
                let mut ty = None;
                let v: Result<Vec<_>, Error> = expr
                    .elems
                    .iter()
                    .map(|expr| {
                        let ptr = self.run_expr_with_profile(expr)?;
                        let data = self.memory.compute(ptr)?;
                        let expr_ty = data.get_type(&self.memory);
                        match &ty {
                            None => {
                                ty = Some(expr_ty);
                            }
                            Some(t) => {
                                if !expr_ty.compatible_data_types(t) {
                                    return Err(Error::new_spanned(
                                        expr,
                                        format!("Type {expr_ty} incompatible with type {t}."),
                                    ));
                                }
                            }
                        }
                        Ok(self.memory.push(data))
                    })
                    .collect();
                let v = v?;
                let ty = ty.expect("at least one item");
                self.memory
                    .push(Data::Collection(v, CollectionKind::Array(ty)))
                    .as_pointer()
            }
            syn::Expr::Assign(expr) => {
                let left = self.run_expr(&expr.left)?;
                let right = self.run_expr(&expr.right)?;
                let left_type = self.memory.pointed_type(left);
                let converted_right =
                    self.conversion(right, &left_type, ConversionKind::Implicit, expr.eq_token)?;
                let right_data = self.memory.compute(converted_right)?;
                self.memory
                    .assign(left, right_data)
                    .map_err(|e1| Error::new_spanned(expr.eq_token, e1))?;
                self.unit_expr()?
            }
            syn::Expr::Binary(expr) => self.run_expr_binary(expr)?,
            syn::Expr::Block(expr) => {
                let ptr = self.run_block(&expr.block)?;
                self.memory.clone_and_push(ptr)?.as_pointer()
            }
            syn::Expr::Call(expr) => {
                let func = self.run_expr(&expr.func)?;
                let Data::Fn(fn_data) = self.memory.get_underlying_data(func)? else {
                    return Err(Error::new_spanned(expr.func.as_ref(), "Not a function."));
                };
                let args: Result<Vec<_>, Error> = expr
                    .args
                    .iter()
                    .map(|expr| self.run_expr_with_profile(expr))
                    .collect();
                let args = args?;
                self.with_backtrace(expr, |interpreter| fn_data.call(interpreter, &args, expr))?
            }
            syn::Expr::Cast(expr) => {
                let address = self.run_expr(&expr.expr)?;
                let new_type = self.type_from_syn_type(&expr.ty, None)?;
                self.conversion(address, &new_type, ConversionKind::Explicit, expr.as_token)?
            }
            syn::Expr::Closure(expr) => {
                let (closure_id, _) = self
                    .closures
                    .insert_full(Closure::new(expr, self.scope_info.current_scope()));
                let bindings = if expr.capture.is_some() {
                    let mut res = HashMap::new();
                    for (id, addr) in &self.bindings {
                        let addr = self.memory.clone_and_push(addr.as_pointer())?;
                        res.insert(id.clone(), addr);
                    }
                    res
                } else {
                    self.bindings.clone()
                };
                self.memory
                    .push(Data::Fn(FnData::Closure(closure_id, bindings)))
                    .as_pointer()
            }
            syn::Expr::Const(expr) => {
                self.run_with_new_bindings(|interpreter| interpreter.run_block(&expr.block))?
            }
            syn::Expr::Field(expr) => {
                let base_address = self.run_expr(&expr.base)?;
                let base_type = self.memory.pointed_type(base_address);
                let underlying_type = base_type.underlying_type();

                let field_id = match &expr.member {
                    syn::Member::Named(name) => {
                        let SupportedType::Struct(s) = underlying_type else {
                            return Err(Error::new_spanned(
                                expr,
                                format!(
                                    "Accessing field of a non-struct type: {}",
                                    underlying_type
                                ),
                            ));
                        };
                        let Some(field_id) = s.get_field_id(name) else {
                            return Err(Error::new_spanned(
                                name,
                                format!("Unknown Field for struct: {underlying_type}"),
                            ));
                        };
                        *field_id
                    }
                    syn::Member::Unnamed(index) => {
                        if !underlying_type.allows_tuple_access() {
                            return Err(Error::new_spanned(
                                expr,
                                "`.0`, `.1`, ... only works on tuples or tuple structs.",
                            ));
                        }
                        index.index as FieldId
                    }
                };
                self.memory.create_index(
                    base_address,
                    ArcisValue::from(field_id),
                    expr.member.span(),
                )?
            }
            syn::Expr::ForLoop(expr) => {
                let arr = self.run_expr(&expr.expr)?;
                let rust_iter = self.run_method(
                    &format_ident!("into_iter"),
                    &mut [arr],
                    None,
                    expr,
                    Some(expr.into()),
                )?;
                let len = self.run_method(
                    &format_ident!("len"),
                    &mut [rust_iter],
                    None,
                    expr,
                    Some(expr.into()),
                )?;
                let len = self.expr_to_const_usize(len, &expr.expr)?;

                for _ in 0..len {
                    let next_val = self.run_method(
                        &format_ident!("__next"),
                        &mut [rust_iter],
                        None,
                        expr,
                        Some(expr.into()),
                    )?;
                    self.run_but_keep_bindings(|interpreter| {
                        interpreter.apply_pat(&expr.pat, next_val, false)?;
                        interpreter.run_block(&expr.body)
                    })?;
                }
                self.unit_expr()?
            }
            syn::Expr::Group(expr) => self.run_expr(&expr.expr)?,
            syn::Expr::If(expr) => {
                let condition = self.run_expr_with_profile(&expr.cond)?;
                let condition = self.memory.compute(condition)?;
                let Data::Bool(BoolData(condition)) = condition else {
                    return Err(Error::new_spanned(
                        &expr.cond,
                        "If condition is not a boolean.",
                    ));
                };
                let ptr = match condition.as_constant() {
                    Some(value) => {
                        if !value {
                            // value == false
                            if let Some((_, else_branch)) = &expr.else_branch {
                                self.run_expr(else_branch)?
                            } else {
                                self.unit_expr()?
                            }
                        } else {
                            self.run_block(&expr.then_branch)?
                        }
                    }
                    None => self.with_backtrace(expr, |interpreter| {
                        interpreter.conditionally_run(
                            condition,
                            &expr.then_branch,
                            expr.else_branch
                                .as_ref()
                                .map(|(_, else_expr)| else_expr.as_ref()),
                        )
                    })?,
                };
                self.memory.clone_and_push(ptr)?.as_pointer()
            }
            syn::Expr::Index(expr) => {
                let array_ptr = self.run_expr(&expr.expr)?;

                let index_ptr = self.run_expr_with_profile(&expr.index)?;
                let index = self.memory.compute(index_ptr)?;
                match index {
                    Data::Val(index) => {
                        let span = expr.bracket_token.span.into();
                        if !index.1.can_be_used_as_index() {
                            return Err(Error::new(
                                span,
                                "An index must be of `usize` or `isize` type.",
                            ));
                        }
                        let index = index.val();
                        self.memory.create_index(array_ptr, index, span)?
                    }
                    Data::Collection(a, CollectionKind::Struct(_)) => {
                        let [begin, end, ..] = a.as_slice() else {
                            return Err(Error::new_spanned(
                                &expr.index,
                                "Expected range, found something else",
                            ));
                        };
                        let begin = self.expr_to_const_usize(begin.as_pointer(), &expr.index)?;
                        let end = self.expr_to_const_usize(end.as_pointer(), &expr.index)?;
                        self.memory.create_slice(
                            array_ptr,
                            begin,
                            end,
                            expr.bracket_token.span.into(),
                        )?
                    }
                    _ => {
                        return Err(Error::new_spanned(
                            &expr.index,
                            format!("Expected value or range, found {:?}", index),
                        ));
                    }
                }
            }
            syn::Expr::Lit(expr) => match &expr.lit {
                syn::Lit::Byte(b) => self
                    .memory
                    .push(Data::Val(ValData(b.value().into(), RustType::u8())))
                    .as_pointer(),
                syn::Lit::ByteStr(bs) => {
                    let arr = bs
                        .value()
                        .into_iter()
                        .map(|byte| {
                            self.memory
                                .push(Data::Val(ValData(byte.into(), RustType::u8())))
                        })
                        .collect();
                    let ptr = self.memory.push(Data::Collection(
                        arr,
                        CollectionKind::Array(SupportedType::u8()),
                    ));
                    self.memory.push(ptr.as_data()).as_pointer()
                }
                syn::Lit::Int(i) => {
                    let val = i.base10_parse::<Number>().map_err(|e| {
                        Error::new_spanned(i, format!("Failed to parse literal: {e}"))
                    })?;
                    let val = ArcisValue::from(val);
                    assert!(val.is_constant());
                    let rust_type = RustType::integer_from_suffix(i.suffix());
                    self.memory
                        .push(Data::Val(ValData(val, rust_type)))
                        .as_pointer()
                }
                syn::Lit::Bool(b) => self
                    .memory
                    .push(Data::Bool(BoolData(BooleanValue::from(b.value))))
                    .as_pointer(),
                syn::Lit::Float(f) => {
                    let val = f.base10_parse::<f64>().map_err(|e| {
                        Error::new_spanned(f, format!("Failed to parse literal: {e}"))
                    })?;
                    let val = match ArcisFloatValue::try_from(val) {
                        Ok(a) => a,
                        Err(s) => {
                            return Err(Error::new_spanned(
                                f,
                                format!("Failed to convert literal: {s}"),
                            ));
                        }
                    };
                    self.memory
                        .push(Data::Val(ValData(val.0, RustType::Float)))
                        .as_pointer()
                }
                _ => return Err(Error::new_spanned(expr, "Invalid literal")),
            },
            syn::Expr::Macro(expr) => self.run_macro(&expr.mac)?,
            syn::Expr::MethodCall(expr) => {
                let receiver = self.run_expr(&expr.receiver)?;
                let mut args = vec![receiver];
                for expr in expr.args.iter() {
                    args.push(self.run_expr_with_profile(expr)?);
                }
                let turbofish = expr.turbofish.as_ref();
                self.run_method(&expr.method, &mut args, turbofish, expr, Some(expr.into()))?
            }
            syn::Expr::Paren(expr) => self.run_expr_with_profile(&expr.expr)?,
            syn::Expr::Path(expr_path) => self.get_var(&expr_path.path)?.as_pointer(),
            syn::Expr::Range(range) => {
                let Some(start) = &range.start else {
                    return Err(Error::new_spanned(
                        range,
                        "Unable to compute the start of the range. Only ranges whose start is constant are supported.",
                    ));
                };
                let Some(end) = &range.end else {
                    return Err(Error::new_spanned(
                        range,
                        "Unable to compute the end of the range. Only ranges whose end is constant are supported.",
                    ));
                };
                let start = self.run_expr(start)?;
                let start = self.memory.clone_and_push(start)?;
                let end = self.run_expr(end)?;
                let end = self.memory.clone_and_push(end)?;
                let tuple = self
                    .memory
                    .push(Data::Collection(vec![start, end], CollectionKind::Tuple))
                    .as_pointer();
                let method_name = match range.limits {
                    syn::RangeLimits::HalfOpen(_) => format_ident!("__to_exclusive_range"),
                    syn::RangeLimits::Closed(_) => format_ident!("__to_inclusive_range"),
                };
                let res = self.run_method(&method_name, &mut [tuple], None, range, None);
                res?
            }
            syn::Expr::Reference(expr) => {
                let address = self.run_expr(&expr.expr)?;
                self.memory.push(address.as_data()).as_pointer()
            }
            syn::Expr::Repeat(expr) => {
                let data_ptr = self.run_expr_with_profile(&expr.expr)?;
                let len = self.expr_to_const_usize(expr.len.as_ref(), &expr.len)?;
                let data_addr = self.memory.clone_and_push(data_ptr)?;
                let v: Result<Vec<_>, _> = (0usize..len)
                    .map(|i| {
                        if i == 0 {
                            Ok(data_addr)
                        } else {
                            self.memory.clone_and_push(data_addr.as_pointer())
                        }
                    })
                    .collect();
                let v = v?;
                let ty = self.memory.pointed_type(data_ptr);
                self.memory
                    .push(Data::Collection(v, CollectionKind::Array(ty)))
                    .as_pointer()
            }
            syn::Expr::Struct(expr) => {
                let (ty_id, struct_id) = self.get_struct(&expr.path)?;
                if struct_id.get_kind() != StructKind::Named {
                    return Err(Error::new_spanned(
                        expr,
                        "This struct cannot be built that way.",
                    ));
                }
                let field_data_vec: Result<Vec<_>, _> = expr
                    .fields
                    .iter()
                    .map(|x| {
                        let expr_address = self.run_expr_with_profile(&x.expr)?;
                        let syn::Member::Named(name) = &x.member else {
                            return Err(Error::new_spanned(
                                &x.member,
                                "Struct field of a non-named struct",
                            ));
                        };
                        let Some(field_id) = struct_id.get_field_id(name) else {
                            return Err(Error::new_spanned(name, "unknown field"));
                        };

                        Ok((*field_id, expr_address, name))
                    })
                    .collect();
                let mut field_data_vec = field_data_vec?;
                if let Some(rest) = &expr.rest {
                    let rest_ptr = self.run_expr_with_profile(rest)?;
                    let Data::Collection(mut v, CollectionKind::Struct(s)) =
                        self.memory.compute(rest_ptr)?
                    else {
                        return Err(Error::new_spanned(rest, "This must be a struct."));
                    };
                    if struct_id != s {
                        return Err(Error::new_spanned(rest, "This is the wrong struct."));
                    }
                    for (field_id, address, name) in field_data_vec {
                        let expr_address_after_conversion = self.conversion(
                            address,
                            &struct_id.types()[field_id],
                            ConversionKind::Implicit,
                            name,
                        )?;
                        v[field_id] = self.memory.clone_and_push(expr_address_after_conversion)?;
                    }
                    self.memory
                        .push(Data::Collection(v, CollectionKind::Struct(struct_id)))
                        .as_pointer()
                } else {
                    field_data_vec.sort_by_key(|(i, _, _)| *i);
                    if field_data_vec.len() != struct_id.get_n_fields() {
                        return Err(Error::new_spanned(expr, "missing fields in struct"));
                    }

                    let generic_id = struct_id.get_generic_id();

                    let generics = generic_id.map(|id| self.struct_generics[id]);
                    let turbofish = match expr
                        .path
                        .segments
                        .last()
                        .expect("Paths are not empty.")
                        .arguments
                    {
                        syn::PathArguments::AngleBracketed(ref args) => Some(args),
                        _ => None,
                    };
                    Function::StructRef(ty_id, generics).call(
                        self,
                        field_data_vec
                            .into_iter()
                            .map(|(_, a, _)| a)
                            .collect::<Vec<_>>()
                            .as_slice(),
                        turbofish,
                        Default::default(),
                        expr,
                    )?
                }
            }
            syn::Expr::Tuple(expr) => {
                if expr.elems.is_empty() {
                    return self.unit_expr();
                }
                let v: Result<Vec<_>, Error> = expr
                    .elems
                    .iter()
                    .map(|expr| {
                        let ptr = self.run_expr_with_profile(expr)?;
                        let data = self.memory.compute(ptr)?;
                        Ok(self.memory.push(data))
                    })
                    .collect();
                let v = v?;
                self.memory
                    .push(Data::Collection(v, CollectionKind::Tuple))
                    .as_pointer()
            }
            syn::Expr::Unary(expr) => self.run_expr_unary(expr)?,
            _ => {
                return Err(Error::new_spanned(expr, "Unsupported expression type"));
            }
        };
        Ok(res)
    }
    fn run_expr_with_profile(&mut self, expr: &'ast syn::Expr) -> ExprType {
        self.with_profile(expr, |interpreter| interpreter.run_expr(expr))
    }
    /// Runs the unary expression expr.
    /// Returns Ok(pointer_to_its_value) if it succeeded,
    /// Err(Error) if there is some error to send.
    fn run_expr_unary(&mut self, expr: &'ast syn::ExprUnary) -> ExprType {
        let inner_expr = self.run_expr(&expr.expr)?;
        let res = match expr.op {
            syn::UnOp::Deref(_) => {
                let data = self.memory.compute(inner_expr)?;
                let Data::Pointer(addr) = data else {
                    return Err(Error::new_spanned(
                        expr,
                        "Dereferencing a non-pointer expression",
                    ));
                };
                addr
            }
            syn::UnOp::Not(_) => {
                let val = self.memory.compute(inner_expr)?;
                let Data::Bool(BoolData(val)) = val else {
                    return Err(Error::new_spanned(
                        expr,
                        "Negating a non-boolean expression",
                    ));
                };
                self.memory.push(Data::Bool(BoolData(!val))).as_pointer()
            }
            syn::UnOp::Neg(_) => {
                let val = self.memory.compute(inner_expr)?;
                if let Data::Val(ValData(val, ty)) = val {
                    if ty.cannot_be_neg() {
                        return Err(Error::new_spanned(
                            expr,
                            "Taking the opposite of an unsigned expression",
                        ));
                    }
                    self.memory.push(Data::Val(ValData(-val, ty))).as_pointer()
                } else {
                    return Err(Error::new_spanned(
                        expr,
                        "Taking the opposite of a non-integer expression",
                    ));
                }
            }
            _ => {
                return Err(Error::new_spanned(
                    expr,
                    "unsupported unary expression type",
                ));
            }
        };
        Ok(res)
    }
    /// Runs the binary expression expr.
    /// Returns Ok(pointer_to_its_value) if it succeeded,
    /// Err(Error) if there is some error to send.
    fn run_expr_binary(&mut self, expr: &'ast syn::ExprBinary) -> ExprType {
        let is_assign = matches!(
            expr.op,
            syn::BinOp::AddAssign(_)
                | syn::BinOp::SubAssign(_)
                | syn::BinOp::MulAssign(_)
                | syn::BinOp::DivAssign(_)
                | syn::BinOp::RemAssign(_)
                | syn::BinOp::BitXorAssign(_)
                | syn::BinOp::BitAndAssign(_)
                | syn::BinOp::BitOrAssign(_)
                | syn::BinOp::ShlAssign(_)
                | syn::BinOp::ShrAssign(_)
        );
        let is_short_circuit = matches!(expr.op, syn::BinOp::And(_) | syn::BinOp::Or(_));
        let left_address = self.run_expr(&expr.left)?;
        let left = self.memory.compute(left_address)?;
        if is_short_circuit {
            let Data::Bool(BoolData(left_bool)) = left else {
                return Err(Error::new_spanned(&expr.left, "Must be a boolean."));
            };
            let cond = match expr.op {
                syn::BinOp::And(_) => left_bool,
                syn::BinOp::Or(_) => !left_bool,
                _ => unreachable!("Short circuit boolean op is not a short circuit boolean op."),
            };
            return self.conditionally_run(cond, expr.right.as_ref(), Some(left_address));
        }
        let right_address = self.run_expr(&expr.right)?;
        let right = self.memory.compute(right_address)?;
        if matches!(expr.op, syn::BinOp::Eq(_)) {
            return are_equal(self, &[left_address, right_address], expr);
        }
        if matches!(expr.op, syn::BinOp::Ne(_)) {
            return are_different(self, &[left_address, right_address], expr);
        }
        if is_assign {
            match (left, right) {
                (
                    Data::Val(ValData(l, RustType::Float)),
                    Data::Val(ValData(r, RustType::Float)),
                ) => {
                    let l = ArcisFloatValue(l);
                    let r = ArcisFloatValue(r);
                    let result = match expr.op {
                        syn::BinOp::AddAssign(_) => l + r,
                        syn::BinOp::SubAssign(_) => l - r,
                        syn::BinOp::MulAssign(_) => l * r,
                        syn::BinOp::DivAssign(_) => l / r,
                        _ => {
                            return Err(Error::new_spanned(
                                expr.op,
                                "Unsupported binary assign operator on floats.",
                            ));
                        }
                    };
                    self.memory
                        .assign(left_address, Data::Val(ValData(result.0, RustType::Float)))
                        .expect("Error assigning float to float.");
                    self.unit_expr()
                }
                (Data::Val(ValData(l, l_ty)), Data::Val(ValData(r, r_ty))) => {
                    let res_ty = l_ty.intersect(r_ty).map_err(|_| {
                        Error::new_spanned(
                            expr,
                            format!(
                                "Incompatible numerical types in binary assignment operation `{}`: `{l_ty}` and `{r_ty}`.",
                                expr.to_token_stream(),
                            ),
                        )
                    })?;
                    let result = match expr.op {
                        syn::BinOp::AddAssign(_) => l + r,
                        syn::BinOp::SubAssign(_) => l - r,
                        syn::BinOp::MulAssign(_) => l * r,
                        syn::BinOp::DivAssign(_) => SignedDivide::checked_run(l, r)
                            .map_err(|e| Error::new_spanned(expr, e))?[0],
                        syn::BinOp::RemAssign(_) => SignedDivide::checked_run(l, r)
                            .map_err(|e| Error::new_spanned(expr, e))?[1],
                        syn::BinOp::ShrAssign(_) => {
                            let Some(c) = r.as_constant() else {
                                return Err(Error::new_spanned(
                                    &expr.right,
                                    "We only support >>= when the right side is constant",
                                ));
                            };
                            let Some(c) = c.to_unsigned_number().to_usize() else {
                                return Err(Error::new_spanned(
                                    &expr.right,
                                    format!(
                                        "We only support >>= when the right side is between 0 and {}",
                                        usize::MAX
                                    ),
                                ));
                            };
                            shift_right(l, c, true)
                        }
                        _ => {
                            return Err(Error::new_spanned(
                                expr.op,
                                "Unsupported binary assign operator on integers.",
                            ));
                        }
                    };
                    self.memory
                        .assign(left_address, Data::Val(ValData(result, res_ty)))
                        .expect("Error assigning integer to integer.");
                    self.unit_expr()
                }
                (Data::Bool(BoolData(l)), Data::Bool(BoolData(r))) => {
                    let result = match expr.op {
                        syn::BinOp::BitXorAssign(_) => l ^ r,
                        syn::BinOp::BitAndAssign(_) => l & r,
                        syn::BinOp::BitOrAssign(_) => l | r,
                        _ => {
                            return Err(Error::new_spanned(
                                expr.op,
                                "Unsupported binary assign operator on booleans.",
                            ));
                        }
                    };
                    self.memory
                        .assign(left_address, Data::Bool(BoolData(result)))
                        .expect("Error assigning boolean to boolean.");
                    self.unit_expr()
                }
                (l, r) => {
                    let l_ty = l.get_type(&self.memory);
                    let r_ty = r.get_type(&self.memory);
                    Err(Error::new_spanned(
                        &expr.right,
                        format!(
                            "Unsupported types for ops: {} and {} in {}",
                            l_ty,
                            r_ty,
                            expr.to_token_stream()
                        ),
                    ))
                }
            }
        } else {
            // Allow &u64 + u64 and similar.
            let left = if let Data::Pointer(ptr) = left {
                self.memory.compute(ptr)?
            } else {
                left
            };
            let right = if let Data::Pointer(ptr) = right {
                self.memory.compute(ptr)?
            } else {
                right
            };

            let returns_bool = matches!(
                expr.op,
                syn::BinOp::Eq(_)
                    | syn::BinOp::Lt(_)
                    | syn::BinOp::Le(_)
                    | syn::BinOp::Ne(_)
                    | syn::BinOp::Gt(_)
                    | syn::BinOp::Ge(_)
            );

            match (left, right) {
                (
                    Data::Val(ValData(l, RustType::Float)),
                    Data::Val(ValData(r, RustType::Float)),
                ) => {
                    if returns_bool {
                        let result = match expr.op {
                            syn::BinOp::Lt(_) => l.signed_lt(r),
                            syn::BinOp::Le(_) => l.signed_le(r),
                            syn::BinOp::Ge(_) => l.signed_ge(r),
                            syn::BinOp::Gt(_) => l.signed_gt(r),
                            _ => {
                                return Err(Error::new_spanned(
                                    expr.op,
                                    "Unsupported boolean binary operator on floats.",
                                ));
                            }
                        };
                        Ok(self.memory.push(Data::Bool(BoolData(result))).as_pointer())
                    } else {
                        let l = ArcisFloatValue(l);
                        let r = ArcisFloatValue(r);
                        let result = match expr.op {
                            syn::BinOp::Add(_) => l + r,
                            syn::BinOp::Sub(_) => l - r,
                            syn::BinOp::Mul(_) => l * r,
                            syn::BinOp::Div(_) => l / r,
                            _ => {
                                return Err(Error::new_spanned(
                                    expr.op,
                                    "Unsupported binary operator on floats.",
                                ));
                            }
                        };
                        Ok(self
                            .memory
                            .push(Data::Val(ValData(result.0, RustType::Float)))
                            .as_pointer())
                    }
                }
                (Data::Val(ValData(l, l_ty)), Data::Val(ValData(r, r_ty))) => {
                    let inter_ty = l_ty.intersect(r_ty).map_err(|_| {
                        Error::new_spanned(expr, format!(
                            "Incompatible numerical types in binary operation `{}`: `{l_ty}` and `{r_ty}`.",
                            expr.to_token_stream(),
                        ),)
                    })?;
                    let result = match expr.op {
                        syn::BinOp::Add(_) => l + r,
                        syn::BinOp::Sub(_) => l - r,
                        syn::BinOp::Mul(_) => l * r,
                        syn::BinOp::Div(_) => SignedDivide::checked_run(l, r)
                            .map_err(|e| Error::new_spanned(expr, e))?[0],
                        syn::BinOp::Rem(_) => SignedDivide::checked_run(l, r)
                            .map_err(|e| Error::new_spanned(expr, e))?[1],
                        syn::BinOp::Lt(_) => ArcisValue::from(l.signed_lt(r)),
                        syn::BinOp::Le(_) => ArcisValue::from(l.signed_le(r)),
                        syn::BinOp::Ge(_) => ArcisValue::from(l.signed_ge(r)),
                        syn::BinOp::Gt(_) => ArcisValue::from(l.signed_gt(r)),
                        syn::BinOp::Shr(_) => {
                            let Some(c) = r.as_constant() else {
                                return Err(Error::new_spanned(
                                    &expr.right,
                                    "We only support >> when the right side is constant",
                                ));
                            };
                            let Some(c) = c.to_unsigned_number().to_usize() else {
                                return Err(Error::new_spanned(
                                    &expr.right,
                                    format!(
                                        "We only support >> when the right side is between 0 and {}",
                                        usize::MAX
                                    ),
                                ));
                            };
                            shift_right(l, c, true)
                        }

                        _ => {
                            return Err(Error::new_spanned(
                                expr,
                                "Unsupported binary operation between integers.",
                            ));
                        }
                    };
                    if returns_bool {
                        Ok(self
                            .memory
                            .push(Data::Bool(BoolData(result.into())))
                            .as_pointer())
                    } else {
                        Ok(self
                            .memory
                            .push(Data::Val(ValData(result, inter_ty)))
                            .as_pointer())
                    }
                }
                (Data::Bool(BoolData(l)), Data::Bool(BoolData(r))) => {
                    let result = match expr.op {
                        syn::BinOp::BitXor(_) => l ^ r,
                        syn::BinOp::BitAnd(_) => l & r,
                        syn::BinOp::BitOr(_) => l | r,
                        _ => {
                            return Err(Error::new_spanned(
                                expr.op,
                                "Unsupported binary operator on booleans.",
                            ));
                        }
                    };
                    Ok(self.memory.push(Data::Bool(BoolData(result))).as_pointer())
                }
                (l, r) => {
                    let l_ty = l.get_type(&self.memory);
                    let r_ty = r.get_type(&self.memory);
                    Err(Error::new_spanned(
                        &expr.right,
                        format!(
                            "Unsupported types for op {}: {} and {} in {}",
                            expr.op.to_token_stream(),
                            l_ty,
                            r_ty,
                            expr.to_token_stream()
                        ),
                    ))
                }
            }
        }
    }

    pub fn run_stmt(&mut self, stmt: &'ast syn::Stmt) -> ExprType {
        match stmt {
            syn::Stmt::Local(l) => {
                let Some(init) = &l.init else {
                    return Err(Error::new_spanned(
                        l,
                        "let without initializer is not supported",
                    ));
                };
                if let Some((div, _)) = &init.diverge {
                    return Err(Error::new_spanned(div, "let with else is not supported"));
                }
                let address = self.run_expr(&init.expr)?;
                self.apply_pat(&l.pat, address, false)?;
                self.unit_expr()
            }
            syn::Stmt::Item(_) => {
                // items have been read already
                self.unit_expr()
            }
            syn::Stmt::Expr(expr, has_semicolon) => {
                let ptr = self.run_expr(expr)?;
                if has_semicolon.is_some() {
                    self.unit_expr()
                } else {
                    Ok(ptr)
                }
            }
            syn::Stmt::Macro(s) => self.run_macro(&s.mac),
        }
    }
    pub fn try_run_stmt(&mut self, stmt: &'ast syn::Stmt) -> ExprType {
        self.run_with_memory_layer(|interpreter| {
            interpreter.run_but_keep_bindings(|interpreter| interpreter.run_stmt(stmt))
        })
        .0
    }
    pub fn run_block(&mut self, block: &'ast syn::Block) -> ExprType {
        self.with_scope(block, |interpreter| {
            interpreter.run_but_keep_bindings(|interpreter| {
                let mut result = interpreter.unit_expr()?;
                for stmt in &block.stmts {
                    let res =
                        interpreter.with_profile(stmt, |interpreter| interpreter.run_stmt(stmt));
                    result = interpreter.improve_error(res, Some(stmt))?;
                }
                Ok(result)
            })
        })
    }
    pub fn run_method(
        &mut self,
        ident: &syn::Ident,
        args: &mut [Pointer],
        turbofish: Option<&'ast syn::AngleBracketedGenericArguments>,
        expr: &dyn ToTokens,
        backtrace_event: Option<BacktraceEvent<'ast>>,
    ) -> ExprType {
        let receiver = args[0];
        let receiver_ty = self.memory.pointed_type(receiver);
        let (func, ctx) = Functions::get_with_ident_and_type(self, ident, &receiver_ty)?;
        args[0] = if func.has_ref_receiver() && !receiver_ty.is_reference() {
            self.memory.push(receiver.as_data()).as_pointer()
        } else {
            receiver
        };
        match backtrace_event {
            None => func.call(self, args, turbofish, ctx, expr),
            Some(backtrace_event) => self.with_backtrace(backtrace_event, |interpreter| {
                func.call(interpreter, args, turbofish, ctx, &backtrace_event)
            }),
        }
    }
}

/// A trait for things that are Runnable.
/// This could be impl for Statements, Binary Expr, Unary Expr if necessary.
pub trait Runnable<'ast>: Copy {
    fn run(self, interpreter: &mut Interpreter<'ast>) -> ExprType;
}
impl<'ast> Runnable<'ast> for &'ast syn::Expr {
    fn run(self, interpreter: &mut Interpreter<'ast>) -> ExprType {
        interpreter.run_expr(self)
    }
}
impl<'ast> Runnable<'ast> for &'ast syn::Block {
    fn run(self, interpreter: &mut Interpreter<'ast>) -> ExprType {
        interpreter.run_block(self)
    }
}
/// Running a pointer just returns itself.
impl<'ast> Runnable<'ast> for Pointer {
    fn run(self, _interpreter: &mut Interpreter<'ast>) -> ExprType {
        Ok(self)
    }
}

impl<'ast> Runnable<'ast> for (SimpleAddress, &[Pointer], &dyn ToTokens) {
    fn run(self, interpreter: &mut Interpreter<'ast>) -> ExprType {
        let (func, args, expr) = self;
        let Data::Fn(func) = interpreter.memory[func].clone() else {
            return Err(Error::new_spanned(expr, "Not a function."));
        };
        func.call(interpreter, args, expr)
    }
}

impl<'ast, T: Runnable<'ast>> Runnable<'ast> for (T, &Cell<Option<Pointer>>) {
    fn run(self, interpreter: &mut Interpreter<'ast>) -> ExprType {
        let (s, cell) = self;
        let res = s.run(interpreter)?;
        cell.replace(Some(res));
        Ok(res)
    }
}
