// import { MutationQueue, CustomMutationQueue } from "./MutationQueue"

// export class Diff {
// 	static persistentClasses = new Set<string>()
// 	static persistentAttributes = new Set<string>()

// 	// Reuse container for diffs to avoid memory allocation
// 	static container: HTMLElement
// 	static rootContainer: HTMLElement
// 	static mutations: CustomMutationQueue = new CustomMutationQueue()

// 	// Callbacks
// 	static onStart: () => void
// 	static onMutationsQueued: (elem: HTMLElement) => void

// 	// innerHTML will diff the element with the given HTML string and apply DOM mutations.
// 	static innerHTML(aRoot: HTMLElement, html: string): Promise<void> {
// 		return new Promise((resolve, reject) => {
// 			if(!Diff.container) {
// 				Diff.container = document.createElement("main")
// 			}

// 			Diff.container.innerHTML = html

// 			if(Diff.onStart) {
// 				Diff.onStart()
// 			}

// 			Diff.childNodes(aRoot, Diff.container)
// 			this.mutations.wait(resolve)
// 		})
// 	}

// 	// root will diff the document root element with the given HTML string and apply DOM mutations.
// 	static root(aRoot: HTMLElement, html: string) {
// 		return new Promise((resolve, reject) => {
// 			if(!Diff.rootContainer) {
// 				Diff.rootContainer = document.createElement("html")
// 			}

// 			Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")

// 			if(Diff.onStart) {
// 				Diff.onStart()
// 			}

// 			Diff.childNodes(aRoot.getElementsByTagName("body")[0], Diff.rootContainer.getElementsByTagName("body")[0])
// 			this.mutations.wait(resolve)
// 		})
// 	}

// 	// childNodes diffs the child nodes of 2 given elements and applies DOM mutations.
// 	static childNodes(aRoot: Node, bRoot: Node) {
// 		let aChild = [...aRoot.childNodes]
// 		let bChild = [...bRoot.childNodes]
// 		let numNodes = Math.max(aChild.length, bChild.length)

// 		for(let i = 0; i < numNodes; i++) {
// 			let a = aChild[i]

// 			// Remove nodes at the end of a that do not exist in b
// 			if(i >= bChild.length) {
// 				this.mutations.queue(() => aRoot.removeChild(a))
// 				continue
// 			}

// 			let b = bChild[i]

// 			// If a doesn't have that many nodes, simply append at the end of a
// 			if(i >= aChild.length) {
// 				this.mutations.queue(() => aRoot.appendChild(b))

// 				if(Diff.onMutationsQueued && b.nodeType === Node.ELEMENT_NODE) {
// 					Diff.onMutationsQueued(b as HTMLElement)
// 				}

// 				continue
// 			}

// 			// If it's a completely different HTML tag or node type, replace it
// 			if(a.nodeName !== b.nodeName || a.nodeType !== b.nodeType) {
// 				this.mutations.queue(() => aRoot.replaceChild(b, a))

// 				if(Diff.onMutationsQueued && b.nodeType === Node.ELEMENT_NODE) {
// 					Diff.onMutationsQueued(b as HTMLElement)
// 				}

// 				continue
// 			}

// 			// Text node:
// 			// We don't need to check for b to be a text node as well because
// 			// we eliminated different node types in the previous condition.
// 			if(a.nodeType === Node.TEXT_NODE) {
// 				this.mutations.queue(() => a.textContent = b.textContent)
// 				continue
// 			}

// 			// HTML element:
// 			if(a.nodeType === Node.ELEMENT_NODE) {
// 				let elemA = a as HTMLElement
// 				let elemB = b as HTMLElement

// 				let removeAttributes: Attr[] = []

// 				for(let x = 0; x < elemA.attributes.length; x++) {
// 					let attrib = elemA.attributes[x]

// 					if(attrib.specified) {
// 						if(!elemB.hasAttribute(attrib.name) && !Diff.persistentAttributes.has(attrib.name)) {
// 							removeAttributes.push(attrib)
// 						}
// 					}
// 				}

// 				this.mutations.queue(() => {
// 					for(let attr of removeAttributes) {
// 						elemA.removeAttributeNode(attr)
// 					}
// 				})

// 				for(let x = 0; x < elemB.attributes.length; x++) {
// 					let attrib = elemB.attributes[x]

// 					if(!attrib.specified) {
// 						continue
// 					}

// 					// If the attribute value is exactly the same, skip this attribute.
// 					if(elemA.getAttribute(attrib.name) === attrib.value) {
// 						continue
// 					}

// 					if(attrib.name === "class") {
// 						let classesA = elemA.classList
// 						let classesB = elemB.classList
// 						let removeClasses: string[] = []

// 						for(let className of classesA) {
// 							if(!classesB.contains(className) && !Diff.persistentClasses.has(className)) {
// 								removeClasses.push(className)
// 							}
// 						}

// 						this.mutations.queue(() => {
// 							for(let className of removeClasses) {
// 								classesA.remove(className)
// 							}

// 							for(let className of classesB) {
// 								if(!classesA.contains(className)) {
// 									classesA.add(className)
// 								}
// 							}
// 						})

// 						continue
// 					}

// 					this.mutations.queue(() => elemA.setAttribute(attrib.name, attrib.value))
// 				}

// 				// Special case: Apply state of input elements
// 				if(elemA !== document.activeElement && elemA instanceof HTMLInputElement && elemB instanceof HTMLInputElement) {
// 					this.mutations.queue(() => {
// 						(elemA as HTMLInputElement).value = (elemB as HTMLInputElement).value
// 					})
// 				}
// 			}

// 			Diff.childNodes(a, b)

// 			if(Diff.onMutationsQueued && a.nodeType === Node.ELEMENT_NODE) {
// 				Diff.onMutationsQueued(a as HTMLElement)
// 			}
// 		}
// 	}
// }