use alloc::boxed::Box;
use core::any::Any;
use core::slice::Iter;

use crate::{Attribute, Attributes, Function, Path};

use super::environment::ProtoEnvironment;

#[derive(Debug, Clone)]
pub struct ProtoFunction {
    pub(crate) name: &'static str,
    pub(crate) path: Path,
    pub(crate) module: Option<usize>,
    pub(crate) attributes: unsafe fn() -> &'static [Attribute],
    pub(crate) function: unsafe fn() -> FunctionPointer,
}

pub struct FunctionPointer {
    pub(crate) raw: &'static dyn Any,
    pub(crate) _any_return: Option<fn() -> AnyReturn>,
}

pub enum AnyReturn {
    #[cfg(feature = "function-call")]
    Value(Box<dyn Any + Send + Sync>),
    Unsupported,
}

pub(crate) struct Functions {
    iterator: Iter<'static, usize>,
    environment: &'static ProtoEnvironment,
}

impl Functions {
    pub(crate) fn new(environment: &'static ProtoEnvironment, indices: &'static [usize]) -> Self {
        Self {
            iterator: indices.iter(),
            environment,
        }
    }
}

impl Iterator for Functions {
    type Item = Function;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator
            .next()
            .map(|index| self.environment.get_function(*index))
    }
}

impl Function {
    pub(crate) fn attributes(&self) -> Attributes {
        Attributes::new(unsafe { (self.proto_function.attributes)() })
    }
}
