-- A basic autoloading script
-- put together by alfalfa6945
-- version 0.0.1.1
-- ***script is still in early development, you really shouldn't use it in a production environment

easyAutoLoader = {}

local easyAutoLoader_directory = g_currentModDirectory
local autoloadModName = g_currentModName

function easyAutoLoader.prerequisitesPresent(specializations)
	return true
end

function easyAutoLoader.registerEventListeners(vehicleType)
	for _, spec in pairs({"onLoad", "onPostLoad", "onDelete", "onUpdate", "onDraw", "onReadStream", "onWriteStream", "onRegisterActionEvents", "saveToXMLFile"}) do
		SpecializationUtil.registerEventListener(vehicleType, spec, easyAutoLoader)
	end
end

function easyAutoLoader:onLoad(savegame)
	self.updateTable = easyAutoLoader.updateTable
	self.doStateChange = easyAutoLoader.doStateChange
	self.setMarkerVisibility = easyAutoLoader.setMarkerVisibility
	self.setMarkerPosition = easyAutoLoader.setMarkerPosition
	self.setUnloadPosition = easyAutoLoader.setUnloadPosition
	self.objectCallback = easyAutoLoader.objectCallback
	self.isDedicatedServer = easyAutoLoader.isDedicatedServer
	self.setWorkMode = easyAutoLoader.setWorkMode
	self.setSelect = easyAutoLoader.setSelect
	self.setUnload = easyAutoLoader.setUnload
	self.changeMarkerPosition = easyAutoLoader.changeMarkerPosition
	self.moveMarkerLeft = easyAutoLoader.moveMarkerLeft
	self.moveMarkerRight = easyAutoLoader.moveMarkerRight
	self.moveMarkerUp = easyAutoLoader.moveMarkerUp
	self.moveMarkerDown = easyAutoLoader.moveMarkerDown
	self.moveMarkerForward = easyAutoLoader.moveMarkerForward
	self.moveMarkerBackward = easyAutoLoader.moveMarkerBackward
	self.moveMarkerOriginal = easyAutoLoader.moveMarkerOriginal
	self.moveTrigger = getXMLString(self.xmlFile, "vehicle.easyAutoLoad#triggerAnimation")
	self.workMode = false
	self.currentNumObjects = 0
	self.unloadPosition = 1
	self.state = 1
	self.var = 0
	self.centerMarkerIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.easyAutoLoad#centerMarkerIndex"), 1)
	self.unloadHeightOffset = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.easyAutoLoad#unloadHeightOffset"), 1.1)
	self.unloadMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.easyAutoLoad#markerIndex"), self.i3dMappings)
	self.markerVisible = false
	if self.unloadMarker and getVisibility(self.unloadMarker) then
		self.markerVisible = true
		self:setMarkerVisibility(false)
	end
	self.markerMoveSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#markerMoveSpeed"), 0.05)
	self.markerMinX = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#minX"), "0 4 -25 -25"))
	self.markerMaxX = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#maxX"), "0 25 25 -4"))
	self.markerMinY = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#minY"), "0 -0.7 -0.7 -0.7"))
	self.markerMaxY = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#maxY"), "0 15 15 15"))
	self.markerMinZ = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#minZ"), "0 -20 -30 -20"))
	self.markerMaxZ = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.moveableMarkerOptions#maxZ"), "0 20 -16 20"))
	self.palletIcon = false
	self.squareBaleIcon = false
	self.roundBaleIcon = false
	local markerPositions = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.easyAutoLoad#markerPositionsIndex"), self.i3dMappings)
	local numMarkerChildren = getNumOfChildren(markerPositions)
	if numMarkerChildren > 0 then
		self.markerPositions = {}
		for i = 1, numMarkerChildren do
			local entry = {}
			local markerId = getChildAt(markerPositions, i-1)
			local name = getName(markerId)
			entry.index = markerId
			entry.translation = {getTranslation(markerId)}
			entry.name = g_i18n:hasText(name) and g_i18n:getText(name) or name
			table.insert(self.markerPositions, entry)
		end
	end
	local triggerNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.easyAutoLoad#triggersIndex"), self.i3dMappings)
	local numTriggerChildren = getNumOfChildren(triggerNode)
	if numTriggerChildren > 0 then
		self.objectTriggers = {}
		for i = 1, numTriggerChildren do
			local triggerId = getChildAt(triggerNode, i-1)
			table.insert(self.objectTriggers, triggerId)
			addTrigger(triggerId, "objectCallback", self)
		end
	end
	local objectsNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.easyAutoLoad#objectsIndex"), self.i3dMappings)
	local numChildren = getNumOfChildren(objectsNode)
	if numChildren > 0 then
		self.autoLoadObjects = {}
		for i = 1, numChildren do
			local entry = {}
			local objectId = getChildAt(objectsNode, i-1)
			local name = getName(objectId)
			entry.index = objectId
			entry.name = name
			entry.nameL = g_i18n:hasText(name) and g_i18n:getText(name) or name
			entry.maxNumObjects = getNumOfChildren(objectId)
			entry.isRoundBale = Utils.getNoNil(getUserAttribute(objectId, "isRoundBale"), false)
			if entry.isRoundBale then
				entry.diameter = Utils.getNoNil(getUserAttribute(objectId, "diameter"), 1.30)
			end
			entry.isSquareBale = Utils.getNoNil(getUserAttribute(objectId, "isSquareBale"), false)
			if entry.isSquareBale then
				entry.width = Utils.getNoNil(getUserAttribute(objectId, "baleWidth"), 1.20)
				entry.length = Utils.getNoNil(getUserAttribute(objectId, "baleLength"), 2.40)
			end
			entry.isHDbale = Utils.getNoNil(getUserAttribute(objectId, "isHDbale"), false)
			entry.isMissionPallet = Utils.getNoNil(getUserAttribute(objectId, "isMissionPallet"), false)
			entry.excludedFillTypes = StringUtil.splitString(" ", getUserAttribute(objectId, "excludedFillTypes"))
			entry.includedFillTypes = StringUtil.splitString(" ", getUserAttribute(objectId, "includedFillTypes"))
			entry.usePalletSize = Utils.getNoNil(getUserAttribute(objectId, "usePalletSize"), false)
			if entry.usePalletSize then
				entry.sizeLength = Utils.getNoNil(getUserAttribute(objectId, "sizeLength"), 2.00)
				entry.sizeWidth = Utils.getNoNil(getUserAttribute(objectId, "sizeWidth"), 2.00)
			end
			entry.toLoad = {}
			entry.toMount = {}
			entry.objects = {}
			self:updateTable(entry.objects, objectId)
			table.insert(self.autoLoadObjects, entry)
		end
	end
	self.easyAutoLoaderRegistrationList = {}
	self.easyAutoLoaderRegistrationList[InputAction.workMode] = { eventId="", callback=self.setWorkMode, triggerUp=false, triggerDown=true, triggerAlways=false, startActive=true, callbackState=-1, text=g_i18n:getText("workModeOn"), textVisibility=true }
	self.easyAutoLoaderRegistrationList[InputAction.select] = { eventId="", callback=self.setSelect, triggerUp=false, triggerDown=true, triggerAlways=false, startActive=true, callbackState=-1, text=self.autoLoadObjects[self.state].nameL, textVisibility=true }
	self.easyAutoLoaderRegistrationList[InputAction.markerPosition] = { eventId="", callback=self.changeMarkerPosition, triggerUp=false, triggerDown=true, triggerAlways=false, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerPosition"), textVisibility=true }
	self.easyAutoLoaderRegistrationList[InputAction.unload] = { eventId="", callback=self.setUnload, triggerUp=false, triggerDown=true, triggerAlways=false, startActive=false, callbackState=-1, text=g_i18n:getText("input_unload"), textVisibility=true }
	self.easyAutoLoaderRegistrationList[InputAction.markerUp] = { eventId="", callback=self.moveMarkerUp, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerUp"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerDown] = { eventId="", callback=self.moveMarkerDown, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerDown"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerLeft] = { eventId="", callback=self.moveMarkerLeft, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerLeft"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerRight] = { eventId="", callback=self.moveMarkerRight, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerRight"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerForward] = { eventId="", callback=self.moveMarkerForward, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerForward"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerBackward] = { eventId="", callback=self.moveMarkerBackward, triggerUp=false, triggerDown=true, triggerAlways=true, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerBackward"), textVisibility=false }
	self.easyAutoLoaderRegistrationList[InputAction.markerOriginal] = { eventId="", callback=self.moveMarkerOriginal, triggerUp=false, triggerDown=true, triggerAlways=false, startActive=false, callbackState=-1, text=g_i18n:getText("input_markerOriginal"), textVisibility=true }
	self.coloredIcons = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.easyAutoLoad.levelBarOptions#coloredIcons"), false)
	if not self:isDedicatedServer() then
		self.easyAutoLoaderIcons = {}
		local fillLevelColor = ConfigurationUtil.getColorFromString(Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.levelBarOptions#fillLevelColor"), "0.991 0.3865 0.01 1"))
		local fillLevelTextColor = {1, 1, 1, 0.2}
		self.fillLevelsTextColor = {1, 1, 1, 1}
		local uiScale = g_gameSettings:getValue("uiScale")
		local iconWidth, iconHeight = getNormalizedScreenValues(33 * uiScale, 33 * uiScale)
		local offsetX, offsetY = getNormalizedScreenValues(2 * uiScale, 4 * uiScale)
		self.easyAutoLoaderIcons.palletIconOverlay = Overlay:new(Utils.getFilename(self.coloredIcons and getXMLString(self.xmlFile, "vehicle.easyAutoLoad.palletIcon#colorIcon") or getXMLString(self.xmlFile, "vehicle.easyAutoLoad.palletIcon#overlayIcon"), easyAutoLoader_directory), g_currentMission.hud.fillLevelsDisplay.origX - offsetX, g_currentMission.hud.fillLevelsDisplay.origY, iconWidth, iconHeight)
		self.easyAutoLoaderIcons.roundBaleIconOverlay = Overlay:new(Utils.getFilename(self.coloredIcons and getXMLString(self.xmlFile, "vehicle.easyAutoLoad.roundBaleIcon#colorIcon") or getXMLString(self.xmlFile, "vehicle.easyAutoLoad.roundBaleIcon#overlayIcon"), easyAutoLoader_directory), g_currentMission.hud.fillLevelsDisplay.origX - offsetX, g_currentMission.hud.fillLevelsDisplay.origY, iconWidth, iconHeight)
		self.easyAutoLoaderIcons.squareBaleIconOverlay = Overlay:new(Utils.getFilename(self.coloredIcons and getXMLString(self.xmlFile, "vehicle.easyAutoLoad.squareBaleIcon#colorIcon") or getXMLString(self.xmlFile, "vehicle.easyAutoLoad.squareBaleIcon#overlayIcon"), easyAutoLoader_directory), g_currentMission.hud.fillLevelsDisplay.origX - offsetX, g_currentMission.hud.fillLevelsDisplay.origY, iconWidth, iconHeight)
		if not self.coloredIcons then
			local iconColor = ConfigurationUtil.getColorFromString(Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.easyAutoLoad.levelBarOptions#iconColor"), "0.6307 0.6307 0.6307 1"))
			for _, icon in pairs(self.easyAutoLoaderIcons) do
				icon:setColor(unpack(iconColor))
			end
		end
		local width, height = getNormalizedScreenValues(144 * uiScale, 10 * uiScale)
		self.fillLevelBar = StatusBar:new(g_baseUIFilename, g_colorBgUVs, nil, fillLevelTextColor, fillLevelColor, nil, g_currentMission.hud.fillLevelsDisplay.origX + iconWidth + offsetX, g_currentMission.hud.fillLevelsDisplay.origY + offsetY, width, height)
	end
	self.currentObjectId = 0
end

function easyAutoLoader:onPostLoad(savegame)
	if savegame ~= nil and not savegame.resetVehicles then
		local key = savegame.key.."."..autoloadModName..".easyAutoLoader"
		self.state = Utils.getNoNil(getXMLInt(savegame.xmlFile, key.."#objectMode"), 1)
	end
end

function easyAutoLoader:onDelete()
	if self.currentNumObjects > 0 then
		self:setUnload()
	end
	for i = 1, #self.objectTriggers do
		removeTrigger(self.objectTriggers[i])
	end
	if self.easyAutoLoaderIcons then
		for _, icon in pairs(self.easyAutoLoaderIcons) do
			icon:delete()
		end
		self.fillLevelBar:delete()
	end
end

function easyAutoLoader:onReadStream(streamId, connection)
    self.currentNumObjects = streamReadInt16(streamId)
    self.state = streamReadInt8(streamId)
	local objectId
	for i = 1, #self.autoLoadObjects[self.state].objects do
		objectId = NetworkUtil.readNodeObjectId(streamId)
		self.autoLoadObjects[self.state].objects[i].objectId = objectId
		if objectId then
			self.autoLoadObjects[self.state].toMount[objectId] = {serverId = objectId, linkNode = self.autoLoadObjects[self.state].objects[i].node, trans = {0,0,0}, rot = {0,0,0}}
		end
	end
	self.palletIcon = streamReadBool(streamId)
	self.squareBaleIcon = streamReadBool(streamId)
	self.roundBaleIcon = streamReadBool(streamId)
	self:setMarkerVisibility(streamReadBool(streamId))
	self.unloadPosition = streamReadInt8(streamId)
end

function easyAutoLoader:onWriteStream(streamId, connection)
    streamWriteInt16(streamId, self.currentNumObjects)
    streamWriteInt8(streamId, self.state)
	local objectId
	for _, object in pairs(self.autoLoadObjects[self.state].objects) do
		if object.objectId ~= nil then
			objectId = object.objectId
			NetworkUtil.writeNodeObjectId(streamId, objectId)
		end
	end
	streamWriteBool(streamId, self.palletIcon)
	streamWriteBool(streamId, self.squareBaleIcon)
	streamWriteBool(streamId, self.roundBaleIcon)
	streamWriteBool(streamId, self.markerVisible)
	streamWriteInt8(streamId, self.unloadPosition)
end

function easyAutoLoader:onUpdate(dt)
	if not self.runOnce then
		self.runOnce = true
		for index, objectToMount in pairs(self.autoLoadObjects[self.state].toMount) do
			local object = NetworkUtil.getObject(objectToMount.serverId)
			if object ~= nil then
				local x,y,z = unpack(objectToMount.trans)
				local rx,ry,rz = unpack(objectToMount.rot)
				object:mount(self, objectToMount.linkNode, x,y,z, rx,ry,rz)
				self.autoLoadObjects[self.state].toMount[index] = nil
			end
		end
	end
	if self:getIsActiveForInput() and self:getIsActive() then
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.workMode].eventId, self.currentNumObjects ~= self.autoLoadObjects[self.state].maxNumObjects)
		if self.workMode then
			g_inputBinding:setActionEventText(self.easyAutoLoaderRegistrationList[InputAction.workMode].eventId, g_i18n:getText("workModeOff"))
		elseif not self.workMode then
			g_inputBinding:setActionEventText(self.easyAutoLoaderRegistrationList[InputAction.workMode].eventId, g_i18n:getText("workModeOn"))
		end
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.select].eventId, self.currentNumObjects == 0 and not self.workMode)
		g_inputBinding:setActionEventText(self.easyAutoLoaderRegistrationList[InputAction.select].eventId, self.autoLoadObjects[self.state].nameL)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerPosition].eventId, self.currentNumObjects >= 1 and not self.workMode)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.unload].eventId, self.currentNumObjects >= 1 and not self.workMode)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerUp].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerDown].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerLeft].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerRight].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerForward].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerBackward].eventId, self.markerVisible)
		g_inputBinding:setActionEventActive(self.easyAutoLoaderRegistrationList[InputAction.markerOriginal].eventId, self.markerVisible)
    end
