"use strict";

function App() {
	this._on_output = null;
	this._send_queue = [];
	return this;
}

App.prototype.readOutput = function(callback) {
	this._on_output = callback;
}

App.prototype.makeFunction = function(api) {
	let self = this;
	let result = function() {
		let message = {action: api[0]};
		for (let i = 1; i < api.length; i++) {
			message[api[i]] = arguments[i - 1];
		}
		self.send(message);
	};
	Object.defineProperty(result, 'name', {value: api[0], writable: false});
	return result;
}

App.prototype.send = function(message) {
	if (message) {
		this._send_queue.push(message);
	}
	if (this._on_output) {
		this._send_queue.forEach(message => this._on_output(message));
		this._send_queue = [];
	}
}

function socket(request, response, client) {
	var process;
	var options = {};
	var credentials = auth.query(request.headers);

	response.onMessage = async function(event) {
		if (event.opCode == 0x1 || event.opCode == 0x2) {
			var message;
			try {
				message = JSON.parse(event.data);
			} catch (error) {
				print("ERROR", error, event.data, event.data.length, event.opCode);
				return;
			}
			if (message.action == "hello") {
				var packageOwner;
				var packageName;
				var blobId;
				var match;
				if (match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path)) {
					blobId = match[1];
				} else if (match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path)) {
					var user = match[1];
					var path = match[2];
					blobId = await new Database(user).get('path:' + path);
					if (!blobId) {
						response.send(JSON.stringify({action: "error", error: message.path + ' not found'}), 0x1);
						request.close();
					}
				}
				response.send(JSON.stringify({action: "session", credentials: credentials}), 0x1);

				options.api = message.api || [];
				options.credentials = credentials;
				var sessionId = makeSessionId();
				if (blobId) {
					process = await getSessionProcessBlob(blobId, sessionId, options);
				}
				if (process) {
					process.app.readOutput(function(message) {
						response.send(JSON.stringify(message), 0x1);
					});
					process.app.send();
				}

				var ping = function() {
					var now = Date.now();
					var again = true;
					if (now - process.lastActive < process.timeout) {
						// Active.
					} else if (process.lastPing > process.lastActive) {
						// We lost them.
						if (process.task) {
							process.task.kill();
						}
						again = false;
					} else {
						// Idle.  Ping them.
						response.send("", 0x9);
						process.lastPing = now;
					}

					if (again) {
						setTimeout(ping, process.timeout);
					}
				}

				if (process && process.timeout > 0) {
					setTimeout(ping, process.timeout);
				}
			} else {
				if (process && process.eventHandlers['message']) {
					await invoke(process.eventHandlers['message'], [message]);
				}
			}
		} else if (event.opCode == 0x8) {
			// Close.
			if (process) {
				process.task.kill();
			}
			response.send(event.data, 0x8);
		} else if (event.opCode == 0xa) {
			// PONG
		}

		if (process) {
			process.lastActive = Date.now();
		}
	}
}

exports.socket = socket;