local module = {}

local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local bias = 0.2

local propeller_power_mod = 0.1
local propeller_cut_mod = 5/2
local propeller_reaction_mod = 0.05

game.Workspace.CompensateDensity.Changed:Connect(function()
	dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)

_G.Events.Command.Event:Connect(function(message)
	local value = message:match("setprop%((.-)%)")
	if not value then return end
	if tonumber(value) then 
		game.Workspace.CompensateDensity = tonumber(value)
		_G.Events.Notification:Fire("Set Prop Density","PropBias.Value = "..value)
	else
		_G.Events.Notification:Fire("Set Prop Density","Invalid number.")
	end
end)

local function setup(this,propeller,context)
	local angle = _G.GetValue(propeller,"Trim",30)
	for _,child in pairs(propeller:GetChildren()) do
		if child.Name == "Blade" then
			local welds = child:GetJoints()
			local joint = child:FindFirstChild("Weld")
			if not child:FindFirstChild("Weld") then
				local c0 = propeller.PrimaryPart.CFrame:ToObjectSpace(child.CFrame)
				joint = Instance.new("Motor6D")
				joint.Name = "Weld"
				joint.Parent = child
				joint.Part0 = propeller.PrimaryPart
				joint.Part1 = child
				joint.C0 = c0
			end
			local flip = 0
			if angle < 0 then flip = 180 end
			local axis = (propeller:GetAttribute("Axis") or Vector3.new(0,0,1)) * (math.rad(-angle+flip))
			local rotation = CFrame.Angles(axis.X,axis.Y,axis.Z)
			joint.C1 = rotation
			for _,weld in pairs(welds) do if weld:IsA("WeldConstraint") then weld:Destroy() end end
		end
	end
end

function module.new(this,propeller,context)
	local size = propeller:GetExtentsSize()
	this.scale = size.X * size.Z * 1.5
	this.length = size.X
	this.power = _G.GetValue(propeller,"Force",1000) * this.scale * 0.01
	this.direction = _G.GetValue(propeller,"Direction",CFrame.identity)
	this.rotor = propeller:FindFirstChild("Spin") or propeller:FindFirstChild("Hinge")
	this.trim = _G.GetValue(propeller,"Trim",30)
	this.blur = propeller:FindFirstChild("Blur")
	if not this.rotor then error("Missing rotor") end
	this.force = _G.GetInstance(propeller,"VectorForce")
	this.sound = _G.GetInstance(this.rotor,"PropSound")
	this.sound:Play()
	setup(this,propeller,context)
	return this
end

function module.update(this,propeller,context)
	print("upd")
	setup(this,propeller,context)
end

function module.idle(this,engine) return true end

local a = 300
local function limit(f)
	return math.tanh(f/a)*a
end

local begin = tick()
module.run = _G.Protect(function(this,propeller,context)
	local angular_velocity = ((this.rotor.CFrame.Rotation * this.direction):PointToObjectSpace(this.rotor.AssemblyAngularVelocity)).X
	local linear_velocity = angular_velocity * math.sin(math.rad(this.trim))
	local air_velocity = this.rotor.CFrame.Rotation:PointToObjectSpace(this.rotor.AssemblyLinearVelocity).X
	local real_velocity = limit((linear_velocity * propeller_cut_mod * this.length) - air_velocity)
	local rpm = (angular_velocity/(math.pi*2))*60
	local multiplier = (this.sound:GetAttribute("Multiplier") or 1) * 0.01
	this.force.Force = Vector3.new(0,0,-real_velocity*this.power*propeller_power_mod*dens)
	this.rotor:ApplyAngularImpulse(
		(this.rotor.CFrame.Rotation * this.direction).RightVector
			* -angular_velocity
			* math.cos(math.rad(this.trim))
			* propeller_reaction_mod)
	context.replicate(this,propeller,module,math.abs(rpm*dens*multiplier))
	return true
end)

module.replication_delta = 0.1
function module.replicate(this,propeller,context,value)
	this.sound.PlaybackSpeed = math.clamp(value,0.1,1.5)
	this.sound.Volume = math.clamp(value,0,this.sound:GetAttribute("Max") or 1)
	if this.blur then 
		this.blur.Transparency = math.clamp(1-value,0.3,1)
	end
	for _,blade in pairs(propeller:GetChildren()) do
		if blade.Name == "Blade" then
			blade.Transparency = math.clamp(value,0,0.7)
		end
	end
end

module.messages = {
	trim = function(this,object,context,value)
		
	end
}
module.messages.default = module.messages.trim

module.manifest = {
	"Propeller",
	"Turbine",
	"LargePropeller",
	"BiPropeller",
	"Jet"
}

return module