end

function easyAutoLoader:onDraw()
	if not self:isDedicatedServer() and self.currentNumObjects >= 1 then
		local percentage = self.currentNumObjects / self.autoLoadObjects[self.state].maxNumObjects
		self.fillLevelBar:setValue(percentage)
		self.fillLevelBar:render()
		if self.easyAutoLoaderIcons.palletIconOverlay and self.palletIcon then
			self.easyAutoLoaderIcons.palletIconOverlay:render()
		end
		if self.easyAutoLoaderIcons.squareBaleIconOverlay and self.squareBaleIcon then
			self.easyAutoLoaderIcons.squareBaleIconOverlay:render()
		end
		if self.easyAutoLoaderIcons.roundBaleIconOverlay and self.roundBaleIcon then
			self.easyAutoLoaderIcons.roundBaleIconOverlay:render()
		end
		setTextBold(true)
		setTextColor(unpack(self.fillLevelsTextColor))
		setTextAlignment(RenderText.ALIGN_RIGHT)
		renderText(g_currentMission.hud.fillLevelsDisplay.origX + self.fillLevelBar.width + self.easyAutoLoaderIcons.palletIconOverlay.width, g_currentMission.hud.fillLevelsDisplay.origY + g_currentMission.hud.fillLevelsDisplay.fillLevelTextOffsetY, g_currentMission.hud.fillLevelsDisplay.fillLevelTextSize, self.currentNumObjects.." / "..self.autoLoadObjects[self.state].maxNumObjects)
		setTextBold(false)
	end
