local function b(b)local S local G local g local f local function r(b,S)local g=b.hostParent local r=b.hostKey local d=b.depth local a=b.originalContext or b.context local K=b.parentLegacyContext f(b)local q=G(S,g,r,a,K)if q~=nil then q.depth=d end return q end local function d(b,S,f)if config.internalTypeChecks then internalAssert(Type.of(b)==Type.VirtualNode,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \086\105\114\116\117\097\108\078\111\100\101")end local r={}for S,G in pairs(b.children)do local d=ElementUtils.getElementByKey(f,S)local a=g(G,d)if a~=nil then b.children[S]=a else r[S]=true end end for S in pairs(r)do b.children[S]=nil end for g,f in ElementUtils.iterateElements(f)do local r=g if g==ElementUtils.UseParentKey then r=b.hostKey end if b.children[g]==nil then local d=G(f,S,r,b.context,b.legacyContext)if d~=nil then d.depth=b.depth+1 b.children[g]=d end end end end local function a(b,S,G)d(b,S,G)end local function K(b,S,G)if Type.of(G)==Type.Element or G==nil or typeof(G)=="\098\111\111\108\101\097\110"then d(b,S,G)else error(("\037\115\n\037\115"):format("\067\111\109\112\111\110\101\110\116 \114\101\116\117\114\110\101\100 \105\110\118\097\108\105\100 \099\104\105\108\100\114\101\110\058",b.currentElement.source or"\060\101\110\097\098\108\101 \101\108\101\109\101\110\116 \116\114\097\099\101\098\097\099\107\115\062"),0)end end function f(G)if config.internalTypeChecks then internalAssert(Type.of(G)==Type.VirtualNode,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \086\105\114\116\117\097\108\078\111\100\101")end local g=ElementKind.of(G.currentElement)if g==ElementKind.Host then b.unmountHostNode(S,G)elseif g==ElementKind.Function then for b,S in pairs(G.children)do f(S)end elseif g==ElementKind.Stateful then G.instance:__unmount()elseif g==ElementKind.Portal then for b,S in pairs(G.children)do f(S)end elseif g==ElementKind.Fragment then for b,S in pairs(G.children)do f(S)end else error(("\085\110\107\110\111\119\110 \069\108\101\109\101\110\116\075\105\110\100 \037\113"):format(tostring(g),2))end end local function q(b,S)local G=S.component(S.props)K(b,b.hostParent,G)return b end local function s(S,G)local g=S.currentElement local f=g.props.target local d=G.props.target assert(b.isHostObject(d),"\069\120\112\101\099\116\101\100 \116\097\114\103\101\116 \116\111 \098\101 \104\111\115\116 \111\098\106\101\099\116")if d~=f then return r(S,G)end local K=G.props[Children]a(S,d,K)return S end local function w(b,S)a(b,b.hostParent,S.elements)return b end function g(G,g,d)if config.internalTypeChecks then internalAssert(Type.of(G)==Type.VirtualNode,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \086\105\114\116\117\097\108\078\111\100\101")end if config.typeChecks then assert(Type.of(g)==Type.Element or typeof(g)=="\098\111\111\108\101\097\110"or g==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\050 \116\111 \098\101 \111\102 \116\121\112\101 \069\108\101\109\101\110\116\044 \098\111\111\108\101\097\110\044 \111\114 \110\105\108")end if G.currentElement==g and d==nil then return G end if typeof(g)=="\098\111\111\108\101\097\110"or g==nil then f(G)return nil end if G.currentElement.component~=g.component then return r(G,g)end local a=ElementKind.of(g)local K=true if a==ElementKind.Host then G=b.updateHostNode(S,G,g)elseif a==ElementKind.Function then G=q(G,g)elseif a==ElementKind.Stateful then K=G.instance:__update(g,d)elseif a==ElementKind.Portal then G=s(G,g)elseif a==ElementKind.Fragment then G=w(G,g)else error(("\085\110\107\110\111\119\110 \069\108\101\109\101\110\116\075\105\110\100 \037\113"):format(tostring(a),2))end if not K then return G end G.currentElement=g return G end local function Q(S,G,g,f,r)if config.internalTypeChecks then internalAssert(b.isHostObject(G)or G==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\050 \116\111 \098\101 \097 \104\111\115\116 \111\098\106\101\099\116")internalAssert(typeof(f)=="\116\097\098\108\101"or f==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\052 \116\111 \098\101 \111\102 \116\121\112\101 \116\097\098\108\101 \111\114 \110\105\108")internalAssert(typeof(r)=="\116\097\098\108\101"or r==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\053 \116\111 \098\101 \111\102 \116\121\112\101 \116\097\098\108\101 \111\114 \110\105\108")end if config.typeChecks then assert(g~=nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\051 \116\111 \098\101 \110\111\110-\110\105\108")assert(Type.of(S)==Type.Element or typeof(S)=="\098\111\111\108\101\097\110","\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \069\108\101\109\101\110\116 \111\114 \098\111\111\108\101\097\110")end return{[Type]=Type.VirtualNode;currentElement=S,depth=1;children={},hostParent=G;hostKey=g,legacyContext=r,parentLegacyContext=r;context=f or{};originalContext=nil}end local function u(b)local S=b.currentElement local G=S.component(S.props)K(b,b.hostParent,G)end local function v(S)local G=S.currentElement local g=G.props.target local f=G.props[Children]assert(b.isHostObject(g),"\069\120\112\101\099\116\101\100 \116\097\114\103\101\116 \116\111 \098\101 \104\111\115\116 \111\098\106\101\099\116")a(S,g,f)end local function Y(b)local S=b.currentElement local G=S.elements a(b,b.hostParent,G)end function G(G,g,f,r,d)if config.internalTypeChecks then internalAssert(b.isHostObject(g)or g==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\050 \116\111 \098\101 \097 \104\111\115\116 \111\098\106\101\099\116")internalAssert(typeof(d)=="\116\097\098\108\101"or d==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\053 \116\111 \098\101 \111\102 \116\121\112\101 \116\097\098\108\101 \111\114 \110\105\108")end if config.typeChecks then assert(f~=nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\051 \116\111 \098\101 \110\111\110-\110\105\108")assert(Type.of(G)==Type.Element or typeof(G)=="\098\111\111\108\101\097\110","\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \069\108\101\109\101\110\116 \111\114 \098\111\111\108\101\097\110")end if typeof(G)=="\098\111\111\108\101\097\110"then return nil end local a=ElementKind.of(G)local K=Q(G,g,f,r,d)if a==ElementKind.Host then b.mountHostNode(S,K)elseif a==ElementKind.Function then u(K)elseif a==ElementKind.Stateful then G.component:__mount(S,K)elseif a==ElementKind.Portal then v(K)elseif a==ElementKind.Fragment then Y(K)else error(("\085\110\107\110\111\119\110 \069\108\101\109\101\110\116\075\105\110\100 \037\113"):format(tostring(a),2))end return K end local function D(S,g,f)if config.typeChecks then assert(Type.of(S)==Type.Element,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \111\102 \116\121\112\101 \069\108\101\109\101\110\116")assert(b.isHostObject(g)or g==nil,"\069\120\112\101\099\116\101\100 \097\114\103 \035\050 \116\111 \098\101 \097 \104\111\115\116 \111\098\106\101\099\116")end if f==nil then f="\082\111\097\099\116\084\114\101\101"end local r={[Type]=Type.VirtualTree,[InternalData]={rootNode=nil,mounted=true}}r[InternalData].rootNode=G(S,g,f)return r end local function A(b)local S=b[InternalData]if config.typeChecks then assert(Type.of(b)==Type.VirtualTree,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \097 \082\111\097\099\116 \104\097\110\100\108\101")assert(S.mounted,"\067\097\110\110\111\116 \117\110\109\111\117\110\116\101\100 \097 \082\111\097\099\116 \116\114\101\101 \116\104\097\116 \104\097\115 \097\108\114\101\097\100\121 \098\101\101\110 \117\110\109\111\117\110\116\101\100")end S.mounted=false if S.rootNode~=nil then f(S.rootNode)end end local function i(b,S)local G=b[InternalData]if config.typeChecks then assert(Type.of(b)==Type.VirtualTree,"\069\120\112\101\099\116\101\100 \097\114\103 \035\049 \116\111 \098\101 \097 \082\111\097\099\116 \104\097\110\100\108\101")assert(Type.of(S)==Type.Element,"\069\120\112\101\099\116\101\100 \097\114\103 \035\050 \116\111 \098\101 \097 \082\111\097\099\116 \069\108\101\109\101\110\116")end G.rootNode=g(G.rootNode,S)return b end S={mountVirtualTree=D,unmountVirtualTree=A,updateVirtualTree=i;createVirtualNode=Q,mountVirtualNode=G,unmountVirtualNode=f;updateVirtualNode=g;updateVirtualNodeWithChildren=a,updateVirtualNodeWithRenderResult=K}return S end return b

local function createReconciler(renderer)
	local reconciler
	local mountVirtualNode
	local updateVirtualNode
	local unmountVirtualNode

	--[[
		Unmount the given virtualNode, replacing it with a new node described by
		the given element.

		Preserves host properties, depth, and legacyContext from parent.
	]]
	local function replaceVirtualNode(virtualNode, newElement)
		local hostParent = virtualNode.hostParent
		local hostKey = virtualNode.hostKey
		local depth = virtualNode.depth

		-- If the node that is being replaced has modified context, we need to
		-- use the original *unmodified* context for the new node
		-- The `originalContext` field will be nil if the context was unchanged
		local context = virtualNode.originalContext or virtualNode.context
		local parentLegacyContext = virtualNode.parentLegacyContext

		unmountVirtualNode(virtualNode)
		local newNode = mountVirtualNode(newElement, hostParent, hostKey, context, parentLegacyContext)

		-- mountVirtualNode can return nil if the element is a boolean
		if newNode ~= nil then
			newNode.depth = depth
		end

		return newNode
	end

	--[[
		Utility to update the children of a virtual node based on zero or more
		updated children given as elements.
	]]
	local function updateChildren(virtualNode, hostParent, newChildElements)
		if config.internalTypeChecks then
			internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode")
		end

		local removeKeys = {}

		-- Changed or removed children
		for childKey, childNode in pairs(virtualNode.children) do
			local newElement = ElementUtils.getElementByKey(newChildElements, childKey)
			local newNode = updateVirtualNode(childNode, newElement)

			if newNode ~= nil then
				virtualNode.children[childKey] = newNode
			else
				removeKeys[childKey] = true
			end
		end

		for childKey in pairs(removeKeys) do
			virtualNode.children[childKey] = nil
		end

		-- Added children
		for childKey, newElement in ElementUtils.iterateElements(newChildElements) do
			local concreteKey = childKey
			if childKey == ElementUtils.UseParentKey then
				concreteKey = virtualNode.hostKey
			end

			if virtualNode.children[childKey] == nil then
				local childNode = mountVirtualNode(
					newElement,
					hostParent,
					concreteKey,
					virtualNode.context,
					virtualNode.legacyContext
				)

				-- mountVirtualNode can return nil if the element is a boolean
				if childNode ~= nil then
					childNode.depth = virtualNode.depth + 1
					virtualNode.children[childKey] = childNode
				end
			end
		end
	end

	local function updateVirtualNodeWithChildren(virtualNode, hostParent, newChildElements)
		updateChildren(virtualNode, hostParent, newChildElements)
	end

	local function updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult)
		if Type.of(renderResult) == Type.Element
			or renderResult == nil
			or typeof(renderResult) == "boolean"
		then
			updateChildren(virtualNode, hostParent, renderResult)
		else
			error(("%s\n%s"):format(
				"Component returned invalid children:",
				virtualNode.currentElement.source or "<enable element tracebacks>"
			), 0)
		end
	end

	--[[
		Unmounts the given virtual node and releases any held resources.
	]]
	function unmountVirtualNode(virtualNode)
		if config.internalTypeChecks then
			internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode")
		end

		local kind = ElementKind.of(virtualNode.currentElement)

		if kind == ElementKind.Host then
			renderer.unmountHostNode(reconciler, virtualNode)
		elseif kind == ElementKind.Function then
			for _, childNode in pairs(virtualNode.children) do
				unmountVirtualNode(childNode)
			end
		elseif kind == ElementKind.Stateful then
			virtualNode.instance:__unmount()
		elseif kind == ElementKind.Portal then
			for _, childNode in pairs(virtualNode.children) do
				unmountVirtualNode(childNode)
			end
		elseif kind == ElementKind.Fragment then
			for _, childNode in pairs(virtualNode.children) do
				unmountVirtualNode(childNode)
			end
		else
			error(("Unknown ElementKind %q"):format(tostring(kind), 2))
		end
	end

	local function updateFunctionVirtualNode(virtualNode, newElement)
		local children = newElement.component(newElement.props)

		updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children)

		return virtualNode
	end

	local function updatePortalVirtualNode(virtualNode, newElement)
		local oldElement = virtualNode.currentElement
		local oldTargetHostParent = oldElement.props.target

		local targetHostParent = newElement.props.target

		assert(renderer.isHostObject(targetHostParent), "Expected target to be host object")

		if targetHostParent ~= oldTargetHostParent then
			return replaceVirtualNode(virtualNode, newElement)
		end

		local children = newElement.props[Children]

		updateVirtualNodeWithChildren(virtualNode, targetHostParent, children)

		return virtualNode
	end

	local function updateFragmentVirtualNode(virtualNode, newElement)
		updateVirtualNodeWithChildren(virtualNode, virtualNode.hostParent, newElement.elements)

		return virtualNode
	end

	--[[
		Update the given virtual node using a new element describing what it
		should transform into.

		`updateVirtualNode` will return a new virtual node that should replace
		the passed in virtual node. This is because a virtual node can be
		updated with an element referencing a different component!

		In that case, `updateVirtualNode` will unmount the input virtual node,
		mount a new virtual node, and return it in this case, while also issuing
		a warning to the user.
	]]
	function updateVirtualNode(virtualNode, newElement, newState)
		if config.internalTypeChecks then
			internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode")
		end
		if config.typeChecks then
			assert(
				Type.of(newElement) == Type.Element or typeof(newElement) == "boolean" or newElement == nil,
				"Expected arg #2 to be of type Element, boolean, or nil"
			)
		end

		-- If nothing changed, we can skip this update
		if virtualNode.currentElement == newElement and newState == nil then
			return virtualNode
		end

		if typeof(newElement) == "boolean" or newElement == nil then
			unmountVirtualNode(virtualNode)
			return nil
		end

		if virtualNode.currentElement.component ~= newElement.component then
			return replaceVirtualNode(virtualNode, newElement)
		end

		local kind = ElementKind.of(newElement)

		local shouldContinueUpdate = true

		if kind == ElementKind.Host then
			virtualNode = renderer.updateHostNode(reconciler, virtualNode, newElement)
		elseif kind == ElementKind.Function then
			virtualNode = updateFunctionVirtualNode(virtualNode, newElement)
		elseif kind == ElementKind.Stateful then
			shouldContinueUpdate = virtualNode.instance:__update(newElement, newState)
		elseif kind == ElementKind.Portal then
			virtualNode = updatePortalVirtualNode(virtualNode, newElement)
		elseif kind == ElementKind.Fragment then
			virtualNode = updateFragmentVirtualNode(virtualNode, newElement)
		else
			error(("Unknown ElementKind %q"):format(tostring(kind), 2))
		end

		-- Stateful components can abort updates via shouldUpdate. If that
		-- happens, we should stop doing stuff at this point.
		if not shouldContinueUpdate then
			return virtualNode
		end

		virtualNode.currentElement = newElement

		return virtualNode
	end

	--[[
		Constructs a new virtual node but not does mount it.
	]]
	local function createVirtualNode(element, hostParent, hostKey, context, legacyContext)
		if config.internalTypeChecks then
			internalAssert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object")
			internalAssert(typeof(context) == "table" or context == nil, "Expected arg #4 to be of type table or nil")
			internalAssert(
				typeof(legacyContext) == "table" or legacyContext == nil,
				"Expected arg #5 to be of type table or nil"
			)
		end
		if config.typeChecks then
			assert(hostKey ~= nil, "Expected arg #3 to be non-nil")
			assert(
				Type.of(element) == Type.Element or typeof(element) == "boolean",
				"Expected arg #1 to be of type Element or boolean"
			)
		end

		return {
			[Type] = Type.VirtualNode,
			currentElement = element,
			depth = 1,
			children = {},
			hostParent = hostParent,
			hostKey = hostKey,

			-- Legacy Context API
			-- A table of context values inherited from the parent node
			legacyContext = legacyContext,

			-- A saved copy of the parent context, used when replacing a node
			parentLegacyContext = legacyContext,

			-- Context API
			-- A table of context values inherited from the parent node
			context = context or {},

			-- A saved copy of the unmodified context; this will be updated when
			-- a component adds new context and used when a node is replaced
			originalContext = nil,
		}
	end

	local function mountFunctionVirtualNode(virtualNode)
		local element = virtualNode.currentElement

		local children = element.component(element.props)

		updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children)
	end

	local function mountPortalVirtualNode(virtualNode)
		local element = virtualNode.currentElement

		local targetHostParent = element.props.target
		local children = element.props[Children]

		assert(renderer.isHostObject(targetHostParent), "Expected target to be host object")

		updateVirtualNodeWithChildren(virtualNode, targetHostParent, children)
	end

	local function mountFragmentVirtualNode(virtualNode)
		local element = virtualNode.currentElement
		local children = element.elements

		updateVirtualNodeWithChildren(virtualNode, virtualNode.hostParent, children)
	end

	--[[
		Constructs a new virtual node and mounts it, but does not place it into
		the tree.
	]]
	function mountVirtualNode(element, hostParent, hostKey, context, legacyContext)
		if config.internalTypeChecks then
			internalAssert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object")
			internalAssert(
				typeof(legacyContext) == "table" or legacyContext == nil,
				"Expected arg #5 to be of type table or nil"
			)
		end
		if config.typeChecks then
			assert(hostKey ~= nil, "Expected arg #3 to be non-nil")
			assert(
				Type.of(element) == Type.Element or typeof(element) == "boolean",
				"Expected arg #1 to be of type Element or boolean"
			)
		end

		-- Boolean values render as nil to enable terse conditional rendering.
		if typeof(element) == "boolean" then
			return nil
		end

		local kind = ElementKind.of(element)

		local virtualNode = createVirtualNode(element, hostParent, hostKey, context, legacyContext)

		if kind == ElementKind.Host then
			renderer.mountHostNode(reconciler, virtualNode)
		elseif kind == ElementKind.Function then
			mountFunctionVirtualNode(virtualNode)
		elseif kind == ElementKind.Stateful then
			element.component:__mount(reconciler, virtualNode)
		elseif kind == ElementKind.Portal then
			mountPortalVirtualNode(virtualNode)
		elseif kind == ElementKind.Fragment then
			mountFragmentVirtualNode(virtualNode)
		else
			error(("Unknown ElementKind %q"):format(tostring(kind), 2))
		end

		return virtualNode
	end

	--[[
		Constructs a new Roact virtual tree, constructs a root node for
		it, and mounts it.
	]]
	local function mountVirtualTree(element, hostParent, hostKey)
		if config.typeChecks then
			assert(Type.of(element) == Type.Element, "Expected arg #1 to be of type Element")
			assert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object")
		end

		if hostKey == nil then
			hostKey = "RoactTree"
		end

		local tree = {
			[Type] = Type.VirtualTree,
			[InternalData] = {
				-- The root node of the tree, which starts into the hierarchy of
				-- Roact component instances.
				rootNode = nil,
				mounted = true,
			},
		}

		tree[InternalData].rootNode = mountVirtualNode(element, hostParent, hostKey)

		return tree
	end

	--[[
		Unmounts the virtual tree, freeing all of its resources.

		No further operations should be done on the tree after it's been
		unmounted, as indicated by its the `mounted` field.
	]]
	local function unmountVirtualTree(tree)
		local internalData = tree[InternalData]
		if config.typeChecks then
			assert(Type.of(tree) == Type.VirtualTree, "Expected arg #1 to be a Roact handle")
			assert(internalData.mounted, "Cannot unmounted a Roact tree that has already been unmounted")
		end

		internalData.mounted = false

		if internalData.rootNode ~= nil then
			unmountVirtualNode(internalData.rootNode)
		end
	end

	--[[
		Utility method for updating the root node of a virtual tree given a new
		element.
	]]
	local function updateVirtualTree(tree, newElement)
		local internalData = tree[InternalData]
		if config.typeChecks then
			assert(Type.of(tree) == Type.VirtualTree, "Expected arg #1 to be a Roact handle")
			assert(Type.of(newElement) == Type.Element, "Expected arg #2 to be a Roact Element")
		end

		internalData.rootNode = updateVirtualNode(internalData.rootNode, newElement)

		return tree
	end

	reconciler = {
		mountVirtualTree = mountVirtualTree,
		unmountVirtualTree = unmountVirtualTree,
		updateVirtualTree = updateVirtualTree,

		createVirtualNode = createVirtualNode,
		mountVirtualNode = mountVirtualNode,
		unmountVirtualNode = unmountVirtualNode,
		updateVirtualNode = updateVirtualNode,
		updateVirtualNodeWithChildren = updateVirtualNodeWithChildren,
		updateVirtualNodeWithRenderResult = updateVirtualNodeWithRenderResult,
	}

	return reconciler
end

return createReconciler
