local Q=require(script.Parent.Symbol)local R=require(script.Parent.createFragment)local w=require(script.Parent.createSignal)local S=require(script.Parent.PropMarkers.Children)local u=require(script.Parent.Component)local function l(Q)return{value=Q;onUpdate=w()}end local function h(Q)local w=u:extend("\080\114\111\118\105\100\101\114")function w.init(w,R)w.contextEntry=l(R.value)w:__addContext(Q.key,w.contextEntry)end function w.willUpdate(R,Q)if Q.value~=R.props.value then R.contextEntry.value=Q.value end end function w.didUpdate(R,Q)if Q.value~=R.props.value then R.contextEntry.onUpdate:fire(R.props.value)end end function w.render(Q)return R(Q.props[S])end return w end local function o(Q)local R=u:extend("\067\111\110\115\117\109\101\114")function R.validateProps(Q)if type(Q.render)~="\102\117\110\099\116\105\111\110"then return false,"\067\111\110\115\117\109\101\114 \101\120\112\101\099\116\115 \097 \096\114\101\110\100\101\114\096 \102\117\110\099\116\105\111\110"else return true end end function R.init(w,R)w.contextEntry=w:__getContext(Q.key)end function R.render(R)local w if R.contextEntry~=nil then w=R.contextEntry.value else w=Q.defaultValue end return R.props.render(w)end function R.didUpdate(Q)if Q.contextEntry~=nil then Q.lastValue=Q.contextEntry.value end end function R.didMount(Q)if Q.contextEntry~=nil then Q.disconnect=Q.contextEntry.onUpdate:subscribe(function(R)if R~=Q.lastValue then Q:setState({})end end)end end function R.willUnmount(Q)if Q.disconnect~=nil then Q.disconnect()Q.disconnect=nil end end return R end local V={}V.__index=V function V.new(R)return setmetatable({defaultValue=R;key=Q.named("\067\111\110\116\101\120\116\075\101\121")},V)end function V.__tostring(Q)return"\082\111\097\099\116\067\111\110\116\101\120\116"end local function Z(Q)local R=V.new(Q)return{Provider=h(R),Consumer=o(R)}end return Z
local Symbol = require(script.Parent.Symbol)
local createFragment = require(script.Parent.createFragment)
local createSignal = require(script.Parent.createSignal)
local Children = require(script.Parent.PropMarkers.Children)
local Component = require(script.Parent.Component)

--[[
	Construct the value that is assigned to Roact's context storage.
]]
local function createContextEntry(currentValue)
	return {
		value = currentValue,
		onUpdate = createSignal(),
	}
end

local function createProvider(context)
	local Provider = Component:extend("Provider")

	function Provider:init(props)
		self.contextEntry = createContextEntry(props.value)
		self:__addContext(context.key, self.contextEntry)
	end

	function Provider:willUpdate(nextProps)
		-- If the provided value changed, immediately update the context entry.
		--
		-- During this update, any components that are reachable will receive
		-- this updated value at the same time as any props and state updates
		-- that are being applied.
		if nextProps.value ~= self.props.value then
			self.contextEntry.value = nextProps.value
		end
	end

	function Provider:didUpdate(prevProps)
		-- If the provided value changed, after we've updated every reachable
		-- component, fire a signal to update the rest.
		--
		-- This signal will notify all context consumers. It's expected that
		-- they will compare the last context value they updated with and only
		-- trigger an update on themselves if this value is different.
		--
		-- This codepath will generally only update consumer components that has
		-- a component implementing shouldUpdate between them and the provider.
		if prevProps.value ~= self.props.value then
			self.contextEntry.onUpdate:fire(self.props.value)
		end
	end

	function Provider:render()
		return createFragment(self.props[Children])
	end

	return Provider
end

local function createConsumer(context)
	local Consumer = Component:extend("Consumer")

	function Consumer.validateProps(props)
		if type(props.render) ~= "function" then
			return false, "Consumer expects a `render` function"
		else
			return true
		end
	end

	function Consumer:init(_props)
		-- This value may be nil, which indicates that our consumer is not a
		-- descendant of a provider for this context item.
		self.contextEntry = self:__getContext(context.key)
	end

	function Consumer:render()
		-- Render using the latest available for this context item.
		--
		-- We don't store this value in state in order to have more fine-grained
		-- control over our update behavior.
		local value
		if self.contextEntry ~= nil then
			value = self.contextEntry.value
		else
			value = context.defaultValue
		end

		return self.props.render(value)
	end

	function Consumer:didUpdate()
		-- Store the value that we most recently updated with.
		--
		-- This value is compared in the contextEntry onUpdate hook below.
		if self.contextEntry ~= nil then
			self.lastValue = self.contextEntry.value
		end
	end

	function Consumer:didMount()
		if self.contextEntry ~= nil then
			-- When onUpdate is fired, a new value has been made available in
			-- this context entry, but we may have already updated in the same
			-- update cycle.
			--
			-- To avoid sending a redundant update, we compare the new value
			-- with the last value that we updated with (set in didUpdate) and
			-- only update if they differ. This may happen when an update from a
			-- provider was blocked by an intermediate component that returned
			-- false from shouldUpdate.
			self.disconnect = self.contextEntry.onUpdate:subscribe(function(newValue)
				if newValue ~= self.lastValue then
					-- Trigger a dummy state update.
					self:setState({})
				end
			end)
		end
	end

	function Consumer:willUnmount()
		if self.disconnect ~= nil then
			self.disconnect()
			self.disconnect = nil
		end
	end

	return Consumer
end

local Context = {}
Context.__index = Context

function Context.new(defaultValue)
	return setmetatable({
		defaultValue = defaultValue,
		key = Symbol.named("ContextKey"),
	}, Context)
end

function Context:__tostring()
	return "RoactContext"
end

local function createContext(defaultValue)
	local context = Context.new(defaultValue)

	return {
		Provider = createProvider(context),
		Consumer = createConsumer(context),
	}
end

return createContext
