use crate::{
    error::Error,
    interpreter::{
        data::{CollectionKind, Data},
        data_store::SimpleAddress,
        generic_context::VarId,
        memory::Memory,
        namespace::{NamespacedCollection, NamespacedKind, Progress, ProgressKind, Typing},
        scope::Scope,
        supported_type::SupportedType,
        Interpreter,
    },
    utils::Ptr,
};
use quote::ToTokens;

#[derive(Debug, Clone)]
pub enum UnhandledConst<'ast> {
    Normal(&'ast syn::Expr, Scope<'ast>),
    /// Struct id, debug info.
    UnitStruct(usize, Ptr<'ast, dyn ToTokens>),
    SelfIdent(&'ast syn::Path, Scope<'ast>),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum HandledConst {
    Computed(SimpleAddress),
    Generic(VarId),
}

pub struct Consts;

impl<'ast> NamespacedKind<'ast, usize> for Consts {
    fn name() -> &'static str {
        "Consts"
    }
    fn get_index_mut<'a>(
        interpreter: &'a mut Interpreter<'ast>,
    ) -> &'a mut NamespacedCollection<'ast, usize> {
        &mut interpreter.const_index
    }

    fn get_index<'a>(interpreter: &'a Interpreter<'ast>) -> &'a NamespacedCollection<'ast, usize> {
        &interpreter.const_index
    }
}
impl<'ast> ProgressKind<'ast, UnhandledConst<'ast>, HandledConst> for Consts {
    fn get_contents<'a>(
        interpreter: &'a mut Interpreter<'ast>,
    ) -> &'a mut Vec<Progress<UnhandledConst<'ast>, HandledConst>> {
        &mut interpreter.consts
    }

    fn transform(
        interpreter: &mut Interpreter<'ast>,
        input: UnhandledConst<'ast>,
    ) -> Result<HandledConst, Error> {
        match input {
            UnhandledConst::Normal(expr, scope) => {
                let res = interpreter.with_scope(scope, |interpreter| {
                    let ptr = interpreter.run_expr(expr)?;
                    interpreter.memory.clone_and_push(ptr)
                })?;
                Ok(HandledConst::Computed(res))
            }
            UnhandledConst::UnitStruct(ty_id, orig) => {
                let s = Typing::get_from_index(interpreter, ty_id, None)?;
                let (SupportedType::Struct(s), _) = s.destructure() else {
                    return Err(Error::new_spanned(
                        orig.0,
                        format!("{} is not a struct.", orig.0.to_token_stream()),
                    ));
                };
                Ok(HandledConst::Computed(interpreter.memory.push(
                    Data::Collection(Vec::new(), CollectionKind::Struct(s)),
                )))
            }
            UnhandledConst::SelfIdent(path, scope) => {
                let addr = interpreter.with_scope(scope, |interpreter| interpreter.get_var(path));
                match addr {
                    Ok(addr) => Ok(HandledConst::Computed(addr)),
                    Err(_) => Ok(HandledConst::Computed(Memory::SIMPLE_ADDRESS_OF_UNIT)),
                }
            }
        }
    }
}
