/**
 * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
 * Node module: @schukai/monster
 *
 * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
 * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
 *
 * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
 * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
 * For more information about purchasing a commercial license, please contact Volker Schukai.
 *
 * SPDX-License-Identifier: AGPL-3.0
 */
//
// import { buildTree } from "../../data/buildtree.mjs";
// import { Datasource } from "../../data/datasource.mjs";
// import { addAttributeToken } from "../../dom/attributes.mjs";
// import {
// 	ATTRIBUTE_DISABLED,
// 	ATTRIBUTE_ERRORMESSAGE,
// 	ATTRIBUTE_ROLE,
// 	ATTRIBUTE_UPDATER_INSERT_REFERENCE,
// } from "../../dom/constants.mjs";
// import {
// 	assembleMethodSymbol,
// 	CustomElement,
// 	initMethodSymbol,
// 	registerCustomElement,
// } from "../../dom/customelement.mjs";
// import { findTargetElementFromEvent } from "../../dom/events.mjs";
// import { findElementWithSelectorUpwards } from "../../dom/util.mjs";
// import { Formatter } from "../../text/formatter.mjs";
// import { isObject, isString } from "../../types/is.mjs";
// import { Node } from "../../types/node.mjs";
// import { NodeRecursiveIterator } from "../../types/noderecursiveiterator.mjs";
// import { Observer } from "../../types/observer.mjs";
// import { ProxyObserver } from "../../types/proxyobserver.mjs";
// import { validateInstance } from "../../types/validate.mjs";
// import {
// 	datasourceLinkedElementSymbol,
// 	handleDataSourceChanges,
// } from "../datatable/util.mjs";
// import { ATTRIBUTE_INTEND } from "./../constants.mjs";
// import { CommonStyleSheet } from "../stylesheet/common.mjs";
// import { TreeMenuStyleSheet } from "./stylesheet/tree-menu.mjs";
//
// export { TreeMenu };
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const internalNodesSymbol = Symbol("internalNodes");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const controlElementSymbol = Symbol("controlElement");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const openEntryEventHandlerSymbol = Symbol("openEntryEventHandler");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const dragstartEventHandlerSymbol = Symbol("dragstartEventHandler");
// /**
//  * @private
//  * @type {symbol}
//  */
// const dragenterEventHandlerSymbol = Symbol("dragenterEventHandler");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const dragleaveEventHandlerSymbol = Symbol("dragleaveEventHandler");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const dragEventHandlerSymbol = Symbol("dragEventHandler");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const dragoverEventHandlerSymbol = Symbol("dragoverEventHandler");
//
// /**
//  * @private
//  * @type {symbol}
//  */
// const dropEventHandlerSymbol = Symbol("dropEventHandlerSymbol");
//
// /**
//  * TreeMenu
//  *
//  * <img src="./images/tree-menu.png">
//  *
//  * You can create this control either by specifying the HTML tag `<monster-tree-menu />` directly in the HTML
//  *
//  * ```html
//  * <monster-tree-menu></monster-tree-menu>
//  * ```
//  *
//  * or using Javascript via the `document.createElement('monster-tree-menu');` method.
//  *
//  * ```javascript
//  * import {TreeMenu} from 'https://cdn.jsdelivr.net/npm/@schukai/component-treemenu@0.1.0/dist/modules/treemenu.js';
//  * document.createElement('monster-treemenu');
//  * ```
//  *
//  * @startuml tree-menu.png
//  * skinparam monochrome true
//  * skinparam shadowing false
//  * HTMLElement <|-- CustomElement
//  * CustomElement <|-- CustomControl
//  * CustomControl <|-- TreeMenu
//  * @enduml
//  * @since 1.0.0
//  * @copyright Volker Schukai
//  * @memberOf Monster.Components.TreeMenu
//  * @summary A TreeMenu control
//  * @fires monster-fetched
//  */
// class TreeMenu extends CustomElement {
// 	/**
// 	 * This method is called internal and should not be called directly.
// 	 *
// 	 * The defaults can be set either directly in the object or via an attribute in the HTML tag.
// 	 * The value of the attribute `data-monster-options` in the HTML tag must be a JSON string.
// 	 *
// 	 * ```
// 	 * <monster-treemenu data-monster-options="{}"></monster-treemenu>
// 	 * ```
// 	 *
// 	 * Since 1.18.0 the JSON can be specified as a DataURI.
// 	 *
// 	 * ```
// 	 * new Monster.Types.DataUrl(btoa(JSON.stringify({
// 	 *        shadowMode: 'open',
// 	 *    })),'application/json',true).toString()
// 	 * ```
// 	 * @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown
// 	 * @property {Object} templates Template definitions
// 	 * @property {string} templates.main Main template
// 	 * @property {Datasource} datasource data source
// 	 * @property {Object} mapping
// 	 * @property {String} mapping.selector=* Path to select the appropriate entries
// 	 * @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key
// 	 * @property {String} mapping.keyTemplate="" template with the key placeholders in the form ${name}, where name is the key
// 	 * @property {String} mapping.rootReferences=['0', undefined, null]
// 	 * @property {String} mapping.idTemplate=id
// 	 * @property {String} mapping.parentTemplate=parent
// 	 * @property {String} mapping.selection
// 	 */
// 	get defaults() {
// 		return Object.assign(
// 			{},
// 			super.defaults,
// 			{
// 				toggleEventType: ["click", "touch"],
// 				mapping: {
// 					rootReferences: ["0", undefined, null],
// 					idTemplate: "id",
// 					parentTemplate: "parent",
// 					selector: "*",
// 					labelTemplate: "",
// 					valueTemplate: "",
// 					filter: undefined,
// 				},
// 				templates: {
// 					main: getTemplate(),
// 				},
//
// 				datasource: {
// 					selector: null,
// 				},
//
// 				data: [],
// 			},
// 			initOptionsFromArguments.call(this),
// 		);
// 	}
//
// 	/**
// 	 * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
// 	 *
// 	 * @return {string[]}
// 	 * @since 1.15.0
// 	 */
// 	static get observedAttributes() {
// 		const list = super.observedAttributes;
// 		//list.push(ATTRIBUTE_FORM_URL);
// 		return list;
// 	}
//
// 	/**
// 	 *
// 	 */
// 	[initMethodSymbol]() {
// 		super[initMethodSymbol]();
// 	}
//
// 	/**
// 	 *
// 	 * @return {Monster.Components.TreeMenu.Form}
// 	 */
// 	[assembleMethodSymbol]() {
// 		super[assembleMethodSymbol]();
//
// 		initControlReferences.call(this);
// 		initEventHandler.call(this);
// 		// importEntriesFromDatasource.call(this);
// 		initObserver.call(this);
//
// 		return this;
// 	}
//
// 	/**
// 	 * This method is called internal and should not be called directly.
// 	 *
// 	 * @return {CSSStyleSheet[]}
// 	 */
// 	static getCSSStyleSheet() {
// 		return [CommonStyleSheet, TreeMenuStyleSheet];
// 	}
//
// 	/**
// 	 * This method is called internal and should not be called directly.
// 	 *
// 	 * @return {string}
// 	 */
// 	static getTag() {
// 		return "monster-tree-menu";
// 	}
// }
//
// /**
//  * @private
//  */
// function initEventHandler() {
// 	switchToConfig.call(this);
//
// 	const selector = this.getOption("datasource.selector");
//
// 	if (isString(selector)) {
// 		const element = findElementWithSelectorUpwards(this, selector);
// 		if (element === null) {
// 			throw new Error("the selector must match exactly one element");
// 		}
//
// 		if (!(element instanceof Datasource)) {
// 			throw new TypeError("the element must be a datasource");
// 		}
//
// 		this[datasourceLinkedElementSymbol] = element;
// 		element.datasource.attachObserver(
// 			new Observer(handleDataSourceChanges.bind(this)),
// 		);
// 	}
//
// 	this[openEntryEventHandlerSymbol] = (event) => {
// 		const container = findTargetElementFromEvent(
// 			event,
// 			ATTRIBUTE_ROLE,
// 			"entry",
// 		);
// 		if (!(container instanceof HTMLElement)) {
// 			return;
// 		}
//
// 		//let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option');
// 		const index = container
// 			.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE)
// 			.split("-")
// 			.pop();
//
// 		const currentState = this.getOption("data." + index + ".state");
//
// 		const newState = currentState === "close" ? "open" : "close";
// 		this.setOption("data." + index + ".state", newState);
//
// 		const newVisibility = newState === "open" ? "visible" : "hidden";
//
// 		if (container.hasAttribute(ATTRIBUTE_INTEND)) {
// 			const intend = container.getAttribute(ATTRIBUTE_INTEND);
//
// 			let ref = container.nextElementSibling;
// 			const childIntend = parseInt(intend) + 1;
//
// 			const cmp = (a, b) => {
// 				if (newState === "open") {
// 					return a === b;
// 				}
//
// 				return a >= b;
// 			};
//
// 			while (
// 				ref &&
// 				ref.hasAttribute(ATTRIBUTE_INTEND) &&
// 				cmp(parseInt(ref.getAttribute(ATTRIBUTE_INTEND)), childIntend)
// 			) {
// 				const refIndex = ref
// 					.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE)
// 					.split("-")
// 					.pop();
// 				this.setOption("data." + refIndex + ".visibility", newVisibility);
//
// 				if (newState === "close") {
// 					this.setOption("data." + refIndex + ".state", "close");
// 				}
//
// 				ref = ref.nextElementSibling;
// 			}
// 		}
// 	};
//
// 	const types = this.getOption("toggleEventType", ["click"]);
// 	for (const [, type] of Object.entries(types)) {
// 		this.shadowRoot.addEventListener(type, this[openEntryEventHandlerSymbol]);
// 	}
//
// 	// for (const [, type] of Object.entries(types)) {
// 	//
// 	//     self[controlElementSymbol].addEventListener(type, function (event) {
// 	//
// 	//         const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
// 	//         if (!(element instanceof HTMLElement)) {
// 	//             return;
// 	//         }
// 	//
// 	//         toggle.call(self);
// 	//
// 	//
// 	//     })
// 	//
// 	// }
//
// 	return this;
// }
//
// /**
//  * @private
//  * @this Form
//  */
// function initObserver() {}
//
// /**
//  * Import Menu Entries from dataset
//  *
//  * @since 1.0.0
//  * @param {array|object|Map|Set} data
//  * @return {TreeMenu}
//  * @throws {Error} map is not iterable
//  * @private
//  */
// function importEntries(data) {
// 	this[internalNodesSymbol] = new Map();
//
// 	const mappingOptions = this.getOption("mapping", {});
//
// 	const filter = mappingOptions?.["filter"];
// 	const rootReferences = mappingOptions?.["rootReferences"];
//
// 	const id = this.getOption("mapping.idTemplate", "id");
// 	const parentID = this.getOption("mapping.parentTemplate", "parent");
//
// 	const selector = mappingOptions?.["selector"];
//
// 	const nodes = buildTree(data, selector, id, parentID, {
// 		filter,
// 		rootReferences,
// 	});
//
// 	const options = [];
// 	for (const node of nodes) {
// 		const iterator = new NodeRecursiveIterator(node);
// 		for (const n of iterator) {
// 			const formattedValues = formatKeyLabel.call(this, n);
//
// 			const label = formattedValues.label;
// 			const value = formattedValues.value;
// 			const intend = n.level;
//
// 			const visibility = intend > 0 ? "hidden" : "visible";
// 			const state = "close";
//
// 			this[internalNodesSymbol].set(value, n);
//
// 			options.push({
// 				value,
// 				label,
// 				intend,
// 				state,
// 				visibility,
// 				["has-children"]: n.hasChildNodes(),
// 			});
// 		}
// 	}
//
// 	this.setOption("entries", options);
// 	return this;
// }
//
// /**
//  * @private
//  */
// function importEntriesFromDatasource() {
// 	const self = this;
// 	self.setAttribute(ATTRIBUTE_DISABLED, ATTRIBUTE_DISABLED);
//
// 	const datasource = self.getOption("datasource");
// 	if (!(datasource instanceof Datasource)) {
// 		addAttributeToken(
// 			self,
// 			ATTRIBUTE_ERRORMESSAGE,
// 			"datasource is not defined",
// 		);
// 		return;
// 	}
//
// 	datasource.attachObserver(
// 		new Observer(function () {
// 			if (isObject(this) && this instanceof ProxyObserver) {
// 				importEntries.call(self, datasource.get());
// 			}
// 		}),
// 	);
//
// 	datasource
// 		.read()
// 		.then(() => {
// 			new Processing(() => {
// 				self.removeAttribute(ATTRIBUTE_DISABLED);
// 			}).run();
// 		})
// 		.catch((e) => {
// 			addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
// 		});
//
// 	return self;
// }
//
// /**
//  *
//  * @param {Node} node
//  * @return {array<label, value>}
//  * @memberOf Monster.Components.TreeMenu
//  * @private
//  */
// function formatKeyLabel(node) {
// 	validateInstance(node, Node);
//
// 	const label = new Formatter(node.value).format(
// 		this.getOption("mapping.labelTemplate", ""),
// 	);
// 	const value = new Formatter(node.value).format(
// 		this.getOption("mapping.valueTemplate", ""),
// 	);
//
// 	return {
// 		value,
// 		label,
// 	};
// }
//
// /**
//  * @private
//  * @return {Monster.Components.TreeMenu.Form}
//  */
// function initControlReferences() {
// 	if (!this.shadowRoot) {
// 		throw new Error("no shadow-root is defined");
// 	}
//
// 	this[controlElementSymbol] = this.shadowRoot.querySelector(
// 		"[data-monster-role=control]",
// 	);
//
// 	return this;
// }
//
// /**
//  *
//  * ```
//  * <monster-tree-menu data-monster-url="https://example.com/"></monster-tree-menu>
//  * ```
//
//  * @private
//  * @return {object}
//  */
// function initOptionsFromArguments() {
// 	const options = {};
//
// 	// let url = self.getAttribute(ATTRIBUTE_FORM_URL);
// 	//
// 	// if (isString(url)) {
// 	//     options['url'] = new URL(url, document.location).toString()
// 	// }
//
// 	return options;
// }
//
// function switchToConfig() {
// 	if (!this.shadowRoot) {
// 		throw new Error("no shadow-root is defined");
// 	}
//
// 	this[dragoverEventHandlerSymbol] = (event) => {
// 		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry");
// 		event.preventDefault();
// 		if (!(element instanceof HTMLElement)) {
// 			return;
// 		}
//
// 		const dropzone = document.createElement("div");
// 		dropzone.classList.add("dropzone");
//
// 		element.prepend(dropzone);
//
// 		//console.log("over", element.outerHTML, event);
//
// 		event.dataTransfer.dropEffect = "move";
// 	};
//
// 	this[dragenterEventHandlerSymbol] = (event) => {
// 		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry");
// 		//console.log("enter", element.outerHTML, event);
//
// 		event.dataTransfer.dropEffect = "move";
// 		event.preventDefault();
// 	};
//
// 	this[dragleaveEventHandlerSymbol] = (event) => {
// 		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry");
//
// 		event.preventDefault();
// 		if (!(element instanceof HTMLElement)) {
// 			return;
// 		}
//
// 		//console.log("leave", element.outerHTML, event);
//
// 		event.dataTransfer.dropEffect = "move";
// 		event.preventDefault();
// 	};
//
// 	this[dragEventHandlerSymbol] = (event) => {
// 		event.preventDefault();
// 	};
//
// 	this[dropEventHandlerSymbol] = (event) => {
// 		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry");
// 		//console.log("drop", element.outerHTML, event);
// 		event.preventDefault();
// 	};
//
// 	this[dragstartEventHandlerSymbol] = (event) => {
// 		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry");
// 		if (!(element instanceof HTMLElement)) {
// 			return;
// 		}
//
// 		//let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option');
// 		const index = element
// 			.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE)
// 			.split("-")
// 			.pop();
//
// 		const currentState = this.getOption("entries." + index + ".state");
// 		event.dataTransfer.setData("text/plain", "22");
// 		event.dataTransfer.setData("text/html", "22");
// 		event.dataTransfer.effectAllowed = "move";
// 	};
//
// 	this[controlElementSymbol].addEventListener(
// 		"dragstart",
// 		this[dragstartEventHandlerSymbol],
// 	);
// 	this[controlElementSymbol].addEventListener(
// 		"dragenter",
// 		this[dragenterEventHandlerSymbol],
// 	);
// 	this[controlElementSymbol].addEventListener(
// 		"dragleave",
// 		this[dragleaveEventHandlerSymbol],
// 	);
// 	this[controlElementSymbol].addEventListener(
// 		"dragover",
// 		this[dragoverEventHandlerSymbol],
// 	);
// 	this[controlElementSymbol].addEventListener(
// 		"drop",
// 		this[dropEventHandlerSymbol],
// 	);
// }
//
// // /**
// //  * @private
// //  * @throws {Error} missing default slot
// //  * @throws {Error} no shadow-root is defined
// //  * @throws {Error} missing url
// //  * @throws {Error} we won't be able to read the data
// //  * @throws {Error} request failed
// //  * @throws {Error} not found
// //  * @throws {Error} undefined status or type
// //  * @fires monster-fetched
// //  */
// // function initIntersectionObserver() {
// //     const self = this;
// //
// //     if (self[intersectionObserverWasInitialized] === true) {
// //         return
// //     }
// //
// //     self[intersectionObserverWasInitialized] = true;
// //
// //     let options = {
// //         threshold: [0.5]
// //     }
// //
// //     const callback = (entries, observer) => {
// //
// //         for (const [, entry] of entries.entries()) {
// //             if (entry.isIntersecting === true) {
// //                 if (!self.hasAttribute(ATTRIBUTE_FORM_RELOAD) || self.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === 'onshow') {
// //                     observer.disconnect();
// //                 }
// //
// //                 try {
// //                     loadContent.call(self);
// //                 } catch (e) {
// //                     self.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString());
// //                 }
// //
// //
// //             }
// //         }
// //     }
// //
// //     const observer = new IntersectionObserver(callback, options);
// //     observer.observe(self);
// //
// //
// // }
//
// /**
//  * @private
//  * @return {string}
//  */
// function getTemplate() {
// 	// language=HTML
// 	return `
//         <template id="entries">
//             <div data-monster-role="entry"
//                  draggable="true"
//                  data-monster-attributes="
//                  data-monster-intend path:entries.intend,
//                  data-monster-state path:entries.state,
//                  data-monster-visibility path:entries.visibility,
//                  data-monster-filtered path:entries.filtered,
//                  data-monster-has-children path:entries.has-children">
//
//                 <button data-monster-role="button"
//                         data-monster-attributes="
//                                     type path:type,
//                                     role path:role,
//                                     value path:entries.value,
//                                     name path:name,
//                                     part path:type | prefix:option- | suffix: form" tabindex="-1">
//                     <span data-monster-role="folder-handler"></span>
//                     <span data-monster-replace="path:entries | index:label" part="entry-label"></span>
//                 </button>
//         </template>
//
//         <div data-monster-role="control" part="control">
//             <div part="entries" data-monster-role="entries"
//                  data-monster-insert="entries path:entries"
//                  tabindex="-1"></div>
//         </div>
//     `;
// }
//
// registerCustomElement(TreeMenu);
