--[=[
	Instantly skips the spring forwards by that amount time
	@param delta number -- Time to skip forwards
	@return ()
]=]
function SpringObject:TimeSkip(delta)
	assert(type(delta) == "number", "Bad delta")

	self._currentSpring:TimeSkip(delta)
	self.Changed:Fire()
end

function SpringObject:__index(index)
	if index == "Value" or index == "Position" or index == "p" then
		return SpringUtils.fromLinearIfNeeded(self._currentSpring.Value)
	elseif index == "Velocity" or index == "v" then
		return SpringUtils.fromLinearIfNeeded(self._currentSpring.Velocity)
	elseif index == "Target" or index == "t" then
		return SpringUtils.fromLinearIfNeeded(self._currentSpring.Target)
	elseif index == "Damper" or index == "d" then
		return self._currentSpring.Damper
	elseif index == "Speed" or index == "s" then
		return self._currentSpring.Speed
	elseif index == "Clock" then
		return self._currentSpring.Clock
	elseif SpringObject[index] then
		return SpringObject[index]
	else
		error(("%q is not a member of SpringObject"):format(tostring(index)))
	end
end

function SpringObject:__newindex(index, value)
	if index == "Value" or index == "Position" or index == "p" then
		local observable = Blend.toPropertyObservable(value) or Rx.of(value)

		self._maid._valueSub = observable:Subscribe(function(unconverted)
			local converted = SpringUtils.toLinearIfNeeded(unconverted)
			self:_getSpringForType(converted).Value = converted
			self.Changed:Fire()
		end)
	elseif index == "Velocity" or index == "v" then
		local observable = Blend.toPropertyObservable(value) or Rx.of(value)

		self._maid._velocitySub = observable:Subscribe(function(unconverted)
			local converted = SpringUtils.toLinearIfNeeded(unconverted)

			self:_getSpringForType(0*converted).Velocity = converted
			self.Changed:Fire()
		end)
	elseif index == "Target" or index == "t" then
		local observable = Blend.toPropertyObservable(value) or Rx.of(value)

		self._maid._targetSub = observable:Subscribe(function(unconverted)
			local converted = SpringUtils.toLinearIfNeeded(unconverted)
			self:_getSpringForType(converted).Target = converted

			self.Changed:Fire()
		end)
	elseif index == "Damper" or index == "d" then
		local observable = assert(Blend.toNumberObservable(value), "Invalid damper")

		self._maid._damperSub = observable:Subscribe(function(unconverted)
			assert(type(unconverted) == "number", "Bad damper")

			self._currentSpring.Damper = unconverted
			self.Changed:Fire()
		end)
	elseif index == "Speed" or index == "s" then
		local observable = assert(Blend.toNumberObservable(value), "Invalid speed")

		self._maid._speedSub = observable:Subscribe(function(unconverted)
			assert(type(unconverted) == "number", "Bad damper")

			self._currentSpring.Speed = unconverted
			self.Changed:Fire()
		end)
	elseif index == "Clock" then
		assert(type(value) == "function", "Bad clock value")
		self._currentSpring.Clock = value
		self.Changed:Fire()
	else
		error(("%q is not a member of SpringObject"):format(tostring(index)))
	end
end

function SpringObject:_getSpringForType(converted)
	if rawget(self, "_currentSpring") == nil then
		-- only happens on init
		rawset(self, "_currentSpring", Spring.new(converted))
		return self._currentSpring
	else
		local currentType = typeof(SpringUtils.fromLinearIfNeeded(self._currentSpring.Value))
		if currentType == typeof(converted) then
			return self._currentSpring
		else
			local oldDamper = self._currentSpring.d
			local oldSpeed = self._currentSpring.s

			self._currentSpring = Spring.new(converted)
			self._currentSpring.Speed = oldSpeed
			self._currentSpring.Damper = oldDamper
			return self._currentSpring
		end
	end
end