import 'dart:isolate'; import 'dart:async'; import 'package:oto_cli/framework/data/isolate_data.dart'; import 'package:oto_cli/framework/utils/system_util.dart' as system; class _IsolateInnerEvent { static const String ready = '_IsolateInnerEvent.Ready'; static const String exit = '_IsolateInnerEvent.Exit'; } class _IsolateConstructData { late int id; late SendPort mainIsoSender; late Object? initData; late String appName; late String appID; _IsolateConstructData( this.id, this.mainIsoSender, this.appName, this.appID, this.initData); } class IsolateHandler { final int _id; final Map> _listenerMap = {}; final Map> _listenerEventMap = {}; IsolateHandler(this._id); void send(String type, {Object? message}) { IsolateManager.send(_id, IsoMessege(type, data: message)); } void addListener(String type, void Function(Object?) func) { removeListener(type, func); if (!_listenerMap.containsKey(type)) { _listenerMap[type] = []; } _listenerMap[type]?.add(func); } void removeListener(String type, void Function(Object? data) func) { if (_listenerMap.containsKey(type)) { _listenerMap[type]?.remove(func); } } void addEventListener(IsolateEvent event, void Function(Object? data) func) { removeEventListener(event, func); if (!_listenerEventMap.containsKey(event)) { _listenerEventMap[event] = []; } _listenerEventMap[event]?.add(func); } void removeEventListener( IsolateEvent event, void Function(Object? data) func) { if (_listenerEventMap.containsKey(event)) { _listenerEventMap[event]?.remove(func); } } void broadcast(String type, Object? data) { IsolateManager.sendBroadcast(IsoMessege(type, data: data)); } void _isolateEventHandler(IsolateEvent event, Object? data) { if (_listenerEventMap.containsKey(event)) { var list = _listenerEventMap[event]; for (var item in list!) { item(data); } } } void _isolateMessageHandler(IsoMessege message) { var type = message.type; if (_listenerMap.containsKey(type)) { var list = _listenerMap[type]; for (var item in list!) { item(message.data); } } } void exit() { IsolateManager.exit(_id); } } class IsolateManager { IsolateManager._privateConstructor(); static final IsolateManager _instance = IsolateManager._privateConstructor(); static IsolateManager get instance => _instance; static int _nonce = 0; static int get nonce { if (_nonce == double.maxFinite.toInt()) { _nonce = 0; } else { _nonce++; } return _nonce; } final Map _isoMap = {}; final Map> _innerEventMap = {}; static IsolateHandler create(T inst, {Object? initData}) { var id = nonce; var handler = IsolateHandler(id); instance._create(inst, initData, id, handler._isolateEventHandler, handler._isolateMessageHandler); return handler; } void _create( T inst, Object? initData, int id, Function(IsolateEvent, Object?) isolateEventListener, Function(IsoMessege) messageListener) async { final isoListener = ReceivePort(); inst.id = id; _isoMap[id] = inst; _addInnerEvent(id, _IsolateInnerEvent.ready, (message) { inst.isoListener = message as SendPort; isolateEventListener(IsolateEvent.ready, id); }); await Isolate.spawn( inst.initialize, _IsolateConstructData(id, isoListener.sendPort, system.appName, system.appIdendifier, initData)); await for (IsoMessege message in isoListener) { if (message.type == _IsolateInnerEvent.exit) { isolateEventListener(IsolateEvent.exit, null); break; } else if (_filterInnerEvent(id, message)) { messageListener(message); } } isoListener.close(); _isoMap.remove(id); } void _addInnerEvent(int id, String type, Function(Object?) listener) { Map? eventMap; if (!_innerEventMap.containsKey(id)) { eventMap = {}; _innerEventMap[id] = eventMap; } eventMap![type] = listener; } bool _filterInnerEvent(int id, IsoMessege message) { var isNotExist = true; if (_innerEventMap.containsKey(id) && _innerEventMap[id]!.containsKey(message.type)) { isNotExist = false; _innerEventMap[id]![message.type]!(message.data); } return isNotExist; } static void sendBroadcast(IsoMessege message) { var entries = instance._isoMap.entries; for (var entry in entries) { entry.value.insertEvent(message); } } static void send(int id, IsoMessege message) { var map = instance._isoMap; if (map.containsKey(id)) { map[id]?.insertEvent(message); } } static void exit(int id) { var map = instance._isoMap; if (map.containsKey(id)) { map[id]!.insertEvent(IsoMessege(_IsolateInnerEvent.exit)); } } } class _IsolateEventListener { late bool executeOnce; late Function(Object?) listener; _IsolateEventListener(this.listener, this.executeOnce); } class IsolateBase { late int id; late ReceivePort _mainIsoListener; late SendPort _mainIsoSender; late SendPort isoListener; final Map> _eventMap = {}; final List<_IsolateEventListener> _removeList = []; // ignore: library_private_types_in_public_api Future initialize(_IsolateConstructData initData) async { id = initData.id; _mainIsoListener = ReceivePort(); _mainIsoSender = initData.mainIsoSender; system.initialize(initData.appName, initData.appID); send(IsoMessege(_IsolateInnerEvent.ready, data: _mainIsoListener.sendPort)); onReady(initData.initData); await for (IsoMessege message in _mainIsoListener) { if (message.type == _IsolateInnerEvent.exit) { send(IsoMessege(_IsolateInnerEvent.exit)); break; } else { _onEvent(message); } } Isolate.exit(_mainIsoSender); } void onReady(Object? initData) async {} void send(IsoMessege message) { _mainIsoSender.send(message); } void _onEvent(IsoMessege message) { if (_eventMap.containsKey(message.type)) { var list = _eventMap[message.type]!; for (var item in list) { item.listener(message.data); if (item.executeOnce) { _removeList.add(item); } } if (_removeList.isNotEmpty) { for (var item in _removeList) { removeEventListener(message.type, item.listener); } _removeList.clear(); } } } void addEventListener(String type, Function(Object?) listener, {bool executeOnce = false}) { List<_IsolateEventListener> list; if (_eventMap.containsKey(type)) { list = _eventMap[type]!; } else { list = []; _eventMap[type] = list; } list.add(_IsolateEventListener(listener, executeOnce)); } void removeEventListener(String type, Function(Object?) listener) { if (_eventMap.containsKey(type)) { var list = _eventMap[type]!; for (int i = 0; i < list.length; ++i) { var item = list[i]; if (item.listener == listener) { list.removeAt(i); --i; } } } } void insertEvent(IsoMessege message) { isoListener.send(message); } void exit() { IsolateManager.exit(id); } }