deft 0.15.0

Cross platform ui framework
use crate::app::App;
use crate::ext::ext_worker::{WorkerContext, JS_WORKER_CONTEXTS};
use crate::id_generator::IdGenerator;
use crate::id_hash_map::IdHashMap;
use crate::js::js_engine::JsEngine;
use crate::js::js_event_loop::{js_init_event_loop, JsEvent, JsEventLoopClosedError};
use crate::js::ToJsValue;
use log::error;
use std::collections::HashMap;
use std::sync::mpsc::{SendError, Sender};
use std::sync::{Arc, LazyLock, Mutex};
use std::thread;

struct ServiceHolder {
    id_generator: IdGenerator,
    services: HashMap<u32, Service>,
}

static SERVICES: LazyLock<Arc<Mutex<ServiceHolder>>> = LazyLock::new(|| {
    let holder = ServiceHolder {
        id_generator: IdGenerator::new(),
        services: HashMap::new(),
    };
    Arc::new(Mutex::new(holder))
});

#[derive(Clone)]
pub struct Service {
    id: u32,
    sender: Arc<Mutex<Option<Sender<JsEvent>>>>,
    msg_handlers: Arc<Mutex<IdHashMap<Box<dyn FnMut(crate::ext::ext_worker::MessageData) + Send>>>>,
}

impl Service {
    pub fn get(id: u32) -> Option<Self> {
        let services = SERVICES.lock().unwrap();
        services.services.get(&id).cloned()
    }

    pub fn new() -> Self {
        let id = {
            let mut service_holder = SERVICES.lock().unwrap();
            service_holder.id_generator.generate_id()
        };

        let msg_handlers = Arc::new(Mutex::new(IdHashMap::new()));
        let service = Self {
            id,
            sender: Arc::new(Mutex::new(None)),
            msg_handlers: msg_handlers.clone(),
        };
        {
            let mut services = SERVICES.lock().unwrap();
            services.services.insert(id, service.clone());
        }

        service
    }

    pub fn start(&mut self, app: App, module_name: String) {
        let (sender, receiver) = std::sync::mpsc::channel();
        {
            let mut sender_holder = self.sender.lock().unwrap();
            sender_holder.replace(sender.clone());
        }
        let msg_handlers = self.msg_handlers.clone();
        let _ = thread::Builder::new()
            .name("js-worker".to_string())
            .spawn(move || {
                JsEngine::init(app.clone());
                let mut js_engine = JsEngine::get();

                js_init_event_loop(move |js_event| {
                    sender.send(js_event).map_err(|_| JsEventLoopClosedError {})
                });

                let worker_context = WorkerContext::create(Box::new(move |msg| {
                    let mut msg_handlers = msg_handlers.lock().unwrap();
                    msg_handlers.for_each_mut(|_, handler| {
                        handler(msg.clone());
                    });
                }));
                JS_WORKER_CONTEXTS.with_borrow_mut(|ctxs| {
                    ctxs.replace(worker_context);
                });

                let _ = js_engine
                    .js_context
                    .add_callback("WorkerContext_get", move || {
                        let ctx = WorkerContext::get();
                        ctx.unwrap().to_js_value().unwrap()
                    });
                js_engine.add_global_functions(WorkerContext::create_js_apis());

                js_engine.init_api();
                {
                    let mut app = app.app_impl.lock().unwrap();
                    app.init_js_engine(&mut js_engine);
                }

                let r = js_engine.execute_module(module_name.as_str());
                if let Err(err) = r {
                    error!("Error executing module: {}, error:{}", module_name, err);
                    return;
                }
                js_engine.execute_pending_jobs();
                loop {
                    let Ok(event) = receiver.recv() else {
                        break;
                    };
                    match event {
                        JsEvent::MacroTask(task) => {
                            task();
                        }
                    }
                    js_engine.execute_pending_jobs();
                }
            });
    }

    pub fn get_id(&self) -> u32 {
        self.id
    }

    pub fn send_event(&self, event: JsEvent) -> Result<(), SendError<JsEvent>> {
        let sender = self.sender.lock().unwrap();
        if let Some(sender) = sender.as_ref() {
            sender.send(event)
        } else {
            Err(SendError(event))
        }
    }

    pub fn add_msg_handler(
        &self,
        handler: Box<dyn FnMut(crate::ext::ext_worker::MessageData) + Send>,
    ) {
        let mut handlers = self.msg_handlers.lock().unwrap();
        handlers.insert(handler);
    }

    pub fn remove_msg_handler(&self, id: u32) {
        let mut handlers = self.msg_handlers.lock().unwrap();
        handlers.remove(id);
    }
}