use crate::{
    error::Error,
    interpreter::{
        consts::{Consts, HandledConst},
        data::{ArcisStruct, StructId, StructKind, StructOrigin},
        data_store::SimpleAddress,
        function::Function,
        namespace::{
            Namespace,
            NamespacedCollection,
            NamespacedKind,
            Namespaces,
            Progress,
            ProgressKind,
            ScopedProgressKind,
            Typing,
        },
        scope::Scope,
        supported_type::{SupportedType, TypedStruct},
        Interpreter,
    },
};
use quote::ToTokens;

impl<'ast> Interpreter<'ast> {
    pub fn get_const(&mut self, path: &'ast syn::Path) -> Result<SimpleAddress, Error> {
        let a = Consts::get(self, path)?.0;
        let addr = match a {
            HandledConst::Computed(addr) => addr,
            HandledConst::Generic(g) => self
                .generic_context
                .get_const_var_addr(g)
                .ok_or_else(|| Error::new_spanned(path, "Const generic is unset."))?,
        };
        self.memory.deep_clone(addr)
    }
    pub fn has_const(&mut self, ident: &syn::Ident) -> bool {
        Consts::exists(self, ident)
    }
    pub fn insert_method(&mut self, ident: syn::Ident, ty: usize, method: Function<'ast>) {
        Functions::insert_handled_in_type(self, ident, method, ty);
    }

    fn arcis_struct_from_syn(
        &mut self,
        item_struct: &'ast syn::ItemStruct,
    ) -> Result<ArcisStruct, Error> {
        let name = item_struct.ident.to_string();
        let n_fields = item_struct.fields.len();
        let mut named_fields = Vec::with_capacity(n_fields);
        let mut field_types = Vec::with_capacity(n_fields);
        for field in &item_struct.fields {
            if let Some(ident) = &field.ident {
                named_fields.push(ident.clone());
            }
            let ty = self.type_from_syn_type(&field.ty, Some(&field.attrs))?;
            field_types.push(ty);
        }
        let kind = match &item_struct.fields {
            syn::Fields::Named(_) => StructKind::Named,
            syn::Fields::Unnamed(_) => StructKind::Tuple,
            syn::Fields::Unit => StructKind::Unit,
        };
        let generic_id = self.struct_generics.len();
        self.struct_generics.push(&item_struct.generics);
        let named_fields = item_struct
            .fields
            .iter()
            .map(|field| &field.ident)
            .filter_map(|x| x.clone())
            .collect();
        Ok(ArcisStruct::new(
            kind,
            StructOrigin::from_user_struct(item_struct),
            name,
            Some(generic_id),
            named_fields,
            field_types,
        ))
    }
    pub fn get_struct(&mut self, path: &'ast syn::Path) -> Result<(usize, TypedStruct), Error> {
        let Namespace::Type(ty_id) = Namespaces::find_in_index(self, path)?.0 else {
            return Err(Error::new_spanned(
                path,
                format!("{} exists but is not a type.", path.to_token_stream()),
            ));
        };
        let ty = Typing::get_from_index(self, ty_id, Some(path))?
            .destructure()
            .0;
        let SupportedType::Struct(s) = ty else {
            return Err(Error::new_spanned(
                path,
                format!(
                    "{} exists but is not a struct. It's {ty:?}",
                    path.to_token_stream()
                ),
            ));
        };
        Ok((ty_id, s))
    }
    pub fn insert_function(&mut self, ident: syn::Ident, function: Function<'ast>) {
        Functions::insert_handled(self, ident, function);
    }

    pub fn handle_all_unhandled_items(&mut self) {
        Consts::handle_all(self);
        Struct::handle_all(self);
        Typing::handle_all(self);
    }
}

pub struct Struct;

#[derive(Debug, Clone)]
pub struct UnhandledStruct<'ast>(&'ast syn::ItemStruct, Scope<'ast>);

impl<'ast> UnhandledStruct<'ast> {
    pub fn new(code: &'ast syn::ItemStruct, scope: Scope<'ast>) -> UnhandledStruct<'ast> {
        UnhandledStruct(code, scope)
    }
}

impl<'ast> ProgressKind<'ast, UnhandledStruct<'ast>, StructId> for Struct {
    fn get_contents<'a>(
        interpreter: &'a mut Interpreter<'ast>,
    ) -> &'a mut Vec<Progress<UnhandledStruct<'ast>, StructId>> {
        &mut interpreter.structs
    }

    fn transform(
        interpreter: &mut Interpreter<'ast>,
        input: UnhandledStruct<'ast>,
    ) -> Result<StructId, Error> {
        let UnhandledStruct(syn_struct, scope) = input;
        let res = interpreter.with_scope(scope, |interpreter| {
            interpreter
                .arcis_struct_from_syn(syn_struct)
                .map(StructId::new)
        })?;
        Ok(res)
    }
}

pub struct Functions;

impl<'ast> NamespacedKind<'ast, usize> for Functions {
    fn name() -> &'static str {
        "Functions"
    }

    fn get_index_mut<'a>(
        interpreter: &'a mut Interpreter<'ast>,
    ) -> &'a mut NamespacedCollection<'ast, usize> {
        &mut interpreter.function_index
    }

    fn get_index<'a>(interpreter: &'a Interpreter<'ast>) -> &'a NamespacedCollection<'ast, usize> {
        &interpreter.function_index
    }
}

impl<'ast> ProgressKind<'ast, (), Function<'ast>> for Functions {
    fn get_contents<'a>(
        interpreter: &'a mut Interpreter<'ast>,
    ) -> &'a mut Vec<Progress<(), Function<'ast>>> {
        &mut interpreter.functions
    }

    fn transform(
        _interpreter: &mut Interpreter<'ast>,
        _input: (),
    ) -> Result<Function<'ast>, Error> {
        panic!("No transformation on functions.")
    }
}
