import Menu, {MENU_SELECTOR} from "../menu.js";
import Filter, {ITEM_FILTERED_STYLE} from "../filter.js";
import SearchButton from "./button.js";
import SearchTextbox from "./textbox.js";

/**
 * Selector for container element of search elements (mainly the textbox & button).
 * @type {string}
 */
export const MENU_SEARCH_SELECTOR = '#menu_search';

/**
 * Indicates "Selected" menu item. Should only be applied to a single menu item.  
 * Used for keyboard navigation i.e. Arrow keys to navigate, Enter to open menu item.
 * @type {string}
 */
export const ITEM_SELECTED_STYLE = 'selected-item-cursor';

/** 
 * Enums for search result cursor move directions. These should be invoked by keyboard.
 */
const UP = 'UP';
const DOWN = 'DOWN';

/**
 * Handles site menu search/whole functionality surrounding the "Find" button.
 * Applies a Filter instance when a suitable search query is entered.
 * Search results may be navigated via ArrowUp, ArrowDown and Enter on the keyboard.
 * 
 * Encapsulates and creates instances of these search components:
 * @see SearchTextbox 
 * @see SearchButton 
 * @see Filter 
 */
export default class Search {
	/**
	 * @param {Menu} menu
	 */
	constructor(menu) {
		this.menu = menu;
		this.container = document.getElementById('menu_search');
		this.textbox = new SearchTextbox(this);
		this.button = new SearchButton(this);
		/**
		 * @type {?Filter}
		 */
		this.currFilter = null;
		// Enable search:
		this.container.classList.remove('disabled');
	}
	getFilter() {
		return this.currFilter;
	}
	clearFilter() {
		if (this.currFilter) {
			this.currFilter.clear();
		}
		this.currFilter = null;
	}
	/**
	 * @param {Set<string>} wordSet
	 */
	applyFilter(wordSet) {
		this.clearFilter();
		this.currFilter = new Filter(this.menu, wordSet);
		this.currFilter.apply();
	}
	/**
	 * On key press while menu is open.
	 * Should be called by {Menu} 
	 * @param {KeyboardEvent} e
	 */
	onKeyDown(e) {
		if (this.checkKeyboardNavigation(e)) {
			return;
		}
		if (isPrintableKey(e)) {
			// The key event will bubble up and write the key to the textbox.
			this.focusTextbox();
		}
	}
	focusTextbox(focus=true) {
		if (focus) {
			this.textbox.elem.focus();
		} else {
			this.textbox.elem.blur();
		}
	}

	/**
	 * @param {KeyboardEvent} e
	 */
	checkKeyboardNavigation(e) {
		switch(e.key) {
			case 'ArrowUp':
				this.moveSelectedItem(UP);
				return true;
			case 'ArrowDown':
				this.moveSelectedItem(DOWN);
				return true;
			case 'Enter':
				let link = document.querySelector(`dt.${ITEM_SELECTED_STYLE} a`);
				if (link) {
					link.click();
				}
				return true;
		}
		return false;
	}
	moveSelectedItem(dir) {
		let rawItems = Array.from(document.querySelectorAll(`dt.${ITEM_FILTERED_STYLE}`));
		let invisibleItems = Array.from(document.querySelectorAll(`dt.${ITEM_FILTERED_STYLE}.group:not(.expanded) + dd dt`));
		let items = rawItems.filter(elem => !invisibleItems.includes(elem));
		
		let selectedItem = document.querySelector(`dt.${ITEM_SELECTED_STYLE}`);
		let itemPos = items.indexOf(selectedItem);
		let newPos = (()=>{switch (dir) {
			case UP:   return itemPos-1;
			case DOWN: return itemPos+1;
		}})();
		if (newPos < 0 || newPos >= (items.length)) {
			return false;
		}
		selectedItem.classList.remove(ITEM_SELECTED_STYLE);
		let newSelected = items[newPos];
		newSelected.classList.add(ITEM_SELECTED_STYLE);
		newSelected.scrollIntoView({behavior: "auto", block: "nearest"});
		return true;
	}
	
	resetMenu() {
		// Go through all .expanded items and remove .expanded and add display:none to dd.
		this.menu.resetItemStates();
	}
	async toggleActive() {
		return this.setActive(!this.isActive());
	}
	/**
	 * Activate or deactivate the search.
	 * @param {boolean} active
	 * @returns {Promise<null>} Resolves when the visible animation is finished.
	 */
	async setActive(active=true) {
		// Active state unchanged:
		if (this.isActive() === active) {
			return null;
		}
		this.container.classList.toggle('active', active);
		this.menu.setHeaderSticky(active);
		
		// Deactivated -> active
		if (active) {
			// Focus on the textbox 
			this.textbox.elem.focus();
		 	if (this.currFilter) {
		 		this.currFilter.apply();
			}
		} else { // Active -> deactivated
			if (this.currFilter) {
				this.currFilter.clear();
				this.resetMenu();
			}
		}
		// Collapse or expand button: 
		this.button.elem.classList.toggle('collapsed', active);
	}
	isActive() {
		return this.container.classList.contains('active')
	}
}
