--[[*
 * Fast paths for creating regular expressions for common glob patterns.
 * This can significantly speed up processing and has very little downside
 * impact when none of the fast paths match.
 ]]


parse.fastpaths = function(input: string, options: Object?)
	local opts = Object.assign({}, options)
	local max = if typeof(opts.maxLength) == "number" then math.min(MAX_LENGTH, opts.maxLength) else MAX_LENGTH
	local len = #input
	if len > max then
		error(
			Error.new(
				("SyntaxError: Input length: %s, exceeds maximum allowed length: %s"):format(
					tostring(len),
					tostring(max)
				)
			)
		)
	end

	input = Boolean.toJSBoolean(REPLACEMENTS[input]) and REPLACEMENTS[input] or input
	local win32 = utils.isWindows(options)

	-- create constants based on platform, for windows or posix

	local ref = constants.globChars(win32)
	local DOT_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOTS, NO_DOTS_SLASH, STAR, START_ANCHOR =
		ref.DOT_LITERAL,
		ref.SLASH_LITERAL,
		ref.ONE_CHAR,
		ref.DOTS_SLASH,
		ref.NO_DOT,
		ref.NO_DOTS,
		ref.NO_DOTS_SLASH,
		ref.STAR,
		ref.START_ANCHOR

	local nodot = if Boolean.toJSBoolean(opts.dot) then NO_DOTS else NO_DOT
	local slashDot = if Boolean.toJSBoolean(opts.dot) then NO_DOTS_SLASH else NO_DOT
	local capture = if Boolean.toJSBoolean(opts.capture) then "" else "?:"
	local state = { negated = false, prefix = "" }
	local star = if opts.bash == true then ".*?" else STAR

	if Boolean.toJSBoolean(opts.capture) then
		star = ("(%s)"):format(star)
	end

	local function globstar(opts)
		if opts.noglobstar == true then
			return star
		end
		return ("(%s(?:(?!%s%s).)*?)"):format(
			capture,
			START_ANCHOR,
			if Boolean.toJSBoolean(opts.dot) then DOTS_SLASH else DOT_LITERAL
		)
	end

	local function create(str): string?
		if str == "*" then
			return ("%s%s%s"):format(nodot, ONE_CHAR, star)
		elseif str == ".*" then
			return ("%s%s%s"):format(DOT_LITERAL, ONE_CHAR, star)
		elseif str == "*.*" then
			return ("%s%s%s%s%s"):format(nodot, star, DOT_LITERAL, ONE_CHAR, star)
		elseif str == "*/*" then
			return ("%s%s%s%s%s%s"):format(nodot, star, SLASH_LITERAL, ONE_CHAR, slashDot, star)
		elseif str == "**" then
			return nodot .. globstar(opts)
		elseif str == "**/*" then
			return ("(?:%s%s%s)?%s%s%s"):format(nodot, globstar(opts), SLASH_LITERAL, slashDot, ONE_CHAR, star)
		elseif str == "**/*.*" then
			return ("(?:%s%s%s)?%s%s%s%s%s"):format(
				nodot,
				globstar(opts),
				SLASH_LITERAL,
				slashDot,
				star,
				DOT_LITERAL,
				ONE_CHAR,
				star
			)
		elseif str == "**/.*" then
			return ("(?:%s%s%s)?%s%s%s"):format(nodot, globstar(opts), SLASH_LITERAL, DOT_LITERAL, ONE_CHAR, star)
		else
			local match = RegExp("^(.*?)\\.(\\w+)$"):exec(str)
			if not Boolean.toJSBoolean(match) then
				return nil
			end

			local source = create(match[2])
			if not Boolean.toJSBoolean(source) then
				return nil
			end

			return source .. DOT_LITERAL .. match[3]
		end
	end

	local output = utils.removePrefix(input, state)
	local source = create(output)

	if source ~= nil and Boolean.toJSBoolean(source) and opts.strictSlashes ~= true then
		source ..= ("%s?"):format(SLASH_LITERAL)
	end

	return source
end

return parse
