var Assert = require('assert');

/**
* A UI component that allows the user to make a selection from a list.
*
* @memberof module:libresignage/ui/components
*/
class DropSelect {
	/**
	* Construct a new DropSelect object.
	*
	* The container provided to this function *must* have an ID.
	*
	* @param {HTMLElement} container The container where the DropSelect
	*                                is created.
	*/
	constructor(container) {
		Assert.ok(container.id != '', 'Container element must have an ID.');

		this.container = container;
		this.options   = null;
		this.selected  = null;

		this.container.classList.add('dropdown', 'dropselect')
		this.container.innerHTML = DropSelect.make_html(this.container.id);

		// Attach open/close event listeners.
		this.container.addEventListener(('show.bs.dropdown'), e => {
			this.container.dispatchEvent(
				new Event('component.dropselect.show')
			);
		});
		this.container.addEventListener(('hide.bs.dropdown'), e => {
			this.container.dispatchEvent(
				new Event('component.dropselect.hide')
			);
		});
	}

	/**
	* Create the DropSelect HTML from a template.
	*
	* @param {string} container_id The ID of the container where the DropSelect
	*                              is created.
	*
	* @returns {string} The generated HTML.
	*/
	static make_html(container_id) {
		return `
			<button id="btn-${container_id}-dropselect"
				type="button"
				class="btn dropdown-toggle dropselect-open"
				data-toggle="dropdown"
				aria-haspopup="true"
				aria-expanded="false">
			</button>
			<div class="dropdown-menu dropselect-menu"
				aria-labelledby="btn-${container_id}-dropselect">
			</div>
		`;
	}

	/**
	* Set the options of a DropSelect.
	*
	* @param {string[]}    options An array of options.
	* @param {string|null} def     The default option to select.
	*/
	set_options(options, def) {
		this.options  = options;
		this.selected = null;

		let dropselect_menu = this.container.querySelector('.dropselect-menu');
		dropselect_menu.innerHTML = '';

		// Show the '< Empty >' item for empty lists.
		if (options.length === 0) {
			dropselect_menu.innerHTML += `
				<button class="dropdown-item"
						type="button"
						disabled>
					< Empty >
				</button>
			`;
			return;
		}

		// Add selector options from 'options'.
		for (let o of options) {
			let btn = document.createElement('button');
			btn.classList.add('dropdown-item');
			btn.type = 'button';
			btn.value = o;
			btn.innerHTML = `
				<i class="fas fa-angle-right"></i>
				${o}
			`;

			dropselect_menu.appendChild(btn);
			btn.addEventListener('click', () => {
				this.select(o, true)
			});
		}

		if (def != null) { this.select(def, false); }
	}

	/**
	* Select an option.
	*
	* @param {string|null} option         The option to select or null.
	* @param {boolean}     dispatch_event Whether to dispatch the
	*                                     component.dropselect.select event on
	*                                     the DropSelect container.
	*
	* @throws {AssertionError} If no options are set.
	* @throws {AssertionError} If the requested option doesn't exist.
	*/
	select(option, dispatch_event) {
		Assert.ok(this.options != null, 'No options set.');
		Assert.ok(this.options.includes(option), 'No such option.');

		let elems = [
			...this.container.querySelector('.dropselect-menu').children
		];

		this.selected = option;
		for (let e of elems) {
			if (e.getAttribute('value') === option) {
				e.classList.add('dropselect-selected');
			} else {
				e.classList.remove('dropselect-selected');
			}
		}
		if (dispatch_event) {
			this.container.dispatchEvent(
				new Event('component.dropselect.select')
			);
		}
	}

	/**
	* Set the innerHTML of the DropSelect button.
	*
	* @param {string} html The HTML to set.
	*/
	set_button_html(html) {
		this.container.querySelector('.dropselect-open').innerHTML = html;
	}

	/**
	* Get the current selection.
	*
	* @return {string|null} The current selection.
	*/
	get_selection() {
		return this.selected;
	}
}
module.exports = DropSelect;
