const vscode = require("vscode");
const codemp = require("./codemp.node");

var CLIENT = null
var CONTROLLER
var CURSOR
var DECORATION = null
var OP_CACHE = new Set()

async function activate(context) {
	context.subscriptions.push(
		vscode.commands.registerCommand("codemp.connect", connect),
		vscode.commands.registerCommand("codemp.share", share),
		vscode.commands.registerCommand("codemp.join", join),
	)
}

async function connect() {
	let host = await vscode.window.showInputBox({prompt: "server host (default to http://fantabos.co:50051)"})
	if (host === undefined) return  // user cancelled with ESC
	if (host.length == 0) host = "http://fantabos.co:50051"
	CLIENT = await codemp.connect(host)
	vscode.window.showInformationMessage(`Connected to codemp @[${host}]`);
}

async function share() {
	if (CLIENT === null) {
		vscode.window.showErrorMessage("No connected client");
	}

	let path = await vscode.window.showInputBox({prompt: "buffer uri (default to file path)"})
	if (path === undefined) return  // user cancelled with ESC
	if (path.length == 0) path = doc.uri.toString()

	let doc = vscode.window.activeTextEditor.document;

	try {
		if (!await CLIENT.create(path, doc.getText())) {
			vscode.window.showErrorMessage("Could not share buffer");
		}

		await _attach(path)

		vscode.window.showInformationMessage(`Shared document on buffer "${path}"`);
	} catch (err) {
		vscode.window.showErrorMessage("Error sharing: " + err)
	}
}

async function join() {
	if (CLIENT === null) {
		vscode.window.showErrorMessage("No connected client");
	}

	let path = await vscode.window.showInputBox({prompt: "buffer uri"})

	try {
		let controller = await _attach(path)

		vscode.window.showInformationMessage(`Joined buffer "${path}"`);

		let editor = vscode.window.activeTextEditor

		let range = new vscode.Range(
			editor.document.positionAt(0),
			editor.document.positionAt(editor.document.getText().length)
		)
		let content = controller.content()
		OP_CACHE.add((range, content))
		editor.edit(editBuilder => editBuilder.replace(range, content))
	} catch (err) {
		vscode.window.showErrorMessage("error joining " + err)
	}
}

function _order_tuples(a, b) {
	if (a[0] < b[0]) return (a, b)
	if (a[0] > b[0]) return (b, a)
	if (a[1] < b[1]) return (a, b)
	return (b, a)
}

async function _attach(path) {
	let editor = vscode.window.activeTextEditor
	let doc = editor.document;

	CURSOR = await CLIENT.listen()
	CURSOR.callback((usr, path, start, end) => {
		try {
			if (DECORATION != null) {
				DECORATION.dispose()
				DECORATION = null
			}
			const range_start = new vscode.Position(start[0] - 1, start[1]);
			const range_end = new vscode.Position(end[0] - 1, end[1]);
			const decorationRange = new vscode.Range(range_start, range_end);
			DECORATION = vscode.window.createTextEditorDecorationType(
				{backgroundColor: 'red', color: 'white'}
			)
			editor.setDecorations(DECORATION, [decorationRange])
		} catch (err) {
			vscode.window.showErrorMessage("error setting cursor decoration: " + err)
		}
	})
	vscode.window.onDidChangeTextEditorSelection(async (e) => {
		let buf = e.textEditor.document.uri.toString()
		let selection = e.selections[0] // TODO there may be more than one cursor!!
		let anchor = [selection.anchor.line+1, selection.anchor.character]
		let position = [selection.active.line+1, selection.active.character+1]
		// (anchor, position) = _order_tuples(anchor, position)
		await CURSOR.send(buf, anchor, position)
	})

	CONTROLLER = await CLIENT.attach(path)
	CONTROLLER.callback((start, end, text) => {
		try {
			let range = new vscode.Range(
				editor.document.positionAt(start),
				editor.document.positionAt(end)
			)
			OP_CACHE.add((range, text))
			editor.edit(editBuilder => editBuilder.replace(range, text))
		} catch (err) {
			vscode.window.showErrorMessage("could not set buffer: " + err)
		}
	})
	vscode.workspace.onDidChangeTextDocument(async (e) => {
		if (e.document != doc) return
		for (let change of e.contentChanges) {
			if (OP_CACHE.has((change.range, change.text))) {
				OP_CACHE.delete((change.range, change.text))
				continue
			}
			try {
				await CONTROLLER.apply(change.rangeOffset, change.text, change.rangeOffset + change.rangeLength)
			} catch (err) {
				vscode.window.showErrorMessage("failed sending change: " + err)
			}
		}
	})
	return CONTROLLER
}

module.exports = {
	activate,
}