end

function easyAutoLoader:updateTable(objectTable, id)
	local i = 0
    while true do
		if i >= getNumOfChildren(id) then
			break
		end
		local entry = {}
		local index = getChildAt(id, i)
		if index ~= nil then
            entry.node = index
            entry.objectId = nil
            table.insert(objectTable, entry)
        end
        i = i + 1
	end
end

function easyAutoLoader:doStateChange(mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon, noEventSend)
	easyAutoLoaderEvent.sendEvent(self, mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon, noEventSend)
	if mode == 1 then
		local object = NetworkUtil.getObject(var)
        if object == nil then
            return
        end	
        self.currentNumObjects = self.currentNumObjects + 1
		object:mount(self, self.autoLoadObjects[state].objects[self.currentNumObjects].node, 0,0,0, 0,0,0)
		self.autoLoadObjects[state].objects[self.currentNumObjects].objectId = var
		self.currentObjectId = var
	elseif mode == 2 then
		local x, y, z = getTranslation(self.unloadMarker)
		if state > 1 then
			setTranslation(self.autoLoadObjects[self.state].index, x, y + self.unloadHeightOffset, z)
		else
			setTranslation(self.autoLoadObjects[self.state].index, unpack(self.markerPositions[self.centerMarkerIndex].translation))
		end
		for _, placeholder in pairs(self.autoLoadObjects[self.state].objects) do
			if placeholder.objectId ~= nil then
				local objectId = placeholder.objectId
				local object = NetworkUtil.getObject(objectId)
				if object ~= nil then
					object:unmount()
				end
				placeholder.objectId = nil
			end
		end
		self.currentObjectId = 0
        self.currentNumObjects = var
		self:setMarkerVisibility(bool)
		self:setMarkerPosition(unpack(self.markerPositions[self.centerMarkerIndex].translation))
		self:setUnloadPosition(self.centerMarkerIndex)
		setTranslation(self.autoLoadObjects[self.state].index, unpack(self.markerPositions[self.centerMarkerIndex].translation))
	elseif mode == 3 then
        self.state = state
	elseif mode == 4 then
        self.workMode = bool
		if self.moveTrigger then
			self:playAnimation(self.moveTrigger, self.workMode and 1 or -1, nil, true)
		end
	end
	self.palletIcon = palletIcon
	self.squareBaleIcon = squareBaleIcon
	self.roundBaleIcon = roundBaleIcon
