-- Set anchor at offset from absolute position
function Element:setAnchor(offsetX, offsetY)
	if(offsetX > self:getSize().x or offsetY > self:getSize().y or offsetX < 0 or offsetY < 0) then
		error("Attempt to set anchor beyond element's bounds")
	else
		self.anchor = Vector2.new(offsetX, offsetY)
	end
end

function Element:isDisplayed()
	return self:getRbxInstance().Visible
end

function Element:isSelected()
	return self:getRbxInstance().Selected
end

function Element:getRbxInstance()
	return self:waitForRbxInstance(self.path.waitDelay, self.path.waitTimeout)
end

function Element:waitForRbxInstance(timeout, delay)
	if self.rbxInstance == nil and self.path ~= nil then
		self.path:setWait(timeout, delay)
		self.rbxInstance = self.path:waitForFirstInstance()
	end

	if self.rbxInstance and not self.anchor then
		if pcall(function() local size = self.rbxInstance.AbsoluteSize end) then
			self.anchor = self.rbxInstance.AbsoluteSize/2
		else
			self.anchor = nil
		end
	end

	return self.rbxInstance
end

function Element:_override(class)
	for k, v in pairs(class) do
		if not k:find("^_") then
			self[k] = v
		end
	end
end

function Element:centralizeInstance()
	self:_centralizeInScrollingFrame(self:getRbxInstance())
end

function Element:centralize()
	local instance = self:getRbxInstance()
	if instance then
		self:centralizeInstance()
	else
		self:centralizeWithInfiniteScrolling()
	end
end

function Element:_scrollingFrames(instance)
	if instance == nil or instance == game then return 0 end
	local num = self:_scrollingFrames(instance.Parent)
	if instance.ClassName == "ScrollingFrame" then num = num + 1 end
	return num
end

function Element:_centralizeInScrollingFrame(child, parent)
	if child == game then return end
	parent = parent or child.Parent
	if parent == game then return end

	if parent.ClassName == "ScrollingFrame" then
		self:_centralizeInScrollingFrame(parent, parent.Parent)
		-- this is computational error tolerate.
		local threshold = 2

		--first scroll down to make child appears neas screen,
		--so that we can access child.AbsolutPosition property
		local isChildInScreen = false
		while not isChildInScreen do

			local prevChildPosition = child.AbsolutePosition
			local prevCanvasPosition = parent.CanvasPosition
			-- when scroll too much at one time, the element may move out side of screen immediately
			-- its AbsoluteSize will not update. limit to 300
			local scrollDistance = Vector2.new(math.min(300, parent.AbsoluteSize.X), math.min(300, parent.AbsoluteSize.Y))
			parent.CanvasPosition  = parent.CanvasPosition + scrollDistance
			wait()
			local deltaCanvas = (parent.CanvasPosition - prevCanvasPosition)
			local isBottom = deltaCanvas.Magnitude <= threshold
			local deltaChild = child.AbsolutePosition - prevChildPosition
			isChildInScreen = isBottom or deltaChild.Magnitude > threshold
		end
		--second scroll to centerize the child, at most twice.
		for _ = 1, 2 do
			local frameCenter = parent.AbsolutePosition + parent.AbsoluteSize/2
			local childCenter = child.AbsolutePosition + child.AbsoluteSize/2
			local delta = childCenter - frameCenter
			if delta.Magnitude <= threshold then break end
			parent.CanvasPosition  = parent.CanvasPosition + delta
			wait()
		end
	else
		self:_centralizeInScrollingFrame(child, parent.Parent)
	end
end

function Element:_scrollToFindInstance(scrollingFrame, absPath)