--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]

local FrameTime = FrameTime
local util_QuickTrace = util.QuickTrace
local VectorRand = VectorRand
local Vector = Vector
local Angle = Angle
local physenv_GetGravity = physenv.GetGravity

local BUILDER, PART = pac.PartTemplate("base_drawable")

PART.ClassName = "jiggle"
PART.Group = 'model'
PART.Icon = 'icon16/chart_line.png'

BUILDER:StartStorableVars()
	BUILDER:GetSet("Strain", 0.5, {editor_onchange = function(self, num)
		self.sens = 0.25
		num = tonumber(num)
		return math.Clamp(num, 0, 1) * 0.999
	end})
	BUILDER:GetSet("Speed", 1)
	BUILDER:GetSet("ConstantVelocity", Vector(0, 0, 0))
	BUILDER:GetSet("LocalVelocity", true)
	BUILDER:GetSet("JiggleAngle", true)
	BUILDER:GetSet("JigglePosition", true)

	BUILDER:GetSet("ConstrainPitch", false)
	BUILDER:GetSet("ConstrainYaw", false)
	BUILDER:GetSet("ConstrainRoll", false)

	BUILDER:GetSet("ConstrainX", false)
	BUILDER:GetSet("ConstrainY", false)
	BUILDER:GetSet("ConstrainZ", false)

	BUILDER:GetSet("ConstrainSphere", 0)
	BUILDER:GetSet("StopRadius", 0)
	BUILDER:GetSet("Ground", false)
	BUILDER:GetSet("ResetOnHide", false)
BUILDER:EndStorableVars()

local math_AngleDifference = math.AngleDifference

function PART:Reset()
	local pos, ang = self:GetDrawPosition()

	self.pos = pos or Vector()
	self.vel = Vector()

	self.ang = ang or Angle()
	self.angvel = Angle()
end

function PART:Initialize()
	self.pos = Vector()
	self.vel = Vector()

	self.ang = Angle()
	self.angvel = Angle()

	self.first_time_reset = true
end

function PART:OnShow()
	if self.ResetOnHide then
		self:Reset()
	end
end

local inf, ninf = math.huge, -math.huge

local function check_num(num)
	if num ~= inf and num ~= ninf and (num >= 0 or num <= 0) then
		return num
	end

	return 0
end

function PART:OnDraw()
	local pos, ang = self:GetDrawPosition()

	if self.first_time_reset then
		self:Reset()
		self.first_time_reset = false
	end

	local delta = FrameTime()
	local speed = self.Speed * delta

	self.vel = self.vel or VectorRand()
	self.pos = self.pos or pos * 1

	if self.StopRadius ~= 0 and self.pos and self.pos:Distance(pos) < self.StopRadius then
		self.vel = Vector()
		return
	end

	if self.JigglePosition then
		if not self.ConstrainX then
			self.vel.x = self.vel.x + (pos.x - self.pos.x)

			if self.LocalVelocity then
				self.vel = self.vel + ang:Right() * self.ConstantVelocity.x
			else
				self.vel.x = self.vel.x + self.ConstantVelocity.x
			end

			self.pos.x = self.pos.x + (self.vel.x * (self.Invert and -speed or speed))
			self.vel.x = self.vel.x * self.Strain
		else
			self.pos.x = pos.x
		end

		if not self.ConstrainY then
			self.vel.y = self.vel.y + (pos.y - self.pos.y)

			if self.LocalVelocity then
				self.vel = self.vel + ang:Forward() * self.ConstantVelocity.y
			else
				self.vel.y = self.vel.y + self.ConstantVelocity.y
			end

			self.pos.y = self.pos.y + (self.vel.y * speed)
			self.vel.y = self.vel.y * self.Strain
		else
			self.pos.y = pos.y
		end

		if not self.ConstrainZ then
			self.vel.z = self.vel.z + (pos.z - self.pos.z)

			if self.LocalVelocity then
				self.vel = self.vel + ang:Up() * self.ConstantVelocity.z
			else
				self.vel.z = self.vel.z + self.ConstantVelocity.z
			end

			self.pos.z = self.pos.z + (self.vel.z * speed)
			self.vel.z = self.vel.z * self.Strain
		else
			self.pos.z = pos.z
		end

		if self.Ground then
			self.pos.z = util_QuickTrace(pos, physenv_GetGravity() * 100).HitPos.z
		end
	else
		self.pos = pos
	end

	if self.ConstrainSphere > 0 then
		local len = math.min(self.pos:Distance(pos), self.ConstrainSphere)

		self.pos = pos + (self.pos - pos):GetNormalized() * len
	end

	if self.JiggleAngle then
		self.angvel = self.angvel or ang * 1
		self.ang = self.ang or ang * 1

		if not self.ConstrainPitch then
			self.angvel.p = self.angvel.p + math_AngleDifference(ang.p, self.ang.p)
			self.ang.p = math_AngleDifference(self.ang.p, self.angvel.p * -speed)
			self.angvel.p = self.angvel.p * self.Strain
		end

		if not self.ConstrainYaw then
			self.angvel.y = self.angvel.y + math_AngleDifference(ang.y, self.ang.y)
			self.ang.y = math_AngleDifference(self.ang.y, self.angvel.y * -speed)
			self.angvel.y = self.angvel.y * self.Strain
		end

		if not self.ConstrainRoll then
			self.angvel.r = self.angvel.r + math_AngleDifference(ang.r, self.ang.r)
			self.ang.r = math_AngleDifference(self.ang.r, self.angvel.r * -speed)
			self.angvel.r = self.angvel.r * self.Strain
		end
	else
		self.ang = ang
	end
end

function PART:OnThink()
	self.pos.x = check_num(self.pos.x)
	self.pos.y = check_num(self.pos.y)
	self.pos.z = check_num(self.pos.z)

	self.ang.p = check_num(self.ang.p)
	self.ang.y = check_num(self.ang.y)
	self.ang.r = check_num(self.ang.r)
end

BUILDER:Register()