import { h } from '@ledge/jsx';
import { NgInputController, NgInputOptions } from './shared';

const DEFAULT_MAX_HEIGHT = 9e4;

class TextBoxController extends NgInputController {
	public $element!: HTMLTextAreaElement;
	public $mirror;

	constructor() {
		super();

		this.$mirror =
			<textarea aria-hidden='true' title='Hidden TextArea' tabIndex={-1} /> as HTMLTextAreaElement;

		this.$mirror.style.setProperty('position', 'absolute');
		this.$mirror.style.setProperty('top', '-999px');
		this.$mirror.style.setProperty('right', 'auto');
		this.$mirror.style.setProperty('bottom', 'auto');
		this.$mirror.style.setProperty('left', '0');
		this.$mirror.style.setProperty('overflow', 'hidden');
		this.$mirror.style.setProperty('box-sizing', 'content-box');
		this.$mirror.style.setProperty('min-height', '0', 'important');
		this.$mirror.style.setProperty('height', '0', 'important');
		this.$mirror.style.setProperty('padding', '0');
		this.$mirror.style.setProperty('word-wrap', 'break-word');
		this.$mirror.style.setProperty('border', '0');

		// append mirror to the DOM
		document.body.appendChild(this.$mirror);
	}

	public $onInit() {
		// the query *must* return an element or nothing we've done so far matters
		const element = this.$element.querySelector('textarea') as HTMLTextAreaElement;
		let computedStyle = window.getComputedStyle(element);

		const boxOuter = {
			width:
				parseInt(computedStyle.getPropertyValue('border-right-width'), 10) +
				parseInt(computedStyle.getPropertyValue('padding-right'), 10) +
				parseInt(computedStyle.getPropertyValue('padding-left'), 10) +
				parseInt(computedStyle.getPropertyValue('border-left-width'), 10),
			height:
				parseInt(computedStyle.getPropertyValue('border-top-width'), 10) +
				parseInt(computedStyle.getPropertyValue('padding-top'), 10) +
				parseInt(computedStyle.getPropertyValue('padding-bottom'), 10) +
				parseInt(computedStyle.getPropertyValue('border-bottom-width'), 10),
		};

		const minHeightValue = parseInt(computedStyle.getPropertyValue('min-height'), 10);
		const heightValue = parseInt(computedStyle.getPropertyValue('height'), 10);
		const minHeight = Math.max(minHeightValue, heightValue) - boxOuter.height;

		// opera returns max-height of -1 if not set
		let maxHeight = parseInt(computedStyle.getPropertyValue('max-height'), 10);
		maxHeight = maxHeight && maxHeight > 0 ? maxHeight : DEFAULT_MAX_HEIGHT;

		// set resize and apply elastic
		element.style.setProperty('resize', 'none');

		const mirror = this.$mirror;
		function adjust() {
			requestAnimationFrame(() => {
				computedStyle = window.getComputedStyle(element);

				const width = `${parseInt(computedStyle.getPropertyValue('width'), 10) - boxOuter.width}px`;
				mirror.style.setProperty('width', width);
				mirror.style.setProperty('overflow-y', computedStyle.getPropertyValue('overflow-y'));

				mirror.value = element.value;

				let { scrollHeight } = mirror;
				let overflow = 'hidden';

				if (scrollHeight > maxHeight) {
					scrollHeight = maxHeight;
					overflow = 'scroll';
				} else if (scrollHeight < minHeight) {
					scrollHeight = minHeight;
				}

				element.style.setProperty('overflow-y', overflow);

				scrollHeight += boxOuter.height;
				if (parseInt(computedStyle.getPropertyValue('height'), 10) !== scrollHeight) {
					element.style.setProperty('height', `${scrollHeight}px`);
				}
			});
		}

		window.addEventListener('resize', adjust);
		element.addEventListener('keydown', adjust);

		this.$scope.$on('$destroy', () => {
			document.body.removeChild(this.$mirror);
			window.removeEventListener('resize', adjust);
			element.removeEventListener('keydown', adjust);
		});

		// copy the essential styles from the textarea to the mirror
		const styles = [
			'font-family',
			'font-size',
			'font-weight',
			'font-style',
			'letter-spacing',
			'line-height',
			'text-transform',
			'word-spacing',
			'text-indent',
			'white-space',
		];

		for (const style of styles) {
			this.$mirror.style.setProperty(style, computedStyle.getPropertyValue(style));
		}
	}
}

export const textBox: NgInputOptions = {
	type: 'input',
	attrs: { maxlength: 3000, placeholder: '' },
	render() {
		const textArea = <textarea class='form-control' maxLength={3000} placeholder='{{placeholder}}' />;

		textArea.style.setProperty('overflow', 'hidden');
		textArea.style.setProperty('overflow-y', 'hidden');
		textArea.style.setProperty('word-wrap', 'break-word');

		return textArea;
	},
	controller: TextBoxController,
};
