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

const EXPANDED_STYLE = 'expanded'

/**
 * Menu item in the site menu tree. Can either be an expanding group menu item (branch node) or a clickable link to 
 * another page (leaf node).  
 * @see Menu
 */
export default class MenuItem {
	/**
	 * @param {Menu} menu
	 * @param {MenuItemObj} itemObj
	 * @param {string} name
	 */
	constructor(menu, itemObj, name, parent = null) {
		this.obj = itemObj;
		this.menu = menu;
		this.elem = document.querySelector(MENU_SELECTOR).querySelector('#menu-item-'+this.obj.id);
		this.parent = parent;
		this._name = name;
		this._children = [];
		for (let [name, subItem] of Object.entries(itemObj.subitems || {})){
			this._children.push(new MenuItem(menu, subItem, name, this));
		}
		/** @type {?Element} */
		this.childContainer = MenuItem.getChildContainer(this.elem);
		// Bind click handler
		this.elem.addEventListener('click', e => this.onClick(e), true);
	}
	// MenuItem public properties:
	get name()     { return this._name;     }
	get element()  { return this.elem;      }
	/**
	 * @return {MenuItem[]} Child items. Will be an empty array for leaf nodes.
	 */
	get children() { return this._children; }
	
	// MenuItemObj properties:
	get path()        { return this.obj.path;        }
	get icon()        { return this.obj.icon;        }
	get description() { return this.obj.description; }
	get modal()       { return this.obj.modal;       }
	get id()          { return this.obj.id;          }

	/**
	 * Returns the container <dl> for this item's child item, or `null` if this item has no children.
	 * 
	 * @param {Element} dt Main <dt> element for the menu item.
	 * @return {?Element}
	 */
	static getChildContainer(dt) {
		return dt.nextElementSibling.querySelector('dd > dl');
	}
	
	expand(state = true) {
		if (this.isLeafNode()) {
			return;
		}
		this.setVisible(state);
		this.elem.classList.toggle(EXPANDED_STYLE, state);
		this.childContainer.style.display = state ? 'block' : 'none';
	}
	setVisible(s = true) {
		this.elem.style.display = s ? 'block' : 'none';
	}
	setClass(style, enabled = true) {
		this.element.classList.toggle(style, enabled);
	}
	hasClass(style) {
		return this.elem.classList.contains(style);
	}
	isLeafNode() {
		return this._children.length <= 0;
	}
	isRootNode() {
		return !this.parent;
	}
	/**
	 * @return {Element[]}
	 */
	siblingElements() {
		// todo: parent elem and return children (root nodes: use menu.getItems)?
		return $(this.elem).siblings('dt').toArray();
	}
	/**
	 * Runs before any other click listeners.
	 * @param {Event} event
	 */
	onClick(event) {
		if (!this.menu.isFiltered() || !this.hasClass(ITEM_FILTERED_STYLE) || this.isLeafNode()) {
			return;
		} // Else: The menu is filtered and this is a branch node. Reveal other items to the filter on click.
		
		// if this item is going to collapse (following this click event):
		if (this.elem.classList.contains(EXPANDED_STYLE)) {
			// If only some children are visible: reveal the rest to the filter. 
			if (this.childrenPartiallyFiltered()) {
				// Prevent collapse behaviour as we're revealing the rest of the children.
				event.stopPropagation();
				
				// Reveal children to filter
				this.children.forEach(child => {
					child.setClass('semitransparent', false);
					Filter.showAdditionalItem(child.element)
				});
				
			// Expand sibling items provided this is not a root menu item:
			} else if (!this.isRootNode()) {
				for (let elem of this.siblingElements()) {
					Filter.showAdditionalItem(elem);
				}
			}
		} else { // The item is going to expand:
			// Reveal children to filter
			this.children.forEach(child => child.setClass(ITEM_FILTERED_STYLE));
		}
	}
	childrenPartiallyFiltered() {
		let children = this.children;
		for (let i = 0; i < children.length-1; i++) {
			if (children[i].hasClass(ITEM_FILTERED_STYLE) !== children[i+1].hasClass(ITEM_FILTERED_STYLE)){
				return true;
			}
		}
		return false;
	}
}

/**
 * Plain menu item object generated on server.
 * See PHP: \Cedar\Core\ops_Page_Navigation::getNavigationJSON
 * @typedef {Object} MenuItemObj
 * @property {?string}  path
 * @property {?string}  icon
 * @property {?string}  description
 * @property {?bool}    modal
 * @property {?int}     id
 * @property {?Object.<string, MenuItemObj>} subitems Key: Item name. Val: Item itself. 
 */