var Assert = require('assert');
var ValidatorSelector = require('libresignage/ui/validator/ValidatorSelector');
var ValidatorTrigger = require('libresignage/ui/validator/ValidatorTrigger');
var AlertDialog = require('libresignage/ui/components/Dialog/AlertDialog');

/**
* @typedef {object} MultiSelectSettings
*
* @property {boolean} nodups  Allow duplicate selections.
* @property {number}  maxopts The maximum number of selections allowed.
*
* @memberof module:libresignage/ui/components
* @alias    MultiSelectSettings
*/

/**
* A UI component that allows the user to write an input and add it to
* a list of selections.
*
* @memberof module:libresignage/ui/components
*/
class MultiSelect {
	/**
	* Construct a new MultiSelect object.
	*
	* @param {string}              container_id The ID of the container where
	*                                           the MultiSelect is created.
	* @param {string}              ms_id        The ID of the MultiSelect to
	*                                           create.
	* @param {BaseValidator[]}     validators   An array of validators to use
	*                                           for the MultiSelect input.
	* @param {MultiSelectSettings} settings     A settings object.
	*
	* @throws {AssertionError} If the supplied container_id is invalid.
	* @throws {AssertionError} If the supplied ms_id is invalid.
	*/
	constructor(container_id, ms_id, validators, settings) {
		Assert.ok(container_id != null, "Invalid container ID.");
		Assert.ok(ms_id != null, "Invalid MultiSelect ID.");

		this.enabled = true;
		this.val_valid = true;
		this.selected = [];
		this.settings = settings;

		this.container = document.getElementById(container_id);
		this.container.innerHTML = MultiSelect.make_html(ms_id)

		this.input = this.container.querySelector('.ms-controls > .ms-input');
		this.btn_add = this.container.querySelector('.ms-controls > .ms-add');
		this.values = this.container.querySelector('.ms-values');

		// Add a listener for Enter keypresses.
		this.input.addEventListener('keypress', (event) => {
			if (event.key == 'Enter' && this.val_valid) {
				this.add(this.input.value);
				this.input.value = '';
				this.vtrig.validate_all();
			}
		});

		// Add a listener for the (+) button.
		this.btn_add.addEventListener('click', () => {
			this.add(this.input.value);
			this.input.value = '';
		});

		// Add validators for the input.
		if (validators && validators.length) {
			(this.vtrig = new ValidatorTrigger(
				[
					new ValidatorSelector(
						this.input,
						this.container,
						validators,
						null
					)
				],
				valid => {
					this.val_valid = valid;
					this.btn_add.disabled = !valid;
				}
			)).trigger();
		}
	}

	/**
	* Create the HTML for a MultiSelect.
	*
	* @param {string} ms_id The DOM ID to use for the MultiSelect.
	*/
	static make_html(ms_id) {
		return `
			<div id="${ms_id}" class="multiselect container-fluid">
				<div class="ms-controls container-fluid">
					<input class="ms-input col form-control" type="text">
					<button class="ms-add col btn btn-primary" type="button">
						<i class="fas fa-plus-circle"></i>
					</button>
					<div class="invalid-feedback"></div>
				</div>
				<div class="ms-values container-fluid"></div>
			</div>
		`;
	}

	/**
	* Get the value of a setting.
	*
	* @param {string} name The name of the setting.
	*
	* @return {mixed} The value of the requested setting or null if
	*                 no such setting exists.
	*/
	get_setting(name) {
		if (name in this.settings) {
			return this.settings[name];
		} else {
			return null;
		}
	}

	/**
	* Select a new option.
	*
	* If the 'nodups' setting is true and 'option' is already selected
	* this function returns without changing anything. If the number of
	* selections would exceed the 'maxopts' setting, this function displays
	* a dialog indicating that and returns.
	*
	* @param {string} option The option to select.
	*/
	add(option) {
		if (option == null) { return; }

		if (this.get_setting('nodups') && this.selected.includes(option)) {
			return;
		}

		if (
			this.get_setting('maxopts')
			&& this.selected.length >= this.get_setting('maxopts')
		) {
			let dialog = new AlertDialog(
				"Too many selections",
				"The maximum amount of selections has been reached.",
				() => {}
			);
			return;
		}

		let div = document.createElement('div');
		div.id = `ms-opt-${option}`;
		div.classList.add('ms-val');
		div.innerHTML = `${option}<span class="ms-rm fas fa-times"></span>`;
		this.values.appendChild(div);

		div.querySelector('.ms-rm').addEventListener(
			'click',
			() => this.remove(option)
		);

		this.selected.push(option);
	}

	/**
	* Select a set of options.
	*
	* This function clears any previous selections.
	*
	* @param {string[]} options An array of options to select.
	*/
	set(options) {
		this.selected = [];
		this.values.innerHTML = '';
		for (let o of options) { this.add(o); }
	}

	/**
	* Remove a selection.
	*
	* @param {string} option The selection to remove.
	*
	* @throws {AssertionError} If 'option' is not selected.
	*/
	remove(option) {
		Assert.ok(this.selected.includes(option), "Option not selected.");
		this.container.querySelector(`#ms-opt-${option}`).outerHTML = '';
		this.selected.splice(this.selected.indexOf(option), 1);
	}

	/**
	* Enable a MultiSelect.
	*/
	enable() {
		this.enabled = true;
		this.input.disabled = false;
		for (let s of this.vtrig.selectors) { s.enable(true); }

		this.values.setAttribute('style', 'background-color: white;');
		for (let e of this.container.querySelectorAll('.ms-rm')) {
			e.setAttribute('style', 'display: inline;');
		}
	}

	/**
	* Disable a MultiSelect.
	*/
	disable() {
		this.enabled = false;
		this.input.disabled = true;
		this.btn_add.disabled = true;
		for (let s of this.vtrig.selectors) { s.enable(false); }

		this.values.setAttribute('style', 'background-color: var(--gray-3);');
		for (let e of this.container.querySelectorAll('.ms-rm')) {
			e.setAttribute('style', 'display: none;');
		}
	}

	/**
	* Get selected options as an array.
	*
	* @param {string[]} The selected options as an array of strings.
	*/
	get_selected() {
		return this.selected;
	}
}
module.exports = MultiSelect;