end

function easyAutoLoader:objectCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if onEnter and self.workMode then
		local object = g_currentMission:getNodeObject(otherId)
		if object == nil or object.mountObject ~= nil then
			return
		end
		local objectId = NetworkUtil.getObjectId(object)
		if objectId == self.currentObjectId then
			return
		end
		local isPallet = object:isa(Vehicle)
		local isRoundbale = false
		local isHDbale = false
		local isNotExcluded = false
		if object:isa(Bale) then
			isRoundbale = self.autoLoadObjects[self.state].isRoundBale and Utils.getNoNil(getUserAttribute(object.nodeId, "isRoundbale"), false)
			isHDbale = self.autoLoadObjects[self.state].isHDbale and Utils.getNoNil(getUserAttribute(object.nodeId, "isHDbale"), false)
			if isRoundbale and self.autoLoadObjects[self.state].diameter == string.format("%1.2f", object.baleDiameter) then
				isNotExcluded = true
			elseif isHDbale and self.autoLoadObjects[self.state].isHDbale then
				isNotExcluded = true
			elseif object.baleLength ~= nil and object.baleWidth ~= nil then
				isNotExcluded = self.autoLoadObjects[self.state].length == string.format("%1.2f", object.baleLength) and self.autoLoadObjects[self.state].width == string.format("%1.2f", object.baleWidth)
			end
		elseif isPallet then
			local objectFillType = object:getFillUnitFillType(1)
			if object:getFillUnitFillLevelPercentage(1) < 1 then
				return
			end
			if self.autoLoadObjects[self.state].excludedFillTypes then
				for _, excludedFillType in pairs(self.autoLoadObjects[self.state].excludedFillTypes) do
					if objectFillType == g_fillTypeManager:getFillTypeIndexByName(excludedFillType) then
						isNotExcluded = false
						break
					else
						isNotExcluded = true
					end
				end
			end
			if self.autoLoadObjects[self.state].includedFillTypes then
				for _, includedFillType in pairs(self.autoLoadObjects[self.state].includedFillTypes) do
					if objectFillType == g_fillTypeManager:getFillTypeIndexByName(includedFillType) then
						isNotExcluded = true
						break
					end
				end
			end
			if self.autoLoadObjects[self.state].usePalletSize then
				if string.format("%1.2f", object.sizeWidth) == self.autoLoadObjects[self.state].sizeWidth and string.format("%1.2f", object.sizeLength) == self.autoLoadObjects[self.state].sizeLength then
					isNotExcluded = true
				end
			end
		elseif object.mission ~= nil and self.autoLoadObjects[self.state].isMissionPallet then
			isNotExcluded = true
			isPallet = true
		end
		if isNotExcluded then
			local palletIcon = isPallet
			local squareBaleIcon = not isRoundbale and not isPallet
			local roundBaleIcon = isRoundbale
			self:doStateChange(1, false, self.state, objectId, palletIcon, squareBaleIcon, roundBaleIcon, false)
			if self.currentNumObjects == self.autoLoadObjects[self.state].maxNumObjects then
				self:doStateChange(4, false, 0, 0, palletIcon, squareBaleIcon, roundBaleIcon, false)
			end
		end
	end
