local EngineFeatureTextBoundsRoundUp = game:GetEngineFeature("TextBoundsRoundUp")

local TextService = game:GetService("TextService")

local Text = {}

-- FYI: Any number greater than 2^30 will make TextService:GetTextSize give invalid results
local MAX_BOUND = 10000

-- Remove with EngineFeatureTextBoundsRoundUp
Text._TEMP_PATCHED_PADDING = Vector2.new(0, 0)

if not EngineFeatureTextBoundsRoundUp then
	Text._TEMP_PATCHED_PADDING = Vector2.new(2, 2)
end

-- Wrapper function for GetTextSize
function Text.GetTextBounds(text, font, fontSize, bounds)
	return TextService:GetTextSize(text, fontSize, font, bounds) + Text._TEMP_PATCHED_PADDING
end

function Text.GetTextWidth(text, font, fontSize)
	return Text.GetTextBounds(text, font, fontSize, Vector2.new(MAX_BOUND, MAX_BOUND)).X
end

function Text.GetTextHeight(text, font, fontSize, widthCap)
	return Text.GetTextBounds(text, font, fontSize, Vector2.new(widthCap, MAX_BOUND)).Y
end

-- TODO(CLIPLAYEREX-391): Kill these truncate functions once we have official support for text truncation
function Text.Truncate(text, font, fontSize, widthInPixels, overflowMarker)
	overflowMarker = overflowMarker or ""

	if Text.GetTextWidth(text, font, fontSize) > widthInPixels then
		-- A binary search may be more efficient
		local lastText = ""
		for _, stopIndex in utf8.graphemes(text) do
			local newText = string.sub(text, 1, stopIndex) .. overflowMarker
			if Text.GetTextWidth(newText, font, fontSize) > widthInPixels then
				return lastText
			end
			lastText = newText
		end
	else -- No truncation needed
		return text
	end

	return ""
end

function Text.TruncateTextLabel(textLabel, overflowMarker)
	textLabel.Text = Text.Truncate(textLabel.Text, textLabel.Font,
			textLabel.TextSize, textLabel.AbsoluteSize.X, overflowMarker)
end

-- Remove whitespace from the beginning and end of the string
function Text.Trim(str)
	if type(str) ~= "string" then
		error(string.format("Text.Trim called on non-string type %s.", type(str)), 2)
	end
	return (str:gsub("^%s*(.-)%s*$", "%1"))
end

-- Remove whitespace from the end of the string
function Text.RightTrim(str)
	if type(str) ~= "string" then
		error(string.format("Text.RightTrim called on non-string type %s.", type(str)), 2)
	end
	return (str:gsub("%s+$", ""))
end

-- Remove whitespace from the beginning of the string
function Text.LeftTrim(str)
	if type(str) ~= "string" then
		error(string.format("Text.LeftTrim called on non-string type %s.", type(str)), 2)
	end
	return (str:gsub("^%s+", ""))
end

-- Replace multiple whitespace with one; remove leading and trailing whitespace
function Text.SpaceNormalize(str)
	if type(str) ~= "string" then
		error(string.format("Text.SpaceNormalize called on non-string type %s.", type(str)), 2)
	end
	return (str:gsub("%s+", " "):gsub("^%s+" , ""):gsub("%s+$" , ""))
end

-- Splits a string by the provided pattern into a table. The pattern is interpreted as plain text.
function Text.Split(str, pattern)
	if type(str) ~= "string" then
		error(string.format("Text.Split called on non-string type %s.", type(str)), 2)
	elseif type(pattern) ~= "string" then
		error(string.format("Text.Split called with a pattern that is non-string type %s.", type(pattern)), 2)
	elseif pattern == "" then
		error("Text.Split called with an empty pattern.", 2)
	end

	local result = {}
	local currentPosition = 1

	while true do
		local patternStart, patternEnd = string.find(str, pattern, currentPosition, true)
		if not patternStart or not patternEnd then break end
		table.insert(result, string.sub(str, currentPosition, patternStart - 1))
		currentPosition = patternEnd + 1
	end

	table.insert(result, string.sub(str, currentPosition, string.len(str)))

	return result
end

return Text