# imba$stdlib=1
import {Text,createComment,createTextNode,Comment,Node} from './core'
import {RenderContext,createRenderContext} from './context'
import {Flags} from './flags'

export def use_slots
	yes

export class Fragment

	def constructor
		childNodes = []

	def log ...params
		return

	def hasChildNodes
		false

	set #parent value
		##parent = value

	get #parent
		##parent or ##up

	get #closestNode\ParentNode
		#parent.#closestNode

	get #isRichElement
		yes

	get flags
		##flags ||= new Flags(self)

	def flagSync$
		self

	def #afterVisit
		self

	def #getRenderContext sym
		createRenderContext(self,sym)

let counter = 0
# like a list
class VirtualFragment < Fragment
	def constructor flags, parent
		super
		##up = parent
		parentNode = null
		#domFlags = flags
		childNodes = []
		#end = createComment('slot' + counter++)

		if parent
			parent.#appendChild(self)

	get #parent
		##parent or parentNode or ##up

	set textContent text
		#textContent = text

	get textContent
		#textContent

	def hasChildNodes
		for item in childNodes
			if item isa Fragment
				return true if item.hasChildNodes!
			if item isa Comment
				yes
			elif item isa Node
				return true

			# unless item isa Comment
			#	return false
		return false
		# #children.length == 0

	def text$ item
		unless #textNode
			#textNode = #placeChild(item)
		else
			#textNode.textContent = item
		return #textNode

	def appendChild child
		if parentNode
			child.#insertInto(parentNode,#end)
		childNodes.push(child)

	def #appendChild child
		if parentNode
			child.#insertInto(parentNode,#end)
		else
			child.##up ??= (##up or self)
		childNodes.push(child)

	def insertBefore node,refnode
		# check if this should really happen?
		if parentNode
			parentNode.#insertChild(node,refnode)
		let idx = childNodes.indexOf(refnode)
		if idx >= 0
			childNodes.splice(idx,0,node)
		return node

	def #removeChild node
		if parentNode
			parentNode.#removeChild(node)
		let idx = childNodes.indexOf(node)
		if idx >= 0
			childNodes.splice(idx,1)
		return

	def #replaceChild newnode, oldnode
		if parentNode
			parentNode.#replaceChild(newnode,oldnode)
		let idx = childNodes.indexOf(oldnode) # what if text?
		childNodes[idx] = newnode
		# let res = #insertChild(newnode,oldnode)
		# #removeChild(oldnode)
		return newnode

	def #insertInto parent, before
		let prev = parentNode

		if parentNode =? parent
			# log '#insertInto',parent,prev,before,#end
			# what if before is a fragment etc?
			if #end
				before = #end.#insertInto(parent,before)
			# before = #end
			for item in childNodes
				item.#insertInto(parent,before)
		return self

	def #replaceWith node, parent
		# log '#replaceWith',node,parent
		# what if this
		# log 'replaced with',node,parent
		let res = node.#insertInto(parent,#end)
		#removeFrom(parent)
		res

	def #insertChild node,refnode
		if parentNode
			insertBefore(node,refnode or #end)

		if refnode
			let idx = childNodes.indexOf(refnode)
			if idx >= 0
				childNodes.splice(idx,0,node)
		else
			childNodes.push(node)
		return node

		# for item in #children
		# 	item.#removeFrom(parent)

	def #removeFrom parent
		for item in childNodes
			# log '#removeFrom',item,parent
			item.#removeFrom(parent)
		#end.#removeFrom(parent) if #end
		parentNode = null
		self

	def #placeChild item, f, prev
		let par = parentNode
		let type = typeof item

		if type === 'undefined' or item === null
			if prev and prev isa Comment # check perf
				return prev

			let el = createComment('')

			if prev
				let idx = childNodes.indexOf(prev)
				childNodes.splice(idx,1,el)
				if par
					prev.#replaceWith(el,par)
				# parentNode.#insert(item,f,prev or #end)
				return el

			childNodes.push(el)
			el.#insertInto(par,#end) if par
			return el
			# return prev ? prev.#replaceWith(el,self) : el.#insertInto(this,null)

		if item === prev
			return item

		if type !== 'object'
			let res
			let txt = item

			if prev
				if prev isa Text # check perf
					prev.textContent = txt
					return prev
				else
					res = createTextNode(txt)
					let idx = childNodes.indexOf(prev)
					childNodes.splice(idx,1,res)
					prev.#replaceWith(res,par) if par
					return res
			else
				childNodes.push(res = createTextNode(txt))
				# self.appendChild$(res = createTextNode(txt))
				res.#insertInto(par,#end) if par
				return res

		elif prev
			let idx = childNodes.indexOf(prev)
			childNodes.splice(idx,1,item)
			prev.#replaceWith(item,par) if par
			return item
		else
			childNodes.push(item)
			item.#insertInto(par,#end) if par
			return item

export def createLiveFragment bitflags, par
	const el = new VirtualFragment(bitflags, par)
	return el

export def createSlot bitflags, par
	const el = new VirtualFragment(bitflags, null)
	el.##up = par
	# el.setup$(bitflags, options)
	# el.##up = par if par
	return el

extend class Node
	def #registerFunctionalSlot name
		let map = #functionalSlots ||= {}
		map[name] ||= createSlot(0,self)

	def #getFunctionalSlot name, context
		let map = #functionalSlots
		return map and map[name] or #getSlot(name,context)

	def #getSlot name, context
		if (name == '__' and !self.render) or !__slots
			return self
		return __slots[name] ||= createSlot(0,self)
