local module = {}

local function targetHinge(target,hinge,stationary,angle,negative)
	local attachment = stationary:FindFirstChildWhichIsA("Attachment")

	local o = attachment.WorldCFrame:PointToObjectSpace(target)

	local x,y,z = CFrame.lookAlong(attachment.WorldCFrame.Position,attachment.WorldCFrame.UpVector,attachment.WorldCFrame.RightVector):ToObjectSpace(
		CFrame.lookAt(
			attachment.WorldCFrame.Position,
			attachment.WorldCFrame:PointToWorldSpace(Vector3.new(0,o.Y,o.Z)*negative)
		)):ToEulerAnglesYXZ()
	hinge.TargetAngle = -math.deg(y) + angle
end

function module.new(this,servo,context)
	this.scale = servo:GetExtentsSize().Magnitude
	this.hinge = _G.GetInstance(servo,"HingeConstraint")
	this.binding = _G.GetInstance(servo,"Binding")   
	this.inverted = servo:FindFirstChild("Inverted")
	this.offset = servo:FindFirstChild("Offset")
	this.range = servo:FindFirstChild("Angle")
	this.target = servo:FindFirstChild("Target")
	this.torque = servo:FindFirstChild("Torque")
	return this
end

local begin = tick()
module.run = _G.Protect(function(this,servo,context)
	local shouldInvert = 1
	local shouldOffset = 0
	local pitchTrim = 0
	local angle = 30
	if context.seat and context.seat:FindFirstChild("PitchTrim") then pitchTrim = context.seat.PitchTrim.Value end
	if this.inverted and this.inverted.Value then shouldInvert = -1 end
	if this.offset and this.offset.Value then shouldOffset = this.offset.Value + (this.starter or 0) end
	if this.range and this.range.Value then angle = this.range.Value end
	if this.torque and this.torque.Value then this.hinge.MotorMaxTorque = this.torque.Value * ((servo:GetAttribute("Health") or 1) / (servo:GetAttribute("MaxHealth") or 1)) end
	local control = (this.control or context.controls[this.binding.Value])
	if this.target and this.target.Value then
		this.hinge.LimitsEnabled = false
		if context.cursor then
			targetHinge(context.cursor,this.hinge,this.hinge.Attachment1.Parent,this.offset.Value,shouldInvert)
		end
	elseif this.binding.Value == "auto" and context.seat then
		local pos = context.seat:GetPivot():ToObjectSpace(this.hinge.Attachment1.WorldCFrame)
		local dir = pos.RightVector
		pos = pos.Position.Unit
		this.hinge.LimitsEnabled = false
		this.hinge.TargetAngle = ( (
				-context.controls.pitch*dir.x*pos.Z
				-context.controls.yaw*dir.y*pos.Z
				+context.controls.roll*dir.x*pos.X
			) * angle + pitchTrim*dir.x*pos.Z
		) * shouldInvert + shouldOffset
	elseif control then
		if this.binding.Value == "pitch" then shouldOffset = shouldOffset + pitchTrim end
		this.hinge.LimitsEnabled = true
		this.hinge.TargetAngle = control * angle * shouldInvert + shouldOffset
	end
	local distance = (this.hinge.Attachment1.WorldCFrame.Position - this.hinge.Attachment1.WorldCFrame.Position).Magnitude
	if distance < 2 then 
		context.replicate(this,servo,module,this.hinge.TargetAngle,this.hinge.LimitsEnabled)
		return true
	else
		context.replicate(this,servo,module,nil,nil)
		return false
	end
end)

module.replication_delta = 0.25
function module.replicate(this,servo,context,angle)
	if not angle then 
		this.hinge:Destroy()
	else
		this.hinge.TargetAngle = angle
	end
end

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

module.manifest = {
	"FlatServoRoll",
	"FlatServoPitch",
	"Servo",
	"ControlSheet",
	"NanoControlSheet",
	"MetalServo"
}

return module
