/**
 * Captures any pasted data and sends it to method "receivedCopyPasteData" which has to be
 * defined on the component which uses this mixin
 */
import Vue from 'vue';
import { debounce } from 'lodash';

export const copyPaste = Vue.extend({
	data () {
		return {
			copyPasteElementsGotCreated: false,
		};
	},
	mounted () {
		if (this.copyPasteElementsGotCreated === true) {
			return;
		}

		this.copyPasteElementsGotCreated = true;
		// Define the style of the html elements that get created to make
		// sure that they are not visible
		const style = document.createElement('style');
		style.type = 'text/css';
		style.innerHTML = `
			.hidden-copy-paste {
			position: absolute;
			bottom: 0;
			left: 0;
			width: 10px;
			height: 10px;
			display: block;
			font-size: 1px;
			z-index: -1;
			color: transparent;
			background: transparent;
			overflow: hidden;
			border: none;
			padding: 0;
			resize: none;
			outline: none;
			-webkit-user-select: text;
			user-select: text;
		}
		`;
		document.getElementsByTagName('head')[0].appendChild(style);

		// Code is mainly from
		// https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript/
		const isSafari = navigator.appVersion.search('Safari') !== -1 && navigator.appVersion.search('Chrome') === -1 && navigator.appVersion.search('CrMo') === -1 && navigator.appVersion.search('CriOS') === -1;
		const isIe = (navigator.userAgent.toLowerCase().indexOf('msie') !== -1 || navigator.userAgent.toLowerCase().indexOf('trident') !== -1);

		const hiddenInput = document.createElement('input');
		hiddenInput.setAttribute('type', 'text');
		hiddenInput.setAttribute('id', 'hidden-input-copy-paste');
		hiddenInput.setAttribute('class', 'hidden-copy-paste');

		document.body.append(hiddenInput);

		let ieClipboardDiv: HTMLDivElement | null = null;
		if (isIe) {
			ieClipboardDiv = document.createElement('div');
			ieClipboardDiv.setAttribute('id', 'hidden-ie-clipboard-copy-paste');
			ieClipboardDiv.setAttribute('class', 'hidden-copy-paste');
			ieClipboardDiv.setAttribute('contenteditable', 'true');
			document.body.append(ieClipboardDiv);

			document.addEventListener('beforepaste', () => {
				// @ts-ignore
				if (hiddenInput.is(':focus')) {
					this.focusIeClipboardDiv(ieClipboardDiv as HTMLDivElement);
				}
			}, true);
		}

		let userInput = '';
		const hiddenInputListener = (text: string) => { };

		hiddenInput.addEventListener('input', (e) => {
			const value = hiddenInput.value;
			userInput += value;
			hiddenInputListener(userInput);

			// There is a bug (sometimes) with Safari and the input area can't be updated during
			// the input event, so we update the input area after the event is done being processed
			if (isSafari) {
				hiddenInput.focus();
				setTimeout(() => { this.focusHiddenArea(hiddenInput); }, 0);
			} else {
				this.focusHiddenArea(hiddenInput);
			}
		});

		// Set clipboard event listeners on the document.
		['paste'].forEach((event) => {
			document.addEventListener(event, debounce((e) => {
				// Check if the event got emitted from a message box or from something
				// else which should ignore the copy/paste
				// @ts-ignore
				const path = e.path || (e.composedPath && e.composedPath());
				for (let index = 0; index < path.length; index++) {
					if (path[index].className && typeof path[index].className === 'string' && (
						path[index].className.includes('el-message-box') || path[index].className.includes('ignore-key-press')
					)) {
						return;
					}
				}

				if (ieClipboardDiv !== null) {
					this.ieClipboardEvent(event, ieClipboardDiv);
				} else {
					this.standardClipboardEvent(event, e as ClipboardEvent);
					// @ts-ignore
					if (!document.activeElement || (document.activeElement && ['textarea', 'text', 'email', 'password'].indexOf(document.activeElement.type) === -1)) {
						// That it still allows to paste into text, email, password & textarea-fiels we
						// check if we can identify the active element and if so only
						// run it if something else is selected.
						this.focusHiddenArea(hiddenInput);
						e.preventDefault();
					}
				}
			}, 1000, { leading: true }));
		});
	},
	methods: {
		receivedCopyPasteData (plainTextData: string, event?: ClipboardEvent): void {
			// THIS HAS TO BE DEFINED IN COMPONENT!
		},

		// For every browser except IE, we can easily get and set data on the clipboard
		standardClipboardEvent (clipboardEventName: string, event: ClipboardEvent) {
			const clipboardData = event.clipboardData;
			if (clipboardData !== null && clipboardEventName === 'paste') {
				const clipboardText = clipboardData.getData('text/plain');
				this.receivedCopyPasteData(clipboardText, event);
			}
		},

		// For IE, we can get/set Text or URL just as we normally would
		ieClipboardEvent (clipboardEventName: string, ieClipboardDiv: HTMLDivElement) {
			// @ts-ignore
			const clipboardData = window.clipboardData;
			if (clipboardEventName === 'paste') {
				const clipboardText = clipboardData.getData('Text');
				// @ts-ignore
				ieClipboardDiv.empty();
				this.receivedCopyPasteData(clipboardText);
			}
		},

		// Focuses an element to be ready for copy/paste (used exclusively for IE)
		focusIeClipboardDiv (ieClipboardDiv: HTMLDivElement) {
			ieClipboardDiv.focus();
			const range = document.createRange();
			// @ts-ignore
			range.selectNodeContents((ieClipboardDiv.get(0)));
			const selection = window.getSelection();
			if (selection !== null) {
				selection.removeAllRanges();
				selection.addRange(range);
			}
		},

		focusHiddenArea (hiddenInput: HTMLInputElement) {
			// In order to ensure that the browser will fire clipboard events, we always need to have something selected
			hiddenInput.value = ' ';
			hiddenInput.focus();
			hiddenInput.select();
		},

		/**
		 * Copies given data to clipboard
		 */
		copyToClipboard (value: string): void {
			// FROM: https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f
			const element = document.createElement('textarea'); // Create a <textarea> element
			element.value = value; // Set its value to the string that you want copied
			element.setAttribute('readonly', ''); // Make it readonly to be tamper-proof
			element.style.position = 'absolute';
			element.style.left = '-9999px'; // Move outside the screen to make it invisible
			document.body.appendChild(element); // Append the <textarea> element to the HTML document

			const selection = document.getSelection();
			if (selection === null) {
				return;
			}

			const selected = selection.rangeCount > 0 // Check if there is any content selected previously
				? selection.getRangeAt(0) // Store selection if found
				: false; // Mark as false to know no selection existed before
			element.select(); // Select the <textarea> content
			document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
			document.body.removeChild(element); // Remove the <textarea> element
			if (selected) {
				// If a selection existed before copying
				selection.removeAllRanges(); // Unselect everything on the HTML document
				selection.addRange(selected); // Restore the original selection
			}
		},

	},
});
