import { entries, setPrototypeOf } from "../shared/object.js";
import { END, NEXT, UPGRADE } from "../shared/symbols.js";
import { shadowRoots } from "../shared/shadow-roots.js";
//#region node_modules/linkedom/esm/interface/custom-element-registry.js
var reactive = false;
var Classes = /* @__PURE__ */ new WeakMap();
var customElements = /* @__PURE__ */ new WeakMap();
var attributeChangedCallback = (element, attributeName, oldValue, newValue) => {
	if (reactive && customElements.has(element) && element.attributeChangedCallback && element.constructor.observedAttributes.includes(attributeName)) element.attributeChangedCallback(attributeName, oldValue, newValue);
};
var createTrigger = (method, isConnected) => (element) => {
	if (customElements.has(element)) {
		const info = customElements.get(element);
		if (info.connected !== isConnected && element.isConnected === isConnected) {
			info.connected = isConnected;
			if (method in element) element[method]();
		}
	}
};
var triggerConnected = createTrigger("connectedCallback", true);
var connectedCallback = (element) => {
	if (reactive) {
		triggerConnected(element);
		if (shadowRoots.has(element)) element = shadowRoots.get(element).shadowRoot;
		let { [NEXT]: next, [END]: end } = element;
		while (next !== end) {
			if (next.nodeType === 1) triggerConnected(next);
			next = next[NEXT];
		}
	}
};
var triggerDisconnected = createTrigger("disconnectedCallback", false);
var disconnectedCallback = (element) => {
	if (reactive) {
		triggerDisconnected(element);
		if (shadowRoots.has(element)) element = shadowRoots.get(element).shadowRoot;
		let { [NEXT]: next, [END]: end } = element;
		while (next !== end) {
			if (next.nodeType === 1) triggerDisconnected(next);
			next = next[NEXT];
		}
	}
};
/**
* @implements globalThis.CustomElementRegistry
*/
var CustomElementRegistry = class {
	/**
	* @param {Document} ownerDocument
	*/
	constructor(ownerDocument) {
		/**
		* @private
		*/
		this.ownerDocument = ownerDocument;
		/**
		* @private
		*/
		this.registry = /* @__PURE__ */ new Map();
		/**
		* @private
		*/
		this.waiting = /* @__PURE__ */ new Map();
		/**
		* @private
		*/
		this.active = false;
	}
	/**
	* @param {string} localName the custom element definition name
	* @param {Function} Class the custom element **Class** definition
	* @param {object?} options the optional object with an `extends` property
	*/
	define(localName, Class, options = {}) {
		const { ownerDocument, registry, waiting } = this;
		if (registry.has(localName)) throw new Error("unable to redefine " + localName);
		if (Classes.has(Class)) throw new Error("unable to redefine the same class: " + Class);
		this.active = reactive = true;
		const { extends: extend } = options;
		Classes.set(Class, {
			ownerDocument,
			options: { is: extend ? localName : "" },
			localName: extend || localName
		});
		const check = extend ? (element) => {
			return element.localName === extend && element.getAttribute("is") === localName;
		} : (element) => element.localName === localName;
		registry.set(localName, {
			Class,
			check
		});
		if (waiting.has(localName)) {
			for (const resolve of waiting.get(localName)) resolve(Class);
			waiting.delete(localName);
		}
		ownerDocument.querySelectorAll(extend ? `${extend}[is="${localName}"]` : localName).forEach(this.upgrade, this);
	}
	/**
	* @param {Element} element
	*/
	upgrade(element) {
		if (customElements.has(element)) return;
		const { ownerDocument, registry } = this;
		const ce = element.getAttribute("is") || element.localName;
		if (registry.has(ce)) {
			const { Class, check } = registry.get(ce);
			if (check(element)) {
				const { attributes, isConnected } = element;
				for (const attr of attributes) element.removeAttributeNode(attr);
				const values = entries(element);
				for (const [key] of values) delete element[key];
				setPrototypeOf(element, Class.prototype);
				ownerDocument[UPGRADE] = {
					element,
					values
				};
				new Class(ownerDocument, ce);
				customElements.set(element, { connected: isConnected });
				for (const attr of attributes) element.setAttributeNode(attr);
				if (isConnected && element.connectedCallback) element.connectedCallback();
			}
		}
	}
	/**
	* @param {string} localName the custom element definition name
	*/
	whenDefined(localName) {
		const { registry, waiting } = this;
		return new Promise((resolve) => {
			if (registry.has(localName)) resolve(registry.get(localName).Class);
			else {
				if (!waiting.has(localName)) waiting.set(localName, []);
				waiting.get(localName).push(resolve);
			}
		});
	}
	/**
	* @param {string} localName the custom element definition name
	* @returns {Function?} the custom element **Class**, if any
	*/
	get(localName) {
		const info = this.registry.get(localName);
		return info && info.Class;
	}
	/**
	* @param {Function} Class **Class** of custom element
	* @returns {string?} found tag name or null
	*/
	getName(Class) {
		if (Classes.has(Class)) {
			const { localName } = Classes.get(Class);
			return localName;
		}
		return null;
	}
};
//#endregion
export { Classes, CustomElementRegistry, attributeChangedCallback, connectedCallback, customElements, disconnectedCallback };
