import SearchElement from "./element.js";
import Search, {MENU_SEARCH_SELECTOR} from "./search.js";

const MIN_CHARS_BEFORE_FILTER = 3;

/**
 * Input text box for the site menu search.
 * @see Search
 */
export default class SearchTextbox extends SearchElement {
	/**
	 * @param {Search} search
	 */
	constructor(search) {
		super(search, document.querySelector(`${MENU_SEARCH_SELECTOR} > input`));
		
		this.elem.addEventListener('click', e => {
			// Avoid collapsing the menu on textbox click.
			e.stopPropagation();  
		});
		this.elem.addEventListener('input', e => this.onChange(e));
		this.elem.addEventListener('keydown', e => this.onKeyDown(e));
	}

	/**
	 * @param {KeyboardEvent} event
	 */
	onKeyDown(event) {
		// Stop default search behaviour with Up/Down keys (cursor home/end).
		switch (event.key) {
			case 'ArrowUp':
			case 'ArrowDown':
				event.preventDefault();
				break;
		}
		
		// NB: Escape will clear the input by default on most browsers.
		// This condition will only match if Esc is pressed *provided* the textbox is already empty.
		if (event.key === 'Escape' && this.elem.value.length === 0) {
			this.search.setActive(false);
		}
	}
	onChange() {
		// Activate the search if necessary:
		this.search.setActive();
	 	let wordSet = SearchTextbox.parseQuery(this.elem.value);
		let currFilter = this.search.getFilter();
		
		// If not enough characters have been entered to activate the filter.
		if (Array.from(wordSet).join('').length < MIN_CHARS_BEFORE_FILTER) {
			// If a filter was previously set and we are now deactivating it, reset the menu so its items are 
			// in a sensible expand/collapse state and is not excessively expanded.
			if (currFilter) {
				this.search.resetMenu();
				this.search.clearFilter();
			}
			return;
		}
		// Do nothing if the wordSet hasn't changed.
		if (currFilter && setEquality(wordSet, currFilter.getWordSet())) {
			return;
		}
		
		// If we are now activating a filter, when there was previously *no* filter set:
		if (!currFilter) {
			// Reset the menu. This will ensure e.g. the "current open location" view (when navigating to new a page)
			// does not interfere with menu search. 
			// See Web/Static/Lib/navigation/menu-essential.js:16
			this.search.resetMenu();
		}
		this.search.applyFilter(wordSet);
	}
	/**
	 * @param string
	 * @return {Set<string>}
	 */
	static parseQuery(rawSearchQuery) {
		// Reject < and > characters (used as markers for filter highlighting)
		let searchQuery = rawSearchQuery.replaceAll('<', ' ').replaceAll('>', ' ');
		// Split on any whitespace character and filter to strings longer than 1 char (performance) 
		let words = searchQuery.toLowerCase().split(/\s/).filter(str => str.length > 1);
		// Instantiate into a Set to ensure unique words only.
		return new Set(words);
	}
}
