-- Calculates closest point on part to player (treats part as a plane since it is not very thick), then uses SPLIT_FACTOR and
-- maxDistanceBeforeSplitting to determine if the player is close enough to split
local function isCloseEnoughToSplit(playerPos, stormPos, partPos, splitLevel, vertexList, stormRadius)
	local partNormal = (partPos * Vector3.new(1, 0, 1) - stormPos * Vector3.new(1, 0, 1)).Unit
	local closestPlanarPointToPlayer = playerPos - (partNormal * (partNormal:Dot(playerPos - partPos)))

	-- Calculate four side vectors
	local sides = {}
	for i = 1, 4 do
		sides[i] = vertexList[(i % 4) + 1] - vertexList[i]
	end

	-- Calculate normals to each side by subtracting middle of part from midpoint of each side
	local normals = {}
	for i = 1, 4 do
		normals[i] = ((vertexList[i] + sides[i] / 2) - partPos).Unit
	end

	-- Determine whether closestPlanarPointToPlayer is towards middle or outside of each side (true = outside)
	local out = {}
	for i = 1, 4 do
		out[i] = normals[i]:Dot(closestPlanarPointToPlayer - vertexList[i]) > 0
	end

	-- Find the closest point on the part to the player
	local closestPointOnPartToPlayer = closestPlanarPointToPlayer -- if all of 'out' array is false, closestPlanarPointToPlayer is already on part
	for i = 1, 4 do
		if out[i] then
			if out[(i % 4) + 1] then -- if out[i+1] then set to closest vertex
				closestPointOnPartToPlayer = vertexList[(i % 4) + 1]
				break
			elseif not out[4 - ((5 - i) % 4)] then -- if not out[i-1] then set to closest point on side[i]
				closestPointOnPartToPlayer = vertexList[i] + sides[i].Unit * sides[i].Unit:Dot(closestPlanarPointToPlayer - vertexList[i])
				break
			end
		end
	end

	-- Check if closestPointOnPartToPlayer is close enough to player to split the part
	local distanceFromPlayer = (closestPointOnPartToPlayer - playerPos).Magnitude
	if distanceFromPlayer < (FRACTION_OF_RADIUS_BEFORE_SPLITTING * stormRadius) * math.pow(SPLIT_FACTOR, splitLevel - 1) then
		return true
	end
	return false
end

local function updateBarrierPartInfo(barrierPartInfo, playerPos, stormPos, stormRadius, barrierPartID)
	local vertexList = getVertexListFromRadians(math.rad(barrierPartInfo.startDeg), math.rad(barrierPartInfo.endDeg), stormRadius, stormPos)
	updatePartFromVertexList(barrierPartInfo.part, vertexList, stormPos)
	barrierPartInfo.part.Rotation = Vector3.new(0, 270 - ((barrierPartInfo.startDeg + barrierPartInfo.endDeg) / 2), 0)

	if
		barrierPartInfo.splitLevel < MAX_SPLIT_LEVEL and
		barrierPartInfo.part.Size.X > (2 * MIN_BARRIERPART_WIDTH) and
		isCloseEnoughToSplit(playerPos, stormPos, barrierPartInfo.part.Position, barrierPartInfo.splitLevel, vertexList, stormRadius)
	then -- split this barrierPart in half
		-- Create second part, add to barrierPartInfos table
		local newPart = barrierPartInfo.part:Clone()
		newPart.Parent = barrierPartsFolder
		local newBarrierPartInfo = getBarrierPartInfo(newPart, (barrierPartInfo.endDeg + barrierPartInfo.startDeg) / 2, barrierPartInfo.endDeg,
													  barrierPartInfo.splitLevel + 1, true, barrierPartInfo.isSecondPartBySplitLevel)
		table.insert(barrierPartInfos, barrierPartID + 1, newBarrierPartInfo)

		-- Update first part
		barrierPartInfo.splitLevel = barrierPartInfo.splitLevel + 1
		barrierPartInfo.endDeg = (barrierPartInfo.endDeg + barrierPartInfo.startDeg) / 2
		barrierPartInfo.isSecondPartBySplitLevel[barrierPartInfo.splitLevel] = false
		updateBarrierPartInfo(barrierPartInfo, playerPos, stormPos, stormRadius, barrierPartID)
	else
		local nextPartInfo = barrierPartInfos[barrierPartID + 1]
		if
			barrierPartInfo.splitLevel > 1 and
			not barrierPartInfo.isSecondPartBySplitLevel[barrierPartInfo.splitLevel] and
			barrierPartInfo.splitLevel == nextPartInfo.splitLevel
		then -- combine this barrierPart with the next barrierPart
			local testVertexList = getVertexListFromRadians(math.rad(barrierPartInfo.startDeg), math.rad(nextPartInfo.endDeg), stormRadius, stormPos)
			if
				not isCloseEnoughToSplit(playerPos, stormPos, getPositionFromVertexList(testVertexList), barrierPartInfo.splitLevel - 1, testVertexList, stormRadius) or
				barrierPartInfo.part.Size.X < MIN_BARRIERPART_WIDTH
			then
				-- Update barrierPartInfo values
				table.remove(barrierPartInfo.isSecondPartBySplitLevel, barrierPartInfo.splitLevel)
				barrierPartInfo.splitLevel = barrierPartInfo.splitLevel - 1
				barrierPartInfo.endDeg = nextPartInfo.endDeg

				-- Destroy next part/partInfo
				nextPartInfo.part:Destroy()
				table.remove(barrierPartInfos, barrierPartID + 1)

				-- Update barrierPartInfo.part
				updatePartFromVertexList(barrierPartInfo.part, testVertexList, stormPos)
				barrierPartInfo.part.Rotation = Vector3.new(0, 270 - ((barrierPartInfo.startDeg + barrierPartInfo.endDeg) / 2), 0)
			end
		end
	end
	-- Set transparency based on distance to player
	barrierPartInfo.part.Transparency = math.clamp(((barrierPartInfo.part.Position - playerPos).Magnitude - TRANSPARENCY_FALLOFF_RANGE.Min) /
										(TRANSPARENCY_FALLOFF_RANGE.Max - TRANSPARENCY_FALLOFF_RANGE.Min), 0, 0.99)
end