const k_is_browser = get_is_browser();
let g_api = {};
let g_next_id = 1;
let g_calls = {};

function get_is_browser() {
	try { return window !== undefined && console !== undefined; } catch { return false; }
}

if (k_is_browser) {
	print = console.log;
}

function make_rpc(target, prop, receiver) {
	return function() {
		let id = g_next_id++;
		while (!id || g_calls[id] !== undefined) {
			id = g_next_id++;
		}
		let promise = new Promise(function(resolve, reject) {
			g_calls[id] = {resolve: resolve, reject: reject};
		});
		if (k_is_browser) {
			window.parent.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}, '*');
			return promise;
		} else {
			return app.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}).then(x => promise);
		}
	}
}

function send(response) {
	if (k_is_browser) {
		window.parent.postMessage(response, '*');
	} else {
		app.postMessage(response);
	}
}

function call_rpc(message) {
	if (message && message.message === 'tfrpc') {
		let id = message.id;
		if (message.method) {
			let method = g_api[message.method];
			if (method) {
				try {
					Promise.resolve(method(...message.params)).then(function(result) {
						send({message: 'tfrpc', id: id, result: result});
					}).catch(function(error) {
						send({message: 'tfrpc', id: id, error: error});
					});
				} catch (error) {
					send({message: 'tfrpc', id: id, error: error});
				}
			} else {
				send({message: 'tfrpc', id: id, error: `Method '${message.method}' not found.`});
			}
		} else if (message.error !== undefined) {
			if (g_calls[id]) {
				g_calls[id].reject(message.error);
				delete g_calls[id];
			} else {
				throw new Error(id + ' not found to reply.');
			}
		} else {
			if (g_calls[id]) {
				g_calls[id].resolve(message.result);
				delete g_calls[id];
			} else {
				throw new Error(id + ' not found to reply.');
			}
		}
	}
}

if (k_is_browser) {
	window.addEventListener('message', function(event) {
		call_rpc(event.data);
	});
} else {
	core.register('message', function(message) {
		call_rpc(message?.message);
	});
}

export let rpc = new Proxy({}, {get: make_rpc});

export function register(method) {
	g_api[method.name] = method;
}