local type { Vector, Angle, Player } = import("~/nattlua/definitions/glua.nlua")

local ply = nil
local NewPos = nil
local dirs = {}

local function FindPassableSpace(n, direction, step)
	local origin = dirs[n]

	if not origin then
		origin = ply:GetPos()
		dirs[n] = origin
	end

	--for i=0,100 do
	--origin = VectorMA( origin, step, direction )
	origin:Add(step * direction)

	if not ply:IsStuck(false, origin) then
		ply:SetPos(origin)

		if not ply:IsStuck(false) then
			NewPos = ply:GetPos()
			return true
		end
	end

	--end
	return false
end

--[[
	Purpose: Unstucks player ,
	Note: Very expensive to call, you have been warned!
]] local forward = Vector(1, 0, 0)
local right = Vector(0, 1, 0)
local up = Vector(0, 0, 1)

local function UnstuckPlayer(pl: Player)
	ply = pl
	NewPos = ply:GetPos()
	local OldPos = NewPos
	dirs = {}

	if ply:IsStuck() then
		local SearchScale = 1 -- Increase and it will unstuck you from even harder places but with lost accuracy. Please, don't try higher values than 12
		local ok
		local forward = ply:GetAimVector()
		forward.z = 0
		forward:Normalize()
		right = forward:Angle():Right()

		for i = 1, 100 do
			ok = true

			if (not FindPassableSpace(1, forward, SearchScale)) then
				if (not FindPassableSpace(2, right, SearchScale)) then
					if (not FindPassableSpace(3, right, -SearchScale)) then
						if (not FindPassableSpace(4, up, SearchScale)) then
							if (not FindPassableSpace(5, up, -SearchScale)) then
								if (not FindPassableSpace(6, forward, -SearchScale)) then -- spam spam spam
									--Msg( "Can't find the world for player "..tostring(ply).."\n" )
									ok = false
								end -- back
							end -- down
						end -- up
					end -- left
				end
			end

			if ok then break end
		end

		if not ok then return false end

		if OldPos == NewPos then
			print("Unstuck: Shouldnothappen")
			-- Not stuck?
			-- For some reason setting origin MAY apply some velocity so we're resetting it here.
			return true
		else
			ply:SetPos(NewPos)

			if SERVER and ply and ply:IsValid() and ply:GetPhysicsObject():IsValid() then
				ply:SetVelocity(-ply:GetVelocity())
			end

			return true
		end
	end
end

util.UnstuckPlayer = UnstuckPlayer
local Player = FindMetaTable("Player")

function Player:UnStuck()
	return UnstuckPlayer(self)
end

local Entity = FindMetaTable("Entity")
Entity.UnStuck = Entity.UnStuck or function()
	assert(false, "not implemented")
end
