"use strict";
var gOnInput = null;

var kMaxHistory = 20;
var kShowHistory = 20;

var lastTimestamp = null;

if (imports.terminal) {
	core.register("onMessage", function(sender, message) {
		if (message.message && message.when) {
			printMessage(message, true);
		}
	});
	core.register("onSessionBegin", function(user) {
		if (user.packageName === core.user.packageName &&
			user.index !== core.user.index) {
			listUsers(user.name + " has joined the BBS.  ");
		}
	});
	core.register("onSessionEnd", function(user) {
		if (user.packageName === core.user.packageName &&
			user.index !== core.user.index) {
			listUsers(user.name + " has left the BBS.  ");
		}
	});
} else {
	// Chat service process.
	core.register("onMessage", function(sender, message) {
		if (message.message && message.when) {
			message.sender = sender;
			return database.get("board").catch(function() {
				return null;
			}).then(function(data) {
				try {
					data = JSON.parse(data);
				} catch(error) {
					data = [];
				}
				data.push(message);
				while (data.length > kMaxHistory) {
					data.shift();
				}
				return saveBoard(data);
			}).then(function() {
				return core.broadcast(message);
			});
		}
	});
}

function listUsers() {
	return core.getUsers(core.user.packageOwner, core.user.packageName).then(function(users) {
		terminal.select("users");
		terminal.clear();
		terminal.print("Users:");
		var counts = {};
		for (var i = 0; i < users.length; i++) {
			counts[users[i].name] = (counts[users[i].name] || 0) + 1;
		}
		var names = Object.keys(counts).sort();
		for (var i = 0; i < names.length; i++) {
			var name = names[i];
			var message = [];
			if (message.length > 1) {
				message.push(", ");
			}
			message.push({class: "orange", value: name});
			if (counts[name] > 1) {
				message.push({class: "base01", value: "(x" + counts[name] + ")"});
			}
			terminal.print(message);
		}
		terminal.select("terminal");
	});
}

function saveBoard(data) {
	return database.set("board", JSON.stringify(data)).catch(function(error) {
		if (error.message.indexOf("MDB_MAP_FULL") != -1) {
			data.shift();
			return saveBoard(data);
		} else {
			throw error;
		}
	});
}

core.register("onInput", function(input) {
	if (gOnInput && typeof input == "string") {
		gOnInput(input);
	}
});

function logo() {
	terminal.clear();
	terminal.print("");
	terminal.print("");
	terminal.print('Welcome to');
	terminal.print('   ______                _          ____  ____ _____');
	terminal.print('  / ____/___  _______  _( )_____   / __ )/ __ ) ___/');
	terminal.print(' / /   / __ \\/ ___/ / / /// ___/  / __  / __  \\__ \\ ');
	terminal.print('/ /___/ /_/ / /  / /_/ / (__  )  / /_/ / /_/ /__/ / ');
	terminal.print('\\____/\\____/_/   \\__, / /____/  /_____/_____/____/  ');
	terminal.print('                /____/                              ');
	terminal.print('                    yesterday\'s technology...today!');
	terminal.print("");
}

function welcome() {
	logo();
	chat();
}

function main() {
	terminal.clear();
	logo();
	terminal.print("");
	terminal.print("Main menu commands:");
	terminal.print("  ", {command: "chat"}, "       chat message board");
	terminal.print("  ", {command: "guess"}, "      guess the number game");
	terminal.print("  ", {command: "exit"}, "       back to that sweet logo");
	gOnInput = function(input) {
		input = input.toLowerCase();
		if (input == "chat") {
			chat();
		} else if (input == "guess") {
			guess();
		} else if (input == "exit") {
			terminal.print("Goodbye.");
			exit(0);
		} else {
			terminal.print("I didn't understand that: " + input);
			main();
		}
	};
}