end

function easyAutoLoader:setMarkerVisibility(markervisibility, noEventSend)
	if markervisibility ~= self.markerVisible then
        if noEventSend == nil or noEventSend == false then
            if g_server ~= nil then
                g_server:broadcastEvent(SetMarkerVisibilityEvent:new(self, markervisibility), nil, nil, self)
            else
                g_client:getServerConnection():sendEvent(SetMarkerVisibilityEvent:new(self, markervisibility))
            end
        end
        self.markerVisible = markervisibility
        setVisibility(self.unloadMarker, markervisibility)
    end
end

function easyAutoLoader:setUnloadPosition(unloadPosition, noEventSend)
	if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(SetUnloadPositionEvent:new(self, unloadPosition), nil, nil, self)
        else
            g_client:getServerConnection():sendEvent(SetUnloadPositionEvent:new(self, unloadPosition))
        end
    end
	self.unloadPosition = unloadPosition
end

function easyAutoLoader:onRegisterActionEvents(isSelected, isOnActiveVehicle)
	if isOnActiveVehicle then
		for actionId, entry in pairs(self.easyAutoLoaderRegistrationList) do
			eventAdded, entry.eventId = g_inputBinding:registerActionEvent(actionId, self, entry.callback, entry.triggerUp, entry.triggerDown, entry.triggerAlways, entry.startActive, entry.callbackState)
			if eventAdded then
				g_inputBinding.events[entry.eventId].displayPriority = 3
			end
		end
	end
