AnimationSWEP = AnimationSWEP or {}

local anim_table = {}

if CLIENT then
	hook.Add("mg_vars_update", "AnimationSWEP.Add", function(ply, name, new, old)
		if name != "animationStatus" or !new then return end
		anim_table[ply] = true

		ply.oldAnimationModel = ply:GetModel()
	end)

	local function ResetBones(ply)
		for i=1, ply:GetBoneCount() - 1 do
			ply:ManipulateBoneAngles(i, angle_zero)
		end
	end

	local function ApplyAnimation(ply, target, class, rem)
		local ply_tb = ply:GetTable()

		if ply_tb.oldAnimationModel != ply:GetModel() then
			ply_tb.animationSWEPAngle = 1
			ply_tb.oldAnimationModel = ply:GetModel()

			ResetBones(ply)
		end

		if ply_tb.animationSWEPAngle != target then
			ply_tb.animationSWEPAngle = Lerp(RealFrameTime() * 5, ply_tb.animationSWEPAngle, target)
			if math.Round(ply_tb.animationSWEPAngle, 3) == target then
				ply_tb.animationSWEPAngle = target
			end
		end

		if ply_tb.animationSWEPAngle != target then
			local oldanim = ply_tb.oldAnimationClass
			if oldanim and oldanim != "" and oldanim != class and AnimationSWEP.GestureAngles[oldanim] then
				for boneName, angle in pairs(AnimationSWEP.GestureAngles[oldanim]) do
					if boneName and angle then
						local bone = ply:LookupBone(boneName)
						if bone then
							ply:ManipulateBoneAngles(bone, angle * 0)
						end
					end
				end
			end
		end

		ply_tb.oldAnimationClass = ply:GetMGVar("animationClass", "")

		if AnimationSWEP.GestureAngles[class] then
			for boneName, angle in pairs(AnimationSWEP.GestureAngles[class]) do
				if boneName and angle then
					local bone = ply:LookupBone(boneName)
					if bone then
						ply:ManipulateBoneAngles(bone, angle * (ply_tb.animationSWEPAngle - 1))
					end
				end
			end
		end

		if rem and ply_tb.animationSWEPAngle == target then
			anim_table[ply] = nil
		end
	end

	local function Think()
		for ply in pairs(anim_table) do
			if !IsValid(ply) then
				anim_table[ply] = nil
				continue
			end

			local animationClass = ply:GetMGVar("animationClass", "")
			if animationClass != "" then
				ply.animationSWEPAngle = ply.animationSWEPAngle or 1

				if ply:GetMGVar("animationStatus", false) then
					ApplyAnimation(ply, 2, animationClass)
				else
					ApplyAnimation(ply, 1, animationClass, true)
				end
			else
				ply.animationSWEPAngle = 1 -- Hard reset start
				ply.oldAnimationModel = nil

				ResetBones(ply)

				anim_table[ply] = nil -- Hard reset end
			end
		end
	end
	hook.Add("Think", "AnimationSWEP.Think", Think)
else
	local function VelocityIsHigher(ply, value)
		local vel = ply:GetVelocity()
		local x, y, z = math.abs(vel.x), math.abs(vel.y), math.abs(vel.z)

		if x > value or y > value or z > value then
			return true
		else
			return false
		end
	end

	local function Think()
		for ply in pairs(anim_table) do
			if !IsValid(ply) then
				anim_table[ply] = nil
				continue
			end
			if ply:GetMGVar("animationStatus", false) then
				local deactivateOnMove = ply.deactivateOnMove or math.huge

				if VelocityIsHigher(ply, deactivateOnMove) or ply:KeyDown(IN_DUCK) then
					AnimationSWEP:SetCrossing(ply, false)
					continue
				end
			else
				anim_table[ply] = nil
			end
		end
	end
	hook.Add("Think", "AnimationSWEP.Think", Think)

	function AnimationSWEP:SetCrossing(ply, crossing, class, deactivateOnMove)
		if crossing then
			ply:SetMGVar("animationStart", CurTime())
			ply:SetMGVar("animationStatus", true)

			if class then
				ply:SetMGVar("animationClass", class)
			end

			ply.deactivateOnMove = deactivateOnMove

			anim_table[ply] = true
		else
			ply:SetMGVar("animationStart", 0)
			ply:SetMGVar("animationStatus", false)

			ply.deactivateOnMove = nil
		end
	end
end