function formatMessage(message) {
	var result;
	if (typeof message == "string") {
		result = [];
		var regex = /(\w+:\/*\S+?)(?=(?:[\.!?])?(?:$|\s))/gi;
		var match;
		var lastIndex = 0;
		while ((match = regex.exec(message)) !== null) {
			result.push({class: "base1", value: message.substring(lastIndex, match.index)});
			result.push({href: match[0]});
			lastIndex = regex.lastIndex;
		}
		result.push({class: "base1", value: message.substring(lastIndex)});
	} else {
		result = message;
	}
	return result;
}

function niceTime(lastTime, thisTime) {
	if (!lastTime) {
		return thisTime;
	}
	let result = [];
	let lastParts = lastTime.split(" ");
	let thisParts = thisTime.split(" ");
	for (let i = 0; i < thisParts.length; i++) {
		if (thisParts[i] !== lastParts[i]) {
			result.push(thisParts[i]);
		}
	}
	return result.join(" ");
}

function printMessage(message, notify) {
	terminal.print(
		{class: "base0", value: niceTime(lastTimestamp, message.when)},
		" ",
		{class: "base00", value: "<"},
		{class: "base3", value: (message.sender ? message.sender.name : "unknown")},
		{class: "base00", value: ">"},
		" ",
		formatMessage(message.message));
	lastTimestamp = message.when;
	if (notify) {
		return core.getUser().then(function(user) {
			if (message.message.indexOf("!") != -1) {
				return terminal.notify("SOMEONE IS SHOUTING!", {body: "<" + (message.sender ? message.sender.name : "unknown") + "> " + message.message});
			} else if (message.message.indexOf(user.name + ":") != -1) {
				return terminal.notify("Someone is talking at you.", {body: "<" + (message.sender ? message.sender.name : "unknown") + "> " + message.message});
			}
		});
	}
}

function chat() {
	terminal.setEcho(false);
	terminal.print("");
	terminal.print("You are now in a chat.  Anything you type will be broadcast to everyone else connected.  To leave, say ", {command: "exit"}, ".");
	listUsers();
	database.get("board").catch(function() {
		return null;
	}).then(function(board) {
		try {
			board = JSON.parse(board);
		} catch (error) {
			board = [];
		}

		for (let i = Math.max(0, board.length - kShowHistory); i < board.length; i++) {
			printMessage(board[i], false);
		}
	});
	gOnInput = function(input) {
		if (input == "exit") {
			terminal.setEcho(true);
			main();
		} else {
			core.getService("chat").then(function(chatService) {
				return chatService.postMessage({when: new Date().toString(), message: input});
			}).catch(function(error) {
				terminal.print("ERROR: " + JSON.stringify(error));
			});
		}
	};
}

function guess() {
	terminal.clear();
	var number = Math.round(Math.random() * 100);
	var guesses = 0;
	terminal.print("OK, I have a number in mind.  What do you think it is?  Use ", {command: "exit"}, " to stop.");
	gOnInput = function(input) {
		if (input == "exit") {
			main();
		} else {
			var guess = parseInt(input);
			guesses++;
			if (input != guess.toString()) {
				terminal.print("I'm not sure that's an integer.  Please guess only integers.");
			} else {
				if (guess < number) {
					terminal.print("Too low.");
				} else if (guess > number) {
					terminal.print("Too high.");
				} else if (guess == number) {
					terminal.print("Wow, you got it in " + guesses + " guesses!  It was " + number + ".");
					guessEnd(guesses);
				}
			}
		}
	};
}

function guessEnd(guesses) {
	terminal.print("What's your name, for the high score table?");
	gOnInput = function(name) {
		var entry = {'guesses': guesses, 'name': name, 'when': new Date().toString()};
		database.get("guessHighScores").then(function(data) {
			data = JSON.parse(data);
			var index = data.length;
			for (var i in data) {
				if (guesses < data[i].guesses) {
					index = i;
					break;
				}
			}
			data.splice(index, 0, entry);
			printHighScores(data);
			database.set("guessHighScores", JSON.stringify(data));
			gOnInput = function() {
				main();
			};
		}).catch(function() {
			var data = [entry];
			printHighScores(data);
			database.set("guessHighScores", JSON.stringify(data));
			main();
		});
	};
}

function printTable(data) {
	var widths = [];
	for (var i in data) {
		var row = data[i];
		for (var c in row) {
			widths[c] = Math.max(widths[c] || 0, row[c].length);
		}
	}

	for (var i in data) {
		var row = data[i];
		var line = "";
		for (var c in row) {
			line += row[c];
			line += " ".repeat(widths[c] - row[c].length + 2);
		}
		terminal.print(line);
	}
}

function printHighScores(data) {
	printTable([["Name", "Guesses", "Date"]].concat(data.map(function(entry) {
		return [entry.name, entry.guesses.toString(), entry.when];
	})));
}

if (imports.terminal) {
	terminal.split([
		{type: "horizontal", children: [
			{name: "terminal", grow: 1},
			{name: "users", grow: 0},
		]},
	]);
	welcome();
}