(function() {

	if (window.jopext_hasRun) return;
	window.jopext_hasRun = true;

	console.info('jopext: Loading content script');

	let browser_ = null;
	if (typeof browser !== 'undefined') {
		browser_ = browser;
		browserSupportsPromises_ = true;
	} else if (typeof chrome !== 'undefined') {
		browser_ = chrome;
		browserSupportsPromises_ = false;
	}

	function pageTitle() {
		const titleElements = document.getElementsByTagName("title");
		if (titleElements.length) return titleElements[0].text.trim();
		return document.title.trim();
	}

	function baseUrl() {
		let output = location.origin + location.pathname;
		if (output[output.length - 1] !== '/') {
			output = output.split('/');
			output.pop();
			output = output.join('/');
		}
		return output;
	}

	// Cleans up element by removing all its invisible children (which we don't want to render as Markdown)
	function cleanUpElement(element) {
		const childNodes = element.childNodes;

		for (let i = 0; i < childNodes.length; i++) {
			const node = childNodes[i];

			let isVisible = node.nodeType === 1 ? window.getComputedStyle(node).display !== 'none' : true;
			if (isVisible && ['input', 'textarea', 'script', 'noscript', 'style', 'select', 'option', 'button'].indexOf(node.nodeName.toLowerCase()) >= 0) isVisible = false;

			if (!isVisible) {
				element.removeChild(node);
			} else {
				cleanUpElement(node);
			}
		}
	}

	function readabilityProcess() {
		var uri = {
			spec: location.href,
			host: location.host,
			prePath: location.protocol + "//" + location.host,
			scheme: location.protocol.substr(0, location.protocol.indexOf(":")),
			pathBase: location.protocol + "//" + location.host + location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1)
		};

		// Readability directly change the passed document so clone it so as
		// to preserve the original web page.
		const documentClone = document.cloneNode(true);
		const readability = new Readability(documentClone); // new window.Readability(uri, documentClone);
		const article = readability.parse();

		if (!article) throw new Error('Could not parse HTML document with Readability');

		return {
			title: article.title,
			body: article.content,
		}
	}

	async function prepareCommandResponse(command) {
		console.info('Got command: ' + command.name);

		const clippedContentResponse = (title, html) => {
			return {
				name: 'clippedContent',
				title: title,
				html: html,
				base_url: baseUrl(),
				url: location.origin + location.pathname,
				parent_id: command.parent_id,
			};			
		}

		if (command.name === "simplifiedPageHtml") {

			let article = null;
			try {
				article = readabilityProcess();
			} catch (error) {
				console.warn(error);
				console.warn('Sending full page HTML instead');
				const newCommand = Object.assign({}, command, { name: 'completePageHtml' });
				const response = await prepareCommandResponse(newCommand);
				response.warning = 'Could not retrieve simplified version of page - full page has been saved instead.';
				return response;
			}
			return clippedContentResponse(article.title, article.body);

		} else if (command.name === "completePageHtml") {

			const cleanDocument = document.body.cloneNode(true);
			cleanUpElement(cleanDocument);
			return clippedContentResponse(pageTitle(), cleanDocument.innerHTML);

		} else if (command.name === "selectedHtml") {

		    const range = window.getSelection().getRangeAt(0);
		    const container = document.createElement('div');
		    container.appendChild(range.cloneContents());
		    return clippedContentResponse(pageTitle(), container.innerHTML);

		} else if (command.name === 'screenshot') {

			const overlay = document.createElement('div');
			overlay.style.opacity = '0.6';
			overlay.style.background = 'black';
			overlay.style.width = '100%';
			overlay.style.height = '100%';
			overlay.style.zIndex = 99999999;
			overlay.style.top = 0;
			overlay.style.left = 0;
			overlay.style.position = 'fixed';

			document.body.appendChild(overlay);

			const messageComp = document.createElement('div');

			const messageCompWidth = 300;
			messageComp.style.position = 'fixed'
			messageComp.style.opacity = '0.95'
			messageComp.style.fontSize = '14px';
			messageComp.style.width = messageCompWidth + 'px'
			messageComp.style.maxWidth = messageCompWidth + 'px'
			messageComp.style.border = '1px solid black'
			messageComp.style.background = 'white'
			messageComp.style.top = '10px'
			messageComp.style.textAlign = 'center';
			messageComp.style.padding = '10px'
			messageComp.style.left = Math.round(document.body.clientWidth / 2 - messageCompWidth / 2) + 'px'
			messageComp.style.zIndex = overlay.style.zIndex + 1

			messageComp.textContent = 'Drag and release to capture a screenshot';

			document.body.appendChild(messageComp);

			const selection = document.createElement('div');
			selection.style.opacity = '0.4';
			selection.style.border = '1px solid red';
			selection.style.background = 'white';
			selection.style.border = '2px solid black';
			selection.style.zIndex = overlay.style.zIndex - 1;
			selection.style.top = 0;
			selection.style.left = 0;
			selection.style.position = 'fixed';

			document.body.appendChild(selection);

			let isDragging = false;
			let draggingStartPos = null;
			let selectionArea = {};

			function updateSelection() {
				selection.style.left = selectionArea.x + 'px';
				selection.style.top = selectionArea.y + 'px';
				selection.style.width = selectionArea.width + 'px';
				selection.style.height = selectionArea.height + 'px';
			}

			function setSelectionSizeFromMouse(event) {
				selectionArea.width = Math.max(1, event.clientX - draggingStartPos.x);
				selectionArea.height = Math.max(1, event.clientY - draggingStartPos.y);
				updateSelection();
			}

			function selection_mouseDown(event) {
				selectionArea = { x: event.clientX, y: event.clientY, width: 0, height: 0 }
				draggingStartPos = { x: event.clientX, y: event.clientY };
				isDragging = true;
				updateSelection();
			}

			function selection_mouseMove(event) {
				if (!isDragging) return;
				setSelectionSizeFromMouse(event);
			}

			function selection_mouseUp(event) {
				setSelectionSizeFromMouse(event);

				isDragging = false;

				overlay.removeEventListener('mousedown', selection_mouseDown);
				overlay.removeEventListener('mousemove', selection_mouseMove);
				overlay.removeEventListener('mouseup', selection_mouseUp);

				document.body.removeChild(overlay);
				document.body.removeChild(selection);
				document.body.removeChild(messageComp);

				console.info('jopext: selectionArea:', selectionArea);

				if (!selectionArea || !selectionArea.width || !selectionArea.height) return;

				// Need to wait a bit before taking the screenshot to make sure
				// the overlays have been removed and don't appear in the
				// screenshot. 10ms is not enough.
				setTimeout(() => {
					const content = {
						title: pageTitle(),
						crop_rect: selectionArea,
						url: location.origin + location.pathname,
						parent_id: command.parent_id,
					};

					browser_.runtime.sendMessage({
						name: 'screenshotArea',
						content: content,
						api_base_url: command.api_base_url,
					});
				}, 100);
			}

			overlay.addEventListener('mousedown', selection_mouseDown);
			overlay.addEventListener('mousemove', selection_mouseMove);
			overlay.addEventListener('mouseup', selection_mouseUp);

			return {};

		} else {
			throw new Error('Unknown command: ' + JSON.stringify(command));
		}
	}

	async function execCommand(command) {
		const response = await prepareCommandResponse(command);
		browser_.runtime.sendMessage(response);
	}

	browser_.runtime.onMessage.addListener((command) => {
		console.info('jopext: Got command:', command);

		execCommand(command);
	});

})();