end

function easyAutoLoader:setWorkMode()
	if self.currentNumObjects ~= self.autoLoadObjects[self.state].maxNumObjects then
		self:setMarkerVisibility(false)
		self:setUnloadPosition(self.centerMarkerIndex)
		self:doStateChange(4, not self.workMode, 0, 0, self.palletIcon, self.squareBaleIcon, self.roundBaleIcon, false)
	end
end

function easyAutoLoader:setSelect()
	self.state = self.state + 1
	if self.state > #self.autoLoadObjects then
		self.state = 1
	end
	self:doStateChange(3, false, self.state, 0, self.palletIcon, self.squareBaleIcon, self.roundBaleIcon, false)
end

function easyAutoLoader:changeMarkerPosition()
	local unloadPosition = self.unloadPosition + 1
	if unloadPosition > #self.markerPositions then
		unloadPosition = 1
	end
	self:setUnloadPosition(unloadPosition)
	self:setMarkerVisibility(unloadPosition > 1)
	self:setMarkerPosition(unpack(self.markerPositions[unloadPosition].translation))
end

function easyAutoLoader:setUnload()
	self:doStateChange(2, false, self.unloadPosition, 0, false, false, false, false)
end

function easyAutoLoader:setMarkerPosition(markerX, markerY, markerZ, noEventSend)
	if self.markerVisible then
		if noEventSend == nil or noEventSend == false then
            if g_server ~= nil then
                g_server:broadcastEvent(SetMarkerMoveEvent:new(self, markerX, markerY, markerZ), nil, nil, self)
            else
                g_client:getServerConnection():sendEvent(SetMarkerMoveEvent:new(self, markerX, markerY, markerZ))
            end
        end
		setTranslation(self.unloadMarker, markerX, markerY, markerZ)
	end
