/*
half pipe lua, written by -
*/
rawset(_G,"HalfPipe",{}) //hello
//look for half pipes lines t ostore them for later use

//note, maybe add requisits like, minimum line lenght, i dunno 
local halfpipelinessearched = false
local function LocaStoreHalfPipeLines()
	if halfpipelinessearched == true then return end //yeeeeeeee, netgames and desynchs
	HalfPipe.HPLines = {}
	halfpipelinessearched = true
	for line in lines.iterate do
		if not (line and line.valid and line.special == 740) then continue end
		table.insert(HalfPipe.HPLines, line)
	end

end 

addHook("MapLoad",LocaStoreHalfPipeLines)
addHook("NetVars",LocaStoreHalfPipeLines)
addHook("MapChange",function() halfpipelinessearched = false HalfPipe.HPLines = nil end)

//helper functions
HalfPipe.funcs = {}

local function LocalSign(s) //bruh
	if s < 0 then 
		return -1 
	else 
		return 1 
	end
end

local function LocalClamp(val, mi, ma)
	return max(mi, min(ma, val))
end

local function localAngleToInt(angle, addtn) 
	//if not add then add = 0 end
	angle = $ / ANG1
	if not addtn then addtn = 0 end
	
	while angle < 0 or angle > 360 do
		if angle < 0 then
			angle = $ + 360
		end
		if angle > 360 then
			angle = $ - 360
		end
		angle = $ + addtn
	end

	return angle
end

//backported from source, gives in which line of al ine isa the point you give
//line can be a real life, or a table pretending to be a line
local function localP_PointOnLineSide(x, y, line)
	local v1 = line.v1
	local dx, dy, left, right

	if not line.dx then
		if x <= v1.x then
			return (line.dy > 0)
		end
	
		return (line.dy < 0)
	end
	if not line.dy then
		if y <= v1.y then
			return (line.dx < 0)
		end

		return (line.dx > 0)
	end

	dx = x - v1.x
	dy = y - v1.y

	left = FixedMul(line.dy / FRACUNIT, dx)
	right = FixedMul(dy, line.dx / FRACUNIT)

	if right < left then
		return 0 // front side
	end
	return 1 // back side

end




//gets the closes point in al ine, but clamped to the line edges
//x and y are the coordinates you wanna test this
//all the other ones can be given OR ommited by a REAL linedef
//or a table (can be unnamed or naed by x1,y1, x2,y3 fields)
local function LocalHalfPipeGetCPOL(x, y, x1, y1, x2, y2)
	if not HalfPipe.HPLines then return nil end 
	local cpol , mi, ma, line = {}, 0, 0, {}
	if type(x1) == "userdata" then
		cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1)
		line = x1
	elseif type(x1) == "table"
		if line[0] == nil then
			cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1.x1, x1.y1, x1.x2, x1.y2 )
			line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1.x1, x1.y1, x1.x2, x1.y2
		else
			cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1[1], x1[2], x1[3], x1[4] )
			line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1[1], x1[2], x1[3], x1[4]
		end
		
	else
		cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1, y1, x2, y2 )
		line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1, y1, x2, y2 
	end
	
	//now we clamp this point to the line range
	mi = min(line.v1.x, line.v2.x)
	ma = max(line.v1.x, line.v2.x)
	cpol.x = LocalClamp($, mi, ma)
	
	mi = min(line.v1.y, line.v2.y)
	ma = max(line.v1.y, line.v2.y)
	cpol.y = LocalClamp($, mi, ma)
	
	return cpol
end



//gets the closest half pipe line
//type defines how it wanna be searched
//noone or 0 is by distance from the closest point
//1 ir by collision with it 
//radius is necessary if you wanna find lines by collision
//is the distance it should use from the origin to detect collsiion with lines
local function LocalNearestHalfPipeLine(x ,y, searchtype, radius, ignore)
	if not HalfPipe.HPLines then return nil end 
	local closest, result, collist, defaultside = INT32_MIN, nil, {}, -1
	
	
	
	if not searchtype then searchtype = 0 end
	if not radius then radius = 42*FRACUNIT end

	for i = 1, #HalfPipe.HPLines do
		local line, dist, cpol, shouldignore
		line = HalfPipe.HPLines[i]
		if ignore and ignore ~= nil then
			if type(ignore) == "table" then
				for j = 1, #ignore do
					if ignore[j] and ignore[j] ~= nil and line == ignore[j] then
						shouldignore = true
					end
				end
			else
				if line == ignore then continue end
			end
		end
		if shouldignore and shouldignore == true then continue end
		cpol = LocalHalfPipeGetCPOL(x, y, line)
	
		dist = FixedHypot(x - cpol.x, y - cpol.y)
		
		if searchtype == 0 then
			if dist > closest then
				closest = dist
				result = line
			end
		end
		
		if searchtype == 1 then
			/*
				brief explanation
				the game does collision with a box (the 4 eges of the player object in a specific distance)
				what i recoomend is t ogive the radius is  the player radius + its speed + (FRACUNIT*12) 
				to MAKE ABSOLUTELY sure it will happen bedofre the game with aly line
				here instead of 4 sides, we'll have 16
				and if ANY side collides it will be added to a list, and the line that has more collisions, wins
			*/
			
			//wanna use this to check which side is not collision
			//because the player never goes throuhg the line itself
			defaultside = localP_PointOnLineSide(x, y, line)
			
			local colcount = 0
			
			for j = 1, 16 do
				local xx, yy, pointcollision
				xx = x + P_ReturnThrustX(nil, (ANGLE_45/2) * j, radius)
				yy = y + P_ReturnThrustY(nil, (ANGLE_45/2) * j, radius)
				pointcollision = localP_PointOnLineSide(xx, yy, line)
				
				//collided (actually is in a different side of the line than the origin)
				//to avoind "collision" with lines farther away, use the dist 
				if pointcollision ~= defaultside
				and dist <= radius + (4*FRACUNIT) then
					colcount = $ + 1
				end
				
			end
			
			//after we count the amount of sides that collided
			//we add this line to the table
			//ONLY IF a collsiion hapenned, ofc
			if colcount ~= 0 then
				table.insert(collist, {line, colcount})
				//sort everytime we found a new one
				table.sort(collist, function(a, b) return b[2] > a[2] end)
				result = collist[1][1]
			end
			
		end
		
	end
	return result
end

//get the wall front angle
local function LocalWallFrontAngle(line, back)
	local side, lineang, linelenght, pos, dir = nil, nil , nil, {}, ANGLE_90
	lineang = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y)
	linelenght = FixedHypot(line.v1.x - line.v2.x, line.v1.y - line.v2.y)
	
	//go to thel ine mdidle
	pos.x = line.v1.x + P_ReturnThrustX(nil, lineang, linelenght/2)
	pos.y = line.v1.y + P_ReturnThrustY(nil, lineang, linelenght/2)
	
	//thrust forwards a bit
	pos.x = $ + P_ReturnThrustX(nil, lineang + ANGLE_90, 16*FRACUNIT)
	pos.y = $ + P_ReturnThrustY(nil, lineang + ANGLE_90, 16*FRACUNIT)
	
	//test to know which side it is
	side = localP_PointOnLineSide(pos.x, pos.y, line)	
	
	if ((side == true or side == 1) and (back == nil or back == false)) //is in backside, invert, if you want the front
	or ((side == false or side == 0) and back == true) then 	//is in the front, invert because you want the back
		dir = -ANGLE_90
	end

	
	return lineang + dir 
end

HalfPipe.funcs.clamp = LocalClamp
HalfPipe.funcs.poitonlineside = localP_PointOnLineSide
HalfPipe.funcs.pointonline = LocalHalfPipeGetCPOL
HalfPipe.funcs.gethalfpipeline = LocalNearestHalfPipeLine
HalfPipe.funcs.sign = LocalSign
HalfPipe.funcs.angletoint = localAngleToInt
HalfPipe.funcs.wallfrontangle = LocalWallFrontAngle