--
-- AutoLoadWood for FS25
-- @author:    	Marhu, kenny456 - FS19/FS22/FS25 conversion (kenny456@seznam.cz)
-- @history:	v1.0 - 2018-12-25 - converted to FS19
--								  - added function to move loading position FWD/BWD depending on logs length
--				v1.1 - 2018-12-28 - fixed multiplayer errors on dedi server
--				v1.2 - 2018-03-09 - -added new 15m and 20m trailers
--									-fixed showing controls when attached to dolly
--									-Autoload script edited (logs are now sorted to the piles)
--				v1.0.0.0 - 2019-12-16 - first release for modHub
--				v1.1.0.0 - 2020-12-14 - fixed conflict with Vehicle Straps mod
--									  - improve stability of the trailers
--									  - fixed issue when not all logs are unloaded from trailer
--									  - count and total mass of loaded logs are displayed in F1 menu
--				2021-12-03 - converted to FS22
--				v1.1.0.0 - 2022-04-17 - fixed issue when implement icon not turn blue when autoload is active since game patch 1.3.1
--				v1.2.0.0 - 2022-11-26 - Fixed issue when trailer slides to the side (full loaded)
--				2024-11-16 - converted to FS25
AutoLoadWood = {};
AutoLoadWood.confDir = getUserProfileAppPath().. "modSettings/AutoLoadWood/";
local modName = g_currentModName

function AutoLoadWood:getTexts(i,text)
	if g_currentMission.AutoLoadWood == nil then
		g_currentMission.AutoLoadWood = {}
		g_currentMission.AutoLoadWood.texts = {}
	end
	g_currentMission.AutoLoadWood.texts[i] = text
end

function AutoLoadWood.prerequisitesPresent(specializations)
    return true
end
function AutoLoadWood.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "dynamicMountTriggerCallback", AutoLoadWood.dynamicMountTriggerCallback)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsTurnedOn", AutoLoadWood.getIsTurnedOn)
end

function AutoLoadWood.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "toggleState", AutoLoadWood.toggleState)
	SpecializationUtil.registerFunction(vehicleType, "toggleLoad", AutoLoadWood.toggleLoad)
	SpecializationUtil.registerFunction(vehicleType, "toggleUnload", AutoLoadWood.toggleUnload)
	SpecializationUtil.registerFunction(vehicleType, "updateValues", AutoLoadWood.updateValues)
	SpecializationUtil.registerFunction(vehicleType, "updateLoad", AutoLoadWood.updateLoad)
	SpecializationUtil.registerFunction(vehicleType, "updateUnload", AutoLoadWood.updateUnload)
	--SpecializationUtil.registerFunction(vehicleType, "getIsTurnedOn", AutoLoadWood.getIsTurnedOn)
	SpecializationUtil.registerFunction(vehicleType, "saveToXml", AutoLoadWood.saveToXml)
	SpecializationUtil.registerFunction(vehicleType, "loadFromXml", AutoLoadWood.loadFromXml)
end
function AutoLoadWood:onRegisterActionEvents(isSelected, isOnActiveVehicle)
	local spec = self.spec_autoLoadWood
	if g_dedicatedServerInfo ~= nil then
		return
	end
	if spec.event_IDs == nil then
		spec.event_IDs = {}
	end
	if isSelected then
		local actions_RC1 = { InputAction.AUTOLOADWOODEW_TOGGLE_AUTO, InputAction.AUTOLOADWOODEW_TOGGLE_UNLOAD, InputAction.AUTOLOADWOODEW_TOGGLE_SIDE,
							InputAction.AUTOLOADWOODEW_TOGGLE_PILES, InputAction.AUTOLOADWOODEW_SWITCH_PILE, InputAction.AUTOLOADWOODEW_TOGGLE_LOAD_MARKER, InputAction.AUTOLOADWOODEW_TOGGLE_UNLOAD_MARKER,
							InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_MOVE, InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_SCALE, InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_ROTATE, InputAction.AUTOLOADWOODEW_TOGGLE_HELP}

		for _,actionName in pairs(actions_RC1) do
			local _, eventID = g_inputBinding:registerActionEvent(actionName, self, AutoLoadWood.actionCallback, true, true, false, true)
			spec.event_IDs[actionName] = eventID
			if g_inputBinding ~= nil and g_inputBinding.events ~= nil and g_inputBinding.events[eventID] ~= nil then
				if actionName == InputAction.AUTOLOADWOODEW_TOGGLE_AUTO or actionName == InputAction.AUTOLOADWOODEW_TOGGLE_UNLOAD or actionName == InputAction.AUTOLOADWOODEW_TOGGLE_SIDE then
					g_inputBinding.events[eventID].displayPriority = 1
				else
					g_inputBinding.events[eventID].displayPriority = 2
				end
				if actionName == InputAction.AUTOLOADWOODEW_TOGGLE_HELP then
					g_inputBinding:setActionEventTextVisibility(eventID, true)
				elseif actionName == InputAction.RC_TOGGLE or actionName == InputAction.RC_TOGGLE_ALL then
					g_inputBinding:setActionEventTextVisibility(eventID, false)
				else
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				end
			end
		end
	end
end

function AutoLoadWood.registerEventListeners(vehicleType)
	for _,n in pairs( { "onLoad","onPostLoad","saveToXMLFile","onUpdate","onDraw","onRegisterActionEvents","saveToXml","loadFromXml","onReadStream","onWriteStream" } ) do
		SpecializationUtil.registerEventListener(vehicleType, n, AutoLoadWood)
	end
end

function AutoLoadWood:onLoad(savegame)
	self.spec_autoLoadWood = {}
	local spec = self.spec_autoLoadWood
	local xmlFile = self.xmlFile
	spec.event_IDs = {}
	
	spec.searchSizeY = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWood#SizeY"),5);
	spec.searchSizeZ = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWood#SizeX"),10);
	spec.autoLoadSpeed = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.AutoLoadWood#Speed"),500);
	spec.autoLoadElaps = 0;
	spec.dynamicMountTrigger = I3DUtil.indexToObject(self.components, '0>0|8|1')
	spec.autoLoadWood = I3DUtil.indexToObject(self.components, '0>0|8|0')
	addTrigger(spec.dynamicMountTrigger, "dynamicMountTriggerCallback", self)
	
	local x,y,z = getTranslation(spec.dynamicMountTrigger)
	local key = "vehicle.AutoLoadWood.fillPose";
	local fillPoseXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),0);
	local fillPoseYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),2);
	local fillPoseZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),0);
	
	local key = "vehicle.AutoLoadWood.unloadPose";
	local unloadPoseXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),0);
	local unloadPoseYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),5);
	local unloadPoseZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),0);
		
	spec.fillPoseNodeX = x + fillPoseXOff
	spec.fillPoseNodeY = 1.4 + fillPoseYOff
	spec.fillPoseNodeZ = z + fillPoseZOff
	spec.fillPoseRoot = createTransformGroup("fillPoseRoot");
	spec.fillPoseNode = createTransformGroup("fillPoseNode");
	link(spec.autoLoadWood,spec.fillPoseRoot);
	link(spec.fillPoseRoot,spec.fillPoseNode);
	setTranslation(spec.fillPoseRoot, spec.fillPoseNodeX, spec.fillPoseNodeY, spec.fillPoseNodeZ)
	setRotation(spec.fillPoseRoot,math.rad(-90),0,0)
	
	local linkNode = getParent(getParent(spec.dynamicMountTrigger))
	spec.unloadPoseRoot = {	createTransformGroup("unloadPoseRoot1"),
							createTransformGroup("unloadPoseRoot2")};
	spec.unloadPoseNode = {	createTransformGroup("unloadPoseNode1"),
								createTransformGroup("unloadPoseNode2")};
	spec.unloadPoseRootX = 2
	spec.unloadPoseRootY = 0
	spec.unloadPoseRootZ = 0
	for i = 1, 2 do
		link(linkNode, spec.unloadPoseRoot[i]);
		setTranslation(spec.unloadPoseRoot[i],spec.unloadPoseRootX,spec.unloadPoseRootY,spec.unloadPoseRootZ)
		setRotation(spec.unloadPoseRoot[i],math.rad(0),math.rad(0),0)
		spec.unloadPoseRootX = spec.unloadPoseRootX * -1;
	end
	
	spec.unloadPoseNodeX =  unloadPoseXOff
	spec.unloadPoseNodeY  = unloadPoseYOff
	spec.unloadPoseNodeZ  = unloadPoseZOff
	for i = 1, 2 do					
		link(spec.unloadPoseRoot[i], spec.unloadPoseNode[i]);
		setTranslation(spec.unloadPoseNode[i], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
		setRotation(spec.unloadPoseNode[i],math.rad(-90),0,0)
		spec.unloadPoseNodeX = spec.unloadPoseNodeX * -1;
	end
		
	spec.loadMarker = I3DUtil.indexToObject(self.components, '0>0|6')
	spec.unloadMarker = I3DUtil.indexToObject(self.components, '0>0|7')
	if spec.loadMarker ~= nil then
		spec.markerX, spec.markerY, spec.markerZ = getTranslation(spec.loadMarker)
		setVisibility(spec.loadMarker, false);
		link(spec.fillPoseRoot, spec.loadMarker);
		setRotation(spec.loadMarker,math.rad(90),0,math.rad(90))
		setTranslation(spec.loadMarker,0,0,-3.1)
	end
	if spec.unloadMarker ~= nil then
		spec.markerX, spec.markerY, spec.markerZ = getTranslation(spec.unloadMarker)
		setVisibility(spec.unloadMarker, false);
		link(spec.unloadPoseRoot[1], spec.unloadMarker);
	end
	
	if self.isServer then
		local key = "vehicle.AutoLoadWood.woodTrigger";
		local woodTriggerXOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#xOff"),1.2);
		local woodTriggerYOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#yOff"),-3);
		local woodTriggerZOff = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#zOff"),1.5);

		local key = "vehicle.AutoLoadWood.limitToLen";
		spec.loadMinLen = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#minLen"),2.5);
		spec.loadMaxLen = Utils.getNoNil(getXMLFloat(xmlFile.handle, key .. "#maxLen"),20);
		spec.loadMaxAtt = Utils.getNoNil(getXMLInt(xmlFile.handle, key .. "#delimbOk"),0);
		
		local linkNode = getParent(spec.dynamicMountTrigger)
		local x,y,z = getTranslation(spec.dynamicMountTrigger)
		
		spec.woodTrigger = {createTransformGroup("woodTrigger1"),
							createTransformGroup("woodTrigger2"),
							createTransformGroup("woodTrigger3"),
							createTransformGroup("woodTrigger4")};
		
		local rot = math.rad(90)
		for i = 1, 4 do					
			link(linkNode, spec.woodTrigger[i]);
			setTranslation(spec.woodTrigger[i], x + woodTriggerXOff, y + woodTriggerYOff, z + woodTriggerZOff)
			setRotation(spec.woodTrigger[i], 0, rot, 0);
			woodTriggerZOff = woodTriggerZOff * -1;
			if i == 2 then 
				woodTriggerXOff = woodTriggerXOff * -1;
				rot = math.rad(-90)
			end
		end
	end
	self.onDeleteBackup = self.delete
	self.delete = Utils.prependedFunction(self.delete, AutoLoadWood.onDeleteAL)
	--DebugUtil.printTableRecursively(self.spec_tensionBelts,"-",0,1)
end
function AutoLoadWood:onPostLoad(savegame)
	local spec = self.spec_autoLoadWood
	local xmlFile = self.xmlFile
	if spec.event_IDs == nil then
		spec.event_IDs = {}
	end
	spec.woodLoad = {};
	spec.autoload = false;
	spec.rightSide = false;
	spec.unload = false;
	spec.showLoadPos = false;
	spec.numPiles = 2;
	spec.pile = 1;
	spec.showUnloadPos = false;
	spec.unloadPos = spec.unloadPoseRootX
	spec.unloadScale = 1;
	spec.unloadRot = false;
	spec.trailer = Utils.getNoNil(getXMLFloat(xmlFile.handle, "vehicle.base.size#length"),15);
	spec.space = 0.3
	spec.rootShiftRotX = 6;
	spec.rootShiftRotXMax = 9;
	AutoLoadWood.showHelp = true
	spec.logsCount = 0
	spec.logsMass = 0
	local x,y,z = getCenterOfMass(self.rootNode)
	spec.massYOrig = y
	spec.massYLast = spec.massYOrig
	spec.mass = 0
	spec.count = 0
	spec.firstRun = true
	
	self:updateValues()
	self:updateLoad()
	self:updateUnload()
	
	if savegame ~= nil then
		local xmlFile = savegame.xmlFile
		local key = savegame.key.."."..modName..".AutoLoadWood"
		local rightSide = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#rightSide"), spec.rightSide);
		local showLoadPos = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#showLoadPos"), spec.showLoadPos);
		local showUnloadPos = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#showUnloadPos"), spec.showUnloadPos);
		local numPiles = Utils.getNoNil(getXMLInt(xmlFile.handle, key.."#numPiles"), spec.numPiles);
		local pile = Utils.getNoNil(getXMLInt(xmlFile.handle, key.."#pile"), spec.pile);
		local unloadPos = Utils.getNoNil(getXMLFloat(xmlFile.handle, key.."#unloadPos"), spec.unloadPos);
		local unloadScale = Utils.getNoNil(getXMLFloat(xmlFile.handle, key.."#unloadScale"), spec.unloadScale);
		local unloadRot = Utils.getNoNil(getXMLBool(xmlFile.handle, key.."#unloadRot"), spec.unloadRot);
		
		self:toggleState(autoload, rightSide, unload, true)
		self:toggleLoad(showLoadPos, numPiles, pile, true)
		self:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, true)
	end
	
	local configFile = AutoLoadWood.confDir .. "AutoLoadWoodConfig.xml";
	if fileExists(configFile) then
		AutoLoadWood.configXml = loadXMLFile("AutoLoadWood_XML", configFile);
		self:loadFromXml(self:getFullName());
	else
		createFolder(getUserProfileAppPath().. "modSettings/");
		createFolder(AutoLoadWood.confDir);
		AutoLoadWood.configXml = createXMLFile("AutoLoadWood_XML", configFile, "AutoLoadWoodConfig");
		self:saveToXml();
		saveXMLFile(AutoLoadWood.configXml);
	end
	if self.spec_vehicleStraps ~= nil then
		self.spec_vehicleStraps.modInitialized = false
		self.spec_vehicleStraps.modAllowed = false
		self.dynamicMountTriggerCallback = Utils.overwrittenFunction(self.dynamicMountTriggerCallback, AutoLoadWood.dynamicMountTriggerCallback)
	end
end
function AutoLoadWood:onDelete()
	local spec = self.spec_autoLoadWood
	if spec.dynamicMountTrigger ~= nil then
        removeTrigger(spec.dynamicMountTrigger)
    end
end
function AutoLoadWood:onDeleteAL()
	local spec = self.spec_autoLoadWood
	
	if spec.dynamicMountTrigger ~= nil then
		removeTrigger(spec.dynamicMountTrigger)
	end
end

function AutoLoadWood:saveToXMLFile(xmlFile, key)
	local spec = self.spec_autoLoadWood
	
	setXMLBool(xmlFile.handle, key.."#rightSide", spec.rightSide)
	setXMLBool(xmlFile.handle, key.."#showLoadPos", spec.showLoadPos)
	setXMLBool(xmlFile.handle, key.."#showUnloadPos", spec.showUnloadPos)
	setXMLInt(xmlFile.handle, key.."#numPiles", spec.numPiles)
	setXMLInt(xmlFile.handle, key.."#pile", spec.pile)
	setXMLFloat(xmlFile.handle, key.."#unloadPos", spec.unloadPos)
	setXMLFloat(xmlFile.handle, key.."#unloadScale", spec.unloadScale)
	setXMLBool(xmlFile.handle, key.."#unloadRot", spec.unloadRot)
end

function AutoLoadWood:loadFromXml()
	local spec = self.spec_autoLoadWood
    if AutoLoadWood.configXml == nil then
        return;
    end
	if getXMLBool(AutoLoadWood.configXml, "AutoLoadWoodConfig.showHelp") ~= nil then
		AutoLoadWood.showHelp = getXMLBool(AutoLoadWood.configXml, "AutoLoadWoodConfig.showHelp")
	end;
end
function AutoLoadWood:saveToXml()
	local spec = self.spec_autoLoadWood
    if AutoLoadWood.configXml == nil then
        return;
    end
	setXMLBool(AutoLoadWood.configXml, "AutoLoadWoodConfig.showHelp", AutoLoadWood.showHelp);
    saveXMLFile(AutoLoadWood.configXml);
end
function AutoLoadWood:onUpdate(dt, vehicle)
	local spec = self.spec_autoLoadWood
	
	local areBeltsFasten = false
	if self.spec_tensionBelts ~= nil then
		areBeltsFasten = self.spec_tensionBelts.fastenedAllBeltsState
	end
	--print('areBeltsFasten - '..tostring(areBeltsFasten))
	--local count = 0
	--local mass = 0
	if self:getIsActive() and (not areBeltsFasten or spec.firstRun) then
		spec.firstRun = false
		spec.count = 0
		spec.mass = 0
		for k, shape in pairs(spec.woodLoad) do
			if entityExists(shape) then
				spec.count = spec.count + 1
				spec.mass = spec.mass + getMass(shape)
			else
				spec.woodLoad[k] = nil
			end
		end
	end
	local massY = spec.massYOrig
	if spec.count > 0 and not areBeltsFasten then
		local k = 0
		if spec.trailer == 20 then
			k = -8.56
		elseif spec.trailer == 15 then
			k = -6.37
		elseif spec.trailer == 12 then
			k = -5.64
		end
		massY = (spec.mass / k) + spec.massYOrig
	end
	--print('count - '..spec.count)
	--print('mass - '..spec.mass)
	--print('massY - '..massY)
	--print('lastSpeed - '..self.lastSpeed)
	if self.lastSpeed < 0.011 then
		massY = spec.massYOrig
	end
	--print('massY - '..massY)
	if massY ~= spec.massYLast then
		--print('massY changing - '..massY)
		spec.massYLast = massY
		local x,y,z = getCenterOfMass(self.rootNode)
		setCenterOfMass(self.rootNode, x, massY, z)
	end
	if spec.autoload then
		spec.autoLoadElaps = spec.autoLoadElaps + dt;
		if spec.autoLoadElaps >= spec.autoLoadSpeed then
			spec.autoLoadElaps = 0;
			if self.isServer then
				if entityExists(spec.lastWoodShapes) and spec.woodLoad[spec.lastWoodShapes] == nil then
					if spec.pile + 1 <= spec.numPiles then
						spec.pile = spec.pile + 1
						spec.lastWoodShapes = 0
						self:toggleLoad(spec.showLoadPos, spec.numPiles, spec.pile)
					else
						--print('off')
						self:toggleState(false, spec.rightSide, spec.unload)
						return;
					end
				end;
				
				local triggerNum = (spec.rightSide and 2 or 0) + 1;
				
				local x,y,z = getWorldTranslation(spec.woodTrigger[triggerNum])
				local nx, ny, nz = localDirectionToWorld(spec.woodTrigger[triggerNum], 1, 0, 0)
				local yx, yy, yz = localDirectionToWorld(spec.woodTrigger[triggerNum], 0, 1, 0)
				local shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, spec.searchSizeY, spec.searchSizeZ)
				if shape == nil or not entityExists(shape) or getRigidBodyType(shape) ~= 2 or spec.badTree == shape then
					triggerNum = triggerNum + 1;
					x,y,z = getWorldTranslation(spec.woodTrigger[triggerNum])
					nx, ny, nz = localDirectionToWorld(spec.woodTrigger[triggerNum], 1, 0, 0)
					yx, yy, yz = localDirectionToWorld(spec.woodTrigger[triggerNum], 0, 1, 0)
					shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, spec.searchSizeY, spec.searchSizeZ)
				end	
				local fX,fY,fZ = getTranslation(spec.fillPoseNode)
				if fX < 0.5 then
					fX = fX + 0.5;
				else
					fX = -0.8;
						rotate(spec.fillPoseNode, 0,0, math.rad(180))
						spec.fXRot = not spec.fXRot
				end
				setTranslation(spec.fillPoseNode, fX, fY, fZ)
				if shape ~= nil and entityExists(shape) and getRigidBodyType(shape) == 2 then
					local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(shape)
					local maxSize = math.max(sizeX, math.max(sizeY, sizeZ)) --fcelsa
					local a, b, c = localToWorld(spec.woodTrigger[triggerNum], 0, (minY + maxY)*0.5, (minZ + maxZ)*0.5)
					local sx,sy,sz = getWorldTranslation(shape)
					local dist = MathUtil.vector3Length(a - sx, b - sy, c - sz)
					local nx, ny, nz = localDirectionToWorld(shape, 0, 1, 0)
					local lenBelow, lenAbove = getSplitShapePlaneExtents(shape, a, b, c, nx, ny, nz);
					dist = dist - lenBelow
					local diffX = (spec.maxLength - sizeX) * 0.5 * (spec.fXRot and -1 or 1)
					local wx,wy,wz = localToWorld(spec.fillPoseNode,0,-dist - (sizeX/2) - diffX,0)
					local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
					local nx, ny, nz = localDirectionToWorld(spec.fillPoseNode, 1, 0, 0)
					local yx, yy, yz = localDirectionToWorld(spec.fillPoseNode, 0, 1, 0)
					local x, y, z = worldDirectionToLocal(getParent(shape), nx, ny, nz)
					local xdir, ydir, zdir = worldDirectionToLocal(getParent(shape), yx, yy, yz)
					if numAttachments == nil then numAttachments = 0; end;
					if maxSize <= spec.loadMaxLen and maxSize >= spec.loadMinLen and numAttachments <= spec.loadMaxAtt --[[and maxSize <= (spec.maxLength + 0.2)]]  then
						removeFromPhysics(shape)
						setTranslation(shape,dx,dy,dz)
						setDirection(shape, x, y, z, xdir, ydir, zdir)
						addToPhysics(shape)
						spec.lastWoodShapes = shape
					else
						spec.badTree = shape
					end;
				end
			end
		end
	elseif spec.unload then
		spec.autoLoadElaps = spec.autoLoadElaps + dt;
		if spec.autoLoadElaps >= spec.autoLoadSpeed then
			spec.autoLoadElaps = 0;
			if self.isServer then
				local side = spec.rightSide and 2 or 1;
				local x,y,z = getWorldTranslation(spec.fillPoseNode)
				local uX,uY,uZ = getTranslation(spec.unloadPoseNode[side])
				local findWood = false;
				for k, shape in pairs(spec.woodLoad) do
					if entityExists(shape) then
						spec.shape = shape
						local sx,sy,sz = getWorldTranslation(shape)
						local dist = MathUtil.vector2Length(x - sx, z - sz)
						uX = math.abs(uX)
						if uX < spec.unloadPoseNodeX + spec.unloadShiftMax then
							uX = uX + 0.6
						else
							uX = spec.unloadPoseNodeX
						end
						setTranslation(spec.unloadPoseNode[side], (spec.rightSide and -1 or 1) * uX, uY, uZ)
						if getChild(shape,'logCenter') == 0 then
							local x,y,z = getWorldTranslation(shape)
							local nx,ny,nz = localDirectionToWorld(shape, 0,1,0);
							local lenBelow, lenAbove = getSplitShapePlaneExtents(shape, x,y,z,nx,ny,nz);
							local sizeX, sizeY, sizeZ = getSplitShapeStats(shape)
							local logCenter = createTransformGroup("logCenter")
							link(shape, logCenter)
							setTranslation(logCenter, 0,-lenBelow+(sizeX/2),0)
							spec.logCenter = logCenter
						else
							local logCenter = getChild(shape,'logCenter')
							spec.logCenter = logCenter
						end
						if dist < 100 then
							local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist,0)
							local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
							local nx, ny, nz = localDirectionToWorld(spec.unloadPoseNode[side], 1, 0, 0)
							local yx, yy, yz = localDirectionToWorld(spec.unloadPoseNode[side], 0, 1, 0)
							local x, y, z = worldDirectionToLocal(getParent(shape), nx, ny, nz)
							local xdir, ydir, zdir = worldDirectionToLocal(getParent(shape), yx, yy, yz)
							removeFromPhysics(shape)
							setTranslation(shape,dx,dy,dz)
							setDirection(shape, x, y, z, xdir, ydir, zdir)
							addToPhysics(shape)
							local x,y,z = getWorldTranslation(spec.logCenter)
							local a,b,c = getWorldTranslation(spec.unloadPoseNode[side])
							local dist2 = MathUtil.vector2Length(x - a, z - c)
							if dist2 > 0 then
								local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist+dist2,0)
								local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
								setTranslation(shape,dx,dy,dz)
							end
							local x,y,z = getWorldTranslation(spec.logCenter)
							local a,b,c = getWorldTranslation(spec.unloadPoseNode[side])
							local dist3 = MathUtil.vector2Length(x - a, z - c)
							if dist3 > 0 then
								local wx,wy,wz = localToWorld(spec.unloadPoseNode[side],0,-dist+dist2-dist3,0)
								local dx,dy,dz = worldToLocal(getParent(shape),wx,wy,wz)
								setTranslation(shape,dx,dy,dz)
							end
							self:dynamicMountTriggerCallback(spec.dynamicMountTrigger, shape, false, true, false, shape)
							if self.spec_timberTrailerInfo ~= nil and self.spec_timberTrailerInfo.logTrigger ~= nil then
								self:logTriggerCallback(self.spec_timberTrailerInfo.logTrigger, shape, false, true, false, shape)
							end
							findWood = true;
							break;
						else
							spec.woodLoad[k] = nil
						end
					else
						spec.woodLoad[k] = nil
					end
				end
				if findWood == false then
					self:toggleState(spec.autoload, spec.rightSide, false)
					setTranslation(spec.unloadPoseNode[side], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
				end;
			end;
		end;
	end
	if self:getIsActiveForInput() then
		if spec.event_IDs ~= nil and g_dedicatedServerInfo == nil and self.isClient then
			for actionName,eventID in pairs(spec.event_IDs) do
				g_inputBinding:setActionEventActive(eventID, true)
				if actionName == InputAction.AUTOLOADWOODEW_TOGGLE_AUTO then
					g_inputBinding:setActionEventText(eventID, spec.autoload and g_i18n:getText('AUTOLOADWOODEW_STOP_AUTOLOAD') or g_i18n:getText('AUTOLOADWOODEW_START_AUTOLOAD'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_UNLOAD then
					g_inputBinding:setActionEventText(eventID, spec.unload and g_i18n:getText('AUTOLOADWOODEW_STOP_UNLOAD') or g_i18n:getText('AUTOLOADWOODEW_START_UNLOAD'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_SIDE then
					g_inputBinding:setActionEventText(eventID, spec.rightSide and g_i18n:getText('AUTOLOADWOODEW_SWITCH_LEFT') or g_i18n:getText('AUTOLOADWOODEW_SWITCH_RIGHT'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_LOAD_MARKER then
					g_inputBinding:setActionEventText(eventID, spec.showLoadPos and g_i18n:getText('AUTOLOADWOODEW_HIDE_LOAD_MARKER') or g_i18n:getText('AUTOLOADWOODEW_SHOW_LOAD_MARKER'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_UNLOAD_MARKER then
					g_inputBinding:setActionEventText(eventID, spec.showUnloadPos and g_i18n:getText('AUTOLOADWOODEW_HIDE_UNLOAD_MARKER') or g_i18n:getText('AUTOLOADWOODEW_SHOW_UNLOAD_MARKER'))
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_HELP then
					g_inputBinding:setActionEventText(eventID, AutoLoadWood.showHelp and g_i18n:getText('AUTOLOADWOODEW_HIDE_HELP') or g_i18n:getText('AUTOLOADWOODEW_SHOW_HELP'))
					g_inputBinding:setActionEventTextVisibility(eventID, true)
				elseif actionName == InputAction.AUTOLOADWOODEW_TOGGLE_PILES then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOODEW_TOGGLE_PILES')..' : '..spec.numPiles..' (max. '..spec.maxLength..' m)')
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_SWITCH_PILE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOODEW_SWITCH_PILE')..' : '..spec.pile)
					g_inputBinding:setActionEventTextVisibility(eventID, AutoLoadWood.showHelp)
				elseif actionName == InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_MOVE then
					g_inputBinding:setActionEventText(eventID,  g_i18n:getText('input_AUTOLOADWOODEW_UNLOAD_MARKER_MOVE')..' : '..math.ceil(spec.unloadRot and spec.unloadPos or spec.unloadPos-1)..'/'..math.ceil(spec.unloadRot and spec.rootShiftRotXMax or 4)..' m')
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos)
				elseif actionName == InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_SCALE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOODEW_UNLOAD_MARKER_SCALE')..' : '..math.ceil(spec.unloadScale*4.8)..'/'..math.ceil(2*4.8)..' m')
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos)
				elseif actionName == InputAction.AUTOLOADWOODEW_UNLOAD_MARKER_ROTATE then
					g_inputBinding:setActionEventText(eventID, g_i18n:getText('input_AUTOLOADWOODEW_UNLOAD_MARKER_ROTATE'))
					g_inputBinding:setActionEventTextVisibility(eventID, spec.showUnloadPos and AutoLoadWood.showHelp)
				end
			end
			if spec.count > 0 then
				g_currentMission:addExtraPrintText(g_i18n:getText('AUTOLOADWOODEW_COUNT').." "..spec.count.." ("..string.format('%.1f', spec.mass).." t)")
			else
				g_currentMission:addExtraPrintText(g_i18n:getText('AUTOLOADWOODEW_NO_LOGS'))
			end
		end
	end
end
function AutoLoadWood:onDraw()
	local spec = self.spec_autoLoadWood
end
function AutoLoadWood:toggleState(autoload, rightSide, unload, noEventSend)
	local spec = self.spec_autoLoadWood
	
	AutoLoadWoodToggleEvent.sendEvent(self, autoload, rightSide, unload, noEventSend)
	
	if rightSide ~= spec.rightSide then
		unlink(spec.unloadMarker)
		rotate(spec.unloadMarker, 0,math.rad(180),0)
		local mX,mY,mZ = getTranslation(spec.unloadMarker)
		mX = mX * -1
		link(spec.unloadPoseRoot[rightSide and 2 or 1], spec.unloadMarker);
	end
	
	spec.lastWoodShapes = 0;
	spec.autoload = autoload;
	spec.rightSide = rightSide;
	spec.unload = unload;
end;
function AutoLoadWood:toggleLoad(showLoadPos, numPiles, pile, noEventSend)
	local spec = self.spec_autoLoadWood
	
	AutoLoadWoodToggleLoadEvent.sendEvent(self, showLoadPos, numPiles, pile, noEventSend)
	
	spec.showLoadPos = showLoadPos
	spec.numPiles = numPiles
	spec.pile = pile
	self:updateValues()
	self:updateLoad()
	self:updateUnload()
end
function AutoLoadWood:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	local spec = self.spec_autoLoadWood
	
	AutoLoadWoodToggleUnloadEvent.sendEvent(self, showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	
	if unloadPos ~= spec.unloadPos or unloadRot ~= spec.unloadRot then
		setTranslation(spec.unloadPoseNode[spec.rightSide and 2 or 1], spec.unloadPoseNodeX, spec.unloadPoseNodeY, spec.unloadPoseNodeZ)
	end
	
	spec.showUnloadPos = showUnloadPos
	spec.unloadPos = unloadPos
	spec.unloadScale = unloadScale
	spec.unloadRot = unloadRot
	self:updateValues()
	self:updateUnload()
end
function AutoLoadWood:actionCallback(actionName, keyStatus, arg4, arg5, arg6)
	local spec = self.spec_autoLoadWood
	if keyStatus > 0 then
		if actionName == 'AUTOLOADWOODEW_TOGGLE_AUTO' then
			self:toggleState(not spec.autoload, spec.rightSide, false)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_UNLOAD' then
			self:toggleState(false, spec.rightSide, not spec.unload)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_SIDE' then
			self:toggleState(false, not spec.rightSide, false)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_PILES' then
			if spec.numPiles + 1 <= spec.maxPiles then
				spec.numPiles = spec.numPiles + 1
			else
				spec.numPiles = 1
			end
			self:toggleLoad(spec.showLoadPos, spec.numPiles, 1)
			self:toggleState(false, spec.rightSide, false)
			if spec.unloadRot then
				spec.unloadPos = spec.rootShiftRotX
			end
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOODEW_SWITCH_PILE' then
			if spec.pile + 1 <= spec.numPiles then
				spec.pile = spec.pile + 1
			else
				spec.pile = 1
			end
			self:toggleLoad(spec.showLoadPos, spec.numPiles, spec.pile)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_LOAD_MARKER' then
			self:toggleLoad(not spec.showLoadPos, spec.numPiles, spec.pile)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_UNLOAD_MARKER' then
			self:toggleUnload(not spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOODEW_UNLOAD_MARKER_MOVE' then
			local rX,rY,rZ = getTranslation(spec.unloadPoseRoot[1])
			if spec.unloadRot == true then
				rX = math.ceil(math.abs(rX))
				if rX < spec.rootShiftRotXMax then
					rX = rX + 1
				else
					rX = spec.rootShiftRotX
				end
			else
				rX = math.ceil(math.abs(rX))
				if rX < 5 then
					rX = rX + 1
				else
					rX = spec.unloadPoseRootX
				end
			end
			local unloadPos = rX
			self:toggleUnload(spec.showUnloadPos, unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOODEW_UNLOAD_MARKER_SCALE' then
			local msX,msY,msZ = getScale(spec.unloadMarker)
			if msZ < 2 then
				msZ = msZ + 0.2
			else
				msZ = 1
			end
			spec.unloadScale = msZ
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, spec.unloadRot)
		elseif actionName == 'AUTOLOADWOODEW_UNLOAD_MARKER_ROTATE' then
			if spec.unloadRot == false then
				spec.unloadPos = spec.rootShiftRotX
			else
				spec.unloadPos = spec.unloadPoseRootX
			end
			self:toggleUnload(spec.showUnloadPos, spec.unloadPos, spec.unloadScale, not spec.unloadRot)
		elseif actionName == 'AUTOLOADWOODEW_TOGGLE_HELP' then
			AutoLoadWood.showHelp = not AutoLoadWood.showHelp
			self:saveToXml();
		end
	end
end
function AutoLoadWood:updateValues()
	local spec = self.spec_autoLoadWood
	spec.unloadShiftMax = spec.unloadScale * 4
	if spec.trailer == 12 then
		spec.maxPiles = 3
		if spec.numPiles == 1 then
			spec.maxLength = 12
			spec.startPosZ = -4.2;
			spec.rootShiftRotX = 8
			spec.rootShiftRotXMax = 11
		elseif spec.numPiles == 2 then
			spec.maxLength = 6
			spec.startPosZ = -1.2;
			spec.rootShiftRotX = 5
			spec.rootShiftRotXMax = 8
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			end
		elseif spec.numPiles == 3 then
			spec.maxLength = 4
			spec.startPosZ = -0.2
			spec.rootShiftRotX = 4
			spec.rootShiftRotXMax = 7
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			elseif spec.pile == 3 then
				spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
			end
		end
	elseif spec.trailer == 15 then
		spec.maxPiles = 4
		if spec.numPiles == 1 then
			spec.maxLength = 15
			spec.startPosZ = -4.2;
			spec.rootShiftRotX = 9.65
			spec.rootShiftRotXMax = 12.8
		elseif spec.numPiles == 2 then
			spec.maxLength = 7.5
			spec.startPosZ = -0.45;
			spec.rootShiftRotX = 5.9
			spec.rootShiftRotXMax = 8.9
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			end
		elseif spec.numPiles == 3 then
			spec.maxLength = 5
			spec.startPosZ = 0.8
			spec.rootShiftRotX = 4.6
			spec.rootShiftRotXMax = 7.6
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			elseif spec.pile == 3 then
				spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
			end
		elseif spec.numPiles == 4 then
			spec.maxLength = 3.5
			spec.startPosZ = 1.55
			spec.rootShiftRotX = 3.9
			spec.rootShiftRotXMax = 6.9
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			elseif spec.pile == 3 then
				spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
			elseif spec.pile == 4 then
				spec.startPosZ = spec.startPosZ - (3 * (spec.maxLength + spec.space))
			end
		end
	elseif spec.trailer == 20 then
		spec.maxPiles = 4
		if spec.numPiles == 1 then
			spec.maxLength = 20
			spec.startPosZ = -4.1;
			spec.rootShiftRotX = 12.3
			spec.rootShiftRotXMax = 15.3
		elseif spec.numPiles == 2 then
			spec.maxLength = 10
			spec.startPosZ = 0.9;
			spec.rootShiftRotX = 7.3
			spec.rootShiftRotXMax = 10.3
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			end
		elseif spec.numPiles == 3 then
			spec.maxLength = 6.5
			spec.startPosZ = 2.65
			spec.rootShiftRotX = 5.55
			spec.rootShiftRotXMax = 8.55
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			elseif spec.pile == 3 then
				spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
			end
		elseif spec.numPiles == 4 then
			spec.maxLength = 5
			spec.startPosZ = 3.4
			spec.rootShiftRotX = 4.8
			spec.rootShiftRotXMax = 7.8
			if spec.pile == 1 then
				spec.startPosZ = spec.startPosZ;
			elseif spec.pile == 2 then
				spec.startPosZ = spec.startPosZ - spec.maxLength - spec.space
			elseif spec.pile == 3 then
				spec.startPosZ = spec.startPosZ - (2 * (spec.maxLength + spec.space))
			elseif spec.pile == 4 then
				spec.startPosZ = spec.startPosZ - (3 * (spec.maxLength + spec.space))
			end
		end
	end
end
function AutoLoadWood:updateLoad()
	local spec = self.spec_autoLoadWood
	
	setTranslation(spec.fillPoseRoot, spec.fillPoseNodeX, spec.fillPoseNodeY, spec.startPosZ)
	setScale(spec.loadMarker, spec.maxLength,1,2.5)
	setVisibility(spec.loadMarker, spec.showLoadPos);
	setScale(spec.unloadMarker, spec.maxLength,1,spec.unloadScale)
end
function AutoLoadWood:updateUnload()
	local spec = self.spec_autoLoadWood
	
	local j = 1
	for i = 1, 2 do
		setRotation(spec.unloadPoseRoot[i], 0,(spec.unloadRot and math.rad(90) or math.rad(0)) * j,0)
		setTranslation(spec.unloadPoseRoot[i], spec.unloadPos * j, 0, (spec.unloadRot and 3 or spec.unloadPoseRootZ))
		j = j * -1
	end
	setScale(spec.unloadMarker, spec.maxLength,1,spec.unloadScale)
	setVisibility(spec.unloadMarker, spec.showUnloadPos);
end
function AutoLoadWood:dynamicMountTriggerCallback(superFunc,triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
	local spec = self.spec_autoLoadWood
	local splitType = g_splitShapeManager:getSplitTypeByIndex(getSplitType(otherActorId))
	if splitType ~= nil then
		if onEnter then
			--print('enter')
			--if not spec.unload then
				if spec.woodLoad[otherActorId] == nil then
					spec.woodLoad[otherActorId] = otherActorId
				end
			--end
		elseif onLeave then
			if not spec.autoload then
				spec.woodLoad[otherActorId] = nil
			end
		end
	end
	--superFunc(spec, triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
end
function AutoLoadWood:getIsTurnedOn()
	local spec = self.spec_autoLoadWood
    return spec.autoload or spec.unload
end
function AutoLoadWood:onReadStream(streamId, connection)
	local spec = self.spec_autoLoadWood
	
	local rightSide = streamReadBool(streamId);
	local showLoadPos = streamReadBool(streamId);
	local showUnloadPos = streamReadBool(streamId);
	local numPiles = streamReadInt8(streamId);
    local pile = streamReadInt8(streamId);
    local unloadPos = streamReadFloat32(streamId);
    local unloadScale = streamReadFloat32(streamId);
    local unloadRot = streamReadBool(streamId);
	
	if rightSide ~= nil and showLoadPos ~= nil and showUnloadPos ~= nil and numPiles ~= nil and pile ~= nil and unloadPos ~= nil and unloadScale ~= nil and unloadRot ~= nil then
		self:toggleState(spec.autoload, rightSide, spec.unload, true)
		self:toggleLoad(showLoadPos, numPiles, pile, true)
		self:toggleUnload(showUnloadPos, unloadPos, unloadScale, unloadRot, true)
	end
end;

function AutoLoadWood:onWriteStream(streamId, connection)
	local spec = self.spec_autoLoadWood
	
	streamWriteBool(streamId, spec.rightSide);
	streamWriteBool(streamId, spec.showLoadPos);
	streamWriteBool(streamId, spec.showUnloadPos);
	streamWriteInt8(streamId, spec.numPiles);
	streamWriteInt8(streamId, spec.pile);
	streamWriteFloat32(streamId, spec.unloadPos);
	streamWriteFloat32(streamId, spec.unloadScale);
	streamWriteBool(streamId, spec.unloadRot);
end;

--- Events ---

AutoLoadWoodToggleEvent = {};
AutoLoadWoodToggleEvent_mt = Class(AutoLoadWoodToggleEvent, Event);

InitEventClass(AutoLoadWoodToggleEvent, "AutoLoadWoodToggleEvent");

function AutoLoadWoodToggleEvent.emptyNew()
    local self = Event.new(AutoLoadWoodToggleEvent_mt);
    return self;
end;
    
function AutoLoadWoodToggleEvent.new(object, autoload, rightSide, unload)
	local self = AutoLoadWoodToggleEvent.emptyNew()
	self.object = object;
	self.autoload = autoload;
	self.rightSide = rightSide;
	self.unload = unload;
	return self;
end;

function AutoLoadWoodToggleEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.autoload = streamReadBool(streamId);
	self.rightSide = streamReadBool(streamId);
	self.unload = streamReadBool(streamId);
	self:run(connection);
end;

function AutoLoadWoodToggleEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.autoload);
	streamWriteBool(streamId, self.rightSide);
	streamWriteBool(streamId, self.unload);
end;

function AutoLoadWoodToggleEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleState(self.autoload, self.rightSide, self.unload, true);
	end;
end;

function AutoLoadWoodToggleEvent.sendEvent(vehicle, autoload, rightSide, unload, noEventSend)
	if autoload ~= vehicle.autoload or rightSide ~= vehicle.rightSide or unload ~= vehicle.unload then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodToggleEvent.new(vehicle, autoload, rightSide, unload), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodToggleEvent.new(vehicle, autoload, rightSide, unload));
			end;
		end;
	end;
end;

AutoLoadWoodToggleLoadEvent = {};
AutoLoadWoodToggleLoadEvent_mt = Class(AutoLoadWoodToggleLoadEvent, Event);

InitEventClass(AutoLoadWoodToggleLoadEvent, "AutoLoadWoodToggleLoadEvent");

function AutoLoadWoodToggleLoadEvent.emptyNew()
    local self = Event.new(AutoLoadWoodToggleLoadEvent_mt);
    return self;
end;
    
function AutoLoadWoodToggleLoadEvent.new(object, showLoadPos, numPiles, pile)
	local self = AutoLoadWoodToggleLoadEvent.emptyNew()
	self.object = object;
	self.showLoadPos = showLoadPos;
	self.numPiles = numPiles;
	self.pile = pile;
	return self;
end;

function AutoLoadWoodToggleLoadEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.showLoadPos = streamReadBool(streamId);
	self.numPiles = streamReadInt8(streamId);
	self.pile = streamReadInt8(streamId);
	self:run(connection);
end;

function AutoLoadWoodToggleLoadEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.showLoadPos);
	streamWriteInt8(streamId, self.numPiles);
	streamWriteInt8(streamId, self.pile);
end;

function AutoLoadWoodToggleLoadEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleLoad(self.showLoadPos, self.numPiles, self.pile, true);
	end;
end;

function AutoLoadWoodToggleLoadEvent.sendEvent(vehicle, showLoadPos, numPiles, pile, noEventSend)
	if showLoadPos ~= vehicle.showLoadPos or numPiles ~= vehicle.numPiles or pile ~= vehicle.pile then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodToggleLoadEvent.new(vehicle, showLoadPos, numPiles, pile), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodToggleLoadEvent.new(vehicle, showLoadPos, numPiles, pile));
			end;
		end;
	end;
end;

AutoLoadWoodToggleUnloadEvent = {};
AutoLoadWoodToggleUnloadEvent_mt = Class(AutoLoadWoodToggleUnloadEvent, Event);

InitEventClass(AutoLoadWoodToggleUnloadEvent, "AutoLoadWoodToggleUnloadEvent");

function AutoLoadWoodToggleUnloadEvent.emptyNew()
    local self = Event.new(AutoLoadWoodToggleUnloadEvent_mt);
    return self;
end;
    
function AutoLoadWoodToggleUnloadEvent.new(object, showUnloadPos, unloadPos, unloadScale, unloadRot)
	local self = AutoLoadWoodToggleUnloadEvent.emptyNew()
	self.object = object;
	self.showUnloadPos = showUnloadPos;
	self.unloadPos = unloadPos;
	self.unloadScale = unloadScale;
	self.unloadRot = unloadRot;
	return self;
end;

function AutoLoadWoodToggleUnloadEvent:readStream(streamId, connection)
	self.object = NetworkUtil.readNodeObject(streamId);
	self.showUnloadPos = streamReadBool(streamId);
	self.unloadPos = streamReadFloat32(streamId);
	self.unloadScale = streamReadFloat32(streamId);
	self.unloadRot = streamReadBool(streamId);
	self:run(connection);
end;

function AutoLoadWoodToggleUnloadEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.object);
	streamWriteBool(streamId, self.showUnloadPos);
	streamWriteFloat32(streamId, self.unloadPos);
	streamWriteFloat32(streamId, self.unloadScale);
	streamWriteBool(streamId, self.unloadRot);
end;

function AutoLoadWoodToggleUnloadEvent:run(connection)
	if not connection:getIsServer() then
		g_server:broadcastEvent(self, false, connection, self.object);
	end;
	if self.object ~= nil then
		self.object:toggleUnload(self.showUnloadPos, self.unloadPos, self.unloadScale, self.unloadRot, true);
	end;
end;

function AutoLoadWoodToggleUnloadEvent.sendEvent(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot, noEventSend)
	if showUnloadPos ~= vehicle.showUnloadPos or unloadPos ~= vehicle.unloadPos or unloadScale ~= vehicle.unloadScale or unloadRot ~= vehicle.unloadRot then
		if noEventSend == nil or noEventSend == false then
			if g_server ~= nil then
				g_server:broadcastEvent(AutoLoadWoodToggleUnloadEvent.new(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot), nil, nil, vehicle);
			else
				g_client:getServerConnection():sendEvent(AutoLoadWoodToggleUnloadEvent.new(vehicle, showUnloadPos, unloadPos, unloadScale, unloadRot));
			end;
		end;
	end;
end