end

function easyAutoLoader:moveMarkerLeft()
	local x, y, z = getTranslation(self.unloadMarker)
	x = MathUtil.clamp(x + self.markerMoveSpeed, self.markerMinX[self.unloadPosition], self.markerMaxX[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerRight()
	local x, y, z = getTranslation(self.unloadMarker)
	x = MathUtil.clamp(x - self.markerMoveSpeed, self.markerMinX[self.unloadPosition], self.markerMaxX[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerUp()
	local x, y, z = getTranslation(self.unloadMarker)
	y = MathUtil.clamp(y + self.markerMoveSpeed, self.markerMinY[self.unloadPosition], self.markerMaxY[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerDown()
	local x, y, z = getTranslation(self.unloadMarker)
	y = MathUtil.clamp(y - self.markerMoveSpeed, self.markerMinY[self.unloadPosition], self.markerMaxY[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerForward()
	local x, y, z = getTranslation(self.unloadMarker)
	z = MathUtil.clamp(z + self.markerMoveSpeed, self.markerMinZ[self.unloadPosition], self.markerMaxZ[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerBackward()
	local x, y, z = getTranslation(self.unloadMarker)
	z = MathUtil.clamp(z - self.markerMoveSpeed, self.markerMinZ[self.unloadPosition], self.markerMaxZ[self.unloadPosition])
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:moveMarkerOriginal()
	x, y, z = unpack(self.markerPositions[self.unloadPosition].translation)
	self:setMarkerPosition(x, y, z)
end

function easyAutoLoader:saveToXMLFile(xmlFile, key)
	setXMLInt(xmlFile, key.."#objectMode", Utils.getNoNil(self.state, 1))
	if self.currentNumObjects > 0 then
		self:setUnload()
	end
end

function easyAutoLoader:isDedicatedServer()
	if g_server ~= nil and g_client ~= nil and g_dedicatedServerInfo ~= nil then
		return true
	end
	return
end

easyAutoLoaderEvent = {}
easyAutoLoaderEvent_mt = Class(easyAutoLoaderEvent, Event)
InitEventClass(easyAutoLoaderEvent, "easyAutoLoaderEvent")

function easyAutoLoaderEvent:emptyNew()
	local self = Event:new(easyAutoLoaderEvent_mt)
    self.className = "easyAutoLoaderEvent"
    return self
end

function easyAutoLoaderEvent:new(vehicle, mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon)
    local self = easyAutoLoaderEvent:emptyNew()
    self.vehicle = vehicle
    self.mode = mode
    self.bool = bool
    self.state = state
    self.var = var
    self.palletIcon = palletIcon
    self.squareBaleIcon = squareBaleIcon
    self.roundBaleIcon = roundBaleIcon
    return self
end

function easyAutoLoaderEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId)
    self.vehicle = NetworkUtil.getObject(id)
    self.mode = streamReadInt8(streamId)
    self.bool = streamReadBool(streamId)
    self.state = streamReadInt8(streamId)
    self.var = streamReadFloat32(streamId)
    self.palletIcon = streamReadBool(streamId)
    self.squareBaleIcon = streamReadBool(streamId)
    self.roundBaleIcon = streamReadBool(streamId)
    self:run(connection)
end

function easyAutoLoaderEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, NetworkUtil.getObjectId(self.vehicle))
    streamWriteInt8(streamId, self.mode)
    streamWriteBool(streamId, self.bool)
    streamWriteInt8(streamId, self.state)
    streamWriteFloat32(streamId, self.var)
    streamWriteBool(streamId, self.palletIcon)
    streamWriteBool(streamId, self.squareBaleIcon)
    streamWriteBool(streamId, self.roundBaleIcon)
end

function easyAutoLoaderEvent:run(connection)  
    self.vehicle:doStateChange(self.mode, self.bool, self.state, self.var, self.palletIcon, self.squareBaleIcon, self.roundBaleIcon, true)
    if not connection:getIsServer() then
        g_server:broadcastEvent(easyAutoLoaderEvent:new(self.vehicle, self.mode, self.bool, self.state, self.var, self.palletIcon, self.squareBaleIcon, self.roundBaleIcon), nil, connection, self.vehicle)
    end
end

function easyAutoLoaderEvent.sendEvent(vehicle, mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon, noEventSend)
    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(easyAutoLoaderEvent:new(vehicle, mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon), nil, nil, vehicle)
        else
            g_client:getServerConnection():sendEvent(easyAutoLoaderEvent:new(vehicle, mode, bool, state, var, palletIcon, squareBaleIcon, roundBaleIcon))
        end
    end
end

SetMarkerVisibilityEvent = {}
SetMarkerVisibilityEvent_mt = Class(SetMarkerVisibilityEvent, Event)
InitEventClass(SetMarkerVisibilityEvent, "SetMarkerVisibilityEvent")

function SetMarkerVisibilityEvent:emptyNew()
    local self = Event:new(SetMarkerVisibilityEvent_mt)
    self.className="SetMarkerVisibilityEvent"
    return self
end

function SetMarkerVisibilityEvent:new(object, active)
    local self = SetMarkerVisibilityEvent:emptyNew()
    self.object = object
	self.active = active
    return self
end

function SetMarkerVisibilityEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId)
	self.object = NetworkUtil.getObject(id)
    self.active = streamReadBool(streamId)
    self:run(connection)
 end

function SetMarkerVisibilityEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, NetworkUtil.getObjectId(self.object))
    streamWriteBool(streamId, self.active)
end

function SetMarkerVisibilityEvent:run(connection)
    self.object:setMarkerVisibility(self.active, true)
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetMarkerVisibilityEvent:new(self.object, self.active), nil, connection, self.object)
    end
end

SetUnloadPositionEvent = {}
SetUnloadPositionEvent_mt = Class(SetUnloadPositionEvent, Event)
InitEventClass(SetUnloadPositionEvent, "SetUnloadPositionEvent")

function SetUnloadPositionEvent:emptyNew()
    local self = Event:new(SetUnloadPositionEvent_mt)
    self.className="SetUnloadPositionEvent"
    return self
end

function SetUnloadPositionEvent:new(object, unloadPosition)
    local self = SetUnloadPositionEvent:emptyNew()
    self.object = object
	self.unloadPosition = unloadPosition
    return self
end

function SetUnloadPositionEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId)
	self.object = NetworkUtil.getObject(id)
	self.unloadPosition = streamReadInt8(streamId)
    self:run(connection)
 end

function SetUnloadPositionEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, NetworkUtil.getObjectId(self.object))
	streamWriteInt8(streamId, self.unloadPosition)
end

function SetUnloadPositionEvent:run(connection)
    self.object:setUnloadPosition(self.unloadPosition, true)
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetUnloadPositionEvent:new(self.object, self.unloadPosition), nil, connection, self.object)
    end
end

SetMarkerMoveEvent = {}
SetMarkerMoveEvent_mt = Class(SetMarkerMoveEvent, Event)
InitEventClass(SetMarkerMoveEvent, "SetMarkerMoveEvent")

function SetMarkerMoveEvent:emptyNew()
    local self = Event:new(SetMarkerMoveEvent_mt)
    self.className="SetMarkerMoveEvent"
    return self
end

function SetMarkerMoveEvent:new(object, markerX, markerY, markerZ)
    local self = SetMarkerMoveEvent:emptyNew()
    self.object = object
	self.markerX = markerX
	self.markerY = markerY
	self.markerZ = markerZ
    return self
end

function SetMarkerMoveEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId)
	self.object = NetworkUtil.getObject(id)
    self.markerX = streamReadFloat32(streamId)
	self.markerY = streamReadFloat32(streamId)
	self.markerZ = streamReadFloat32(streamId)
    self:run(connection)
 end

function SetMarkerMoveEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, NetworkUtil.getObjectId(self.object))
    streamWriteFloat32(streamId, self.markerX)
	streamWriteFloat32(streamId, self.markerY)
	streamWriteFloat32(streamId, self.markerZ)
end

function SetMarkerMoveEvent:run(connection)
	self.object:setMarkerPosition(self.markerX, self.markerY, self.markerZ, true)
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetMarkerMoveEvent:new(self.object, self.markerX, self.markerY, self.markerZ), nil, connection, self.object)
    end
end