kay 0.5.1

Experimental high-performance actor system framework for Rust
use crate::messaging::HandlerFnRef;
use crate::messaging::Message;
use crate::actor::Actor;
use crate::type_registry::ShortTypeId;
use crate::actor_system::{World, MAX_MESSAGE_TYPES};
use crate::id::{broadcast_instance_id, RawID, TypedID};
use crate::messaging::{Fate, Packet};
use crate::tuning::Tuning;
use compact::Compact;
use std::rc::Rc;

mod instance_store;
use self::instance_store::InstanceStore;
pub mod inbox;
use self::inbox::{Inbox, DispatchablePacket};

pub struct Class {
    pub instance_store: InstanceStore,
    pub v_table: ActorVTable,
    pub inbox: Inbox
}

pub struct ActorVTable {
    pub message_handlers: [MessageHandler; MAX_MESSAGE_TYPES],
    pub state_v_table: ActorStateVTable,
    pub type_name: &'static str,
}

pub struct ActorStateVTable {
    pub is_still_compact: Box<dyn Fn(*const ()) -> bool>,
    pub total_size_bytes: Box<dyn Fn(*const ()) -> usize>,
    pub compact_behind: Box<dyn Fn(*mut (), *mut ())>,
    pub drop: Box<dyn Fn(*mut ())>,
    pub get_raw_id: Box<dyn Fn(*const ()) -> RawID>,
    pub set_raw_id: Box<dyn Fn(*mut (), RawID)>,
    pub typical_size: usize
}

impl ActorVTable {
    pub fn new_for_actor_type<A: Actor>() -> ActorVTable {
        let actor_name = unsafe { ::std::intrinsics::type_name::<A>() };
        ActorVTable {
            message_handlers: unsafe { make_array!(MAX_MESSAGE_TYPES, |_| MessageHandler::Unassigned) },
            type_name: actor_name,
            state_v_table: ActorStateVTable {
                is_still_compact: Box::new(|act: *const ()| unsafe {(*(act as *const A)).is_still_compact()}),
                total_size_bytes: Box::new(|act: *const ()| unsafe {(*(act as *const A)).total_size_bytes()}),
                compact_behind: Box::new(|source: *mut (), dest: *mut ()| unsafe{Compact::compact_behind(source as *mut A, dest as *mut A)}),
                drop: Box::new(|act: *mut ()| unsafe{::std::ptr::drop_in_place(act as *mut A)}),
                get_raw_id: Box::new(|act: *const ()| unsafe{(*(act as *const A)).id().as_raw()}),
                set_raw_id: Box::new(|act: *mut (), id: RawID| unsafe{(*(act as *mut A)).set_id(id)}),
                typical_size: A::typical_size()
            }
        }
    }
}

pub enum MessageHandler {
    Unassigned,
    OnMessage{handler: Box<HandlerFnRef>, critical: bool},
    OnSpawn{spawner: Box<dyn Fn(*const (), &mut World, &mut InstanceStore, &ActorStateVTable)>, critical: bool}
}

impl Class {
    pub fn new(v_table: ActorVTable, storage: Rc<dyn chunky::ChunkStorage>, tuning: &Tuning) -> Self {
        let ident: chunky::Ident = v_table.type_name.split("<").map(|piece|
            piece.split("::").last().unwrap_or("")
        ).collect::<Vec<_>>().join("<").replace("<", "(").replace(">", ")").into();
        Class {
            instance_store: InstanceStore::new(&ident, v_table.state_v_table.typical_size, Rc::clone(&storage), tuning),
            inbox: Inbox::new(&ident.sub("inbx"), storage, tuning),
            v_table,
        }
    }

    pub fn add_handler<A: Actor, M: Message, F: Fn(&M, &mut A, &mut World) -> Fate + 'static>(
        &mut self,
        message_id: ShortTypeId,
        handler: F,
        critical: bool,
    ) {
        self.v_table.message_handlers[message_id.as_usize()] = MessageHandler::OnMessage {
                handler: Box::new(move |actor_ptr: *mut (), packet_ptr: *const (), world: &mut World| -> Fate {
                    unsafe {
                        let actor = &mut *(actor_ptr as *mut A);
                        let packet = & *(packet_ptr as *const Packet<M>);
                        handler(&packet.message, actor, world)
                    }
                }),
                critical
        };
    }

    pub fn add_spawner<A: Actor, M: Message, F: Fn(&M, &mut World) -> A + 'static>(
        &mut self,
        message_id: ShortTypeId,
        constructor: F,
        critical: bool,
    ) {
        self.v_table.message_handlers[message_id.as_usize()] = MessageHandler::OnSpawn {
            spawner: Box::new(move |packet_ptr: *const (), world: &mut World, store: &mut InstanceStore, intrinsics: &ActorStateVTable| {
                unsafe {
                    let packet = &*(packet_ptr as *const Packet<M>);
                    let mut instance = constructor(&packet.message, world);
                    store.add(&mut instance as *mut A as *mut (), intrinsics, true);
                    ::std::mem::forget(instance);
                }
            }),
            critical
        };
    }

    pub fn handle_messages(&mut self, message_statistics: &mut [usize], world: &mut World) {
        for DispatchablePacket { message_type, packet_ptr} in self.inbox.drain() {
            Self::dispatch_packet(&mut self.instance_store, &self.v_table, message_type, packet_ptr, world);
            message_statistics[message_type.as_usize()] += 1;
        }
    }

    fn dispatch_packet(
        instance_store: &mut InstanceStore,
        v_table: &ActorVTable,
        message_type: ShortTypeId,
        packet_ptr: *const (),
        world: &mut World,
    )
    {
        let handler_kind = &v_table.message_handlers[message_type.as_usize()];

        if let MessageHandler::OnMessage{ref handler, critical} = handler_kind {
            if *critical || !world.panic_happened() {
                let recipient_id = unsafe {(*(packet_ptr as *const Packet<()>)).recipient_id};
                if recipient_id.instance_id == broadcast_instance_id() {
                    instance_store.receive_broadcast(packet_ptr, world, handler, &v_table.state_v_table);
                } else {
                    instance_store.receive_instance(recipient_id, packet_ptr, world, handler,  &v_table.state_v_table);
                }
            }
        } else if let MessageHandler::OnSpawn{spawner, critical} = handler_kind {
            if *critical || !world.panic_happened() {
                spawner(packet_ptr, world, instance_store, &v_table.state_v_table);
            }
        } else {
            if !world.panic_happened() {
                panic!("Handler for message {} not found in {}", message_type.as_usize(), v_table.type_name);
            }
        }
    }
}