local module = {}

local debugWings = game.Workspace.DebugWings.Value

local space = game.Workspace.Objects
local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local ndelta = 1/60
local function drag(v,r)
	return v * r
end
local rnd = function(vec) return Vector3.new(math.round(vec.X),math.round(vec.Y),math.round(vec.Z)) end 
local a = math.abs
local ln = math.log
local debwings = {}
local function deb(wing,force)
	local part = debwings[wing]
	if not part then
		part = Instance.new("Part")
		part.Name = "WingDebug"
		part.Parent = game.Workspace.Hidden
		part.Color = Color3.new(0.152941, 0.764706, 0.0588235)
		part.Material = Enum.Material.Neon
		part.Anchored = true
		part.CanCollide = false
		part.Transparency = 0.8
		part.Size = Vector3.new(0.25,0.25,2) -- we determine the wing's strength and do math by its size, the bigger, the more lift
		debwings[wing] = part
	end
	part.Transparency = 0.5
	part.CFrame = CFrame.lookAt(wing.Position,wing.Position+force)
	part.Size = Vector3.new(0.25,0.25,force.Magnitude)
	part.CFrame = part.CFrame + part.CFrame.LookVector * part.Size.Z * 0.5
end

require(game.ReplicatedFirst.ReadyModule)("WingCommands")
local process = true

game.Workspace.DebugWings.Changed:Connect(function()
	debugWings = game.Workspace.DebugWings.Value
	if not debugWings then
		for _,item in pairs(debwings) do
			item:Destroy()
		end
		debwings = {}
	end
end)
game.Workspace.CompensateDensity.Changed:Connect(function()
	dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)

function module.new(this,wing,context)
	--local mul = wing:GetAttribute("AeroMultiplier") or 1
	this.bounds = {}
	this.volume = {}
	if wing:IsA("Model") then
		this.aero = {}
		for _,part in pairs(wing:GetChildren()) do
			if part:HasTag("IsAerodynamic") then
				table.insert(this.aero,part)
			end
		end
	else
		this.aero = {wing}
	end
	for _,part in pairs(this.aero) do
		local s = part.Size
		this.bounds[part] = Vector3.new(s.Y*s.Z,s.X*s.Z,s.Y*s.X)-- * mul
		this.volume[part] = s.X*s.Y*s.Z
	end
	return this
end

local visc = 0.75
local commands = require(_G.Modules.CommandsModule)
commands.new("viscosity (.+)","viscosity <number>: change air viscosity coefficient",function(n)
	visc = tonumber(n)
end)

module.run = @native
function(this,wing,context)
	local bounds = this.bounds
	local volume = this.volume
	if not process then return true end
	if not this.aero then print(this,wing) end -- BECAUSE WHAT THE BLOODY HELL IS THIS!?!??
	for _,wing in pairs(this.aero) do
		--if not (wing.Parent == space or wing.Parent.Parent == space) and v.player then return end
		local s = bounds[wing]
		if not s then print(this.aero,bounds,wing,s) end
		local p = wing.Position
		local v = wing:GetVelocityAtPosition(p) -- velocity of wing
		local d = dens * 20
		local fmass = 2 -- mass of air generally
		--[[if p.Y < 0 then 
			d = d * 5
			fmass = 100 -- mass of water
		end]]
		v = v - Vector3.new(0,
			0--0.15 * v.Magnitude -- implement upthrust due to pressure difference across an airfoil (lift)
		,0)
		local lv = wing.CFrame.Rotation:PointToObjectSpace(v) -- velocity for each wing face
		--mv = lv.Magnitude + 0.0001 -- small number to stop zero error
 
		-- "math", YEAH REALLY? i didnt REALISE! I don't even know what this does...
		--lp = Vector3.new(lv.X/(a(scale.X*d*lv.X)+1)-lv.X,lv.Y/(a(scale.Y*d*lv.Y)+1)-lv.Y,lv.Z/(a(scale.Z*d*lv.Z)+1)-lv.Z) -- math
		local drag = Vector3.new(lv.X*s.X,lv.Y*s.Y,lv.Z*s.Z)
		-- implement anti-viscosity (may the riemann sum gods be with the low end machines
		local lp = (drag) * context.delta*d*math.tanh(v.Magnitude*0.0075)

		local r = (wing.CFrame.Rotation:PointToWorldSpace(lp))
		
		r = r + Vector3.yAxis * fmass * volume[wing] * 2 * context.delta -- implement upthrust due to pressure difference in a fluid column (floating)
		
		wing:ApplyImpulseAtPosition(r,wing.Position)

		if debugWings then deb(wing,r) end
	end
	return true
end

module.manifest = {
	predicate = function(instance)
		return instance:HasTag("IsAerodynamic") or instance.Name:match("Sheet") -- LAST CONDITION IS NOT NEEDED
	end,
}

return module
