--
-- ExtendedWindrower
--
-- @author	Rockstar49
-- @date	02/06/2024
--


source(g_currentModDirectory .. "scripts/events/ExtendedWindrowerChangeWorkModeEvent.lua")

ExtendedWindrower = {
	SPEC_ENTRY_NAME = string.format("spec_%s.extendedWindrower", g_currentModName),
	SPEC_NAME = g_currentModName .. ".extendedWindrower",
	BASE_XML_KEY = "vehicle.extendedWindrower",
	MODE = {
		TRANSPORT = 1,
		WINDROWER = 2,
		TEDDER = 3
	}
}

function ExtendedWindrower.prerequisitesPresent(specializations)
	local specs = {WorkArea, Windrower, Tedder, AnimatedVehicle, SpeedRotatingParts, Pickup}

	for _, spec in pairs(specs) do
		if not SpecializationUtil.hasSpecialization(spec, specializations) then
			return false
		end
	end

	return true
end

function ExtendedWindrower.initSpecialization()
	local schema = Vehicle.xmlSchema

	schema:setXMLSpecializationType("ExtendedWindrower")
	schema:register(XMLValueType.NODE_INDEX, ExtendedWindrower.BASE_XML_KEY .. ".pickupTrigger#node", "Pickup trigger node")
	schema:register(XMLValueType.NODE_INDEX, ExtendedWindrower.BASE_XML_KEY .. ".modeTrigger#node", "Mode trigger node")
	schema:register(XMLValueType.STRING, ExtendedWindrower.BASE_XML_KEY .. ".windrowerToTedder#animName", "Windrower to tedder animation name")
	schema:register(XMLValueType.STRING, ExtendedWindrower.BASE_XML_KEY .. ".windrower#animName", "Windrower animation name")
	schema:register(XMLValueType.STRING, ExtendedWindrower.BASE_XML_KEY .. ".tedder#animName", "Tedder animation name")
	schema:register(XMLValueType.INT, ExtendedWindrower.BASE_XML_KEY .. ".drawbar#componentJointIndex", "Drawbar default component joint index")
	schema:register(XMLValueType.INT, ExtendedWindrower.BASE_XML_KEY .. ".drawbar.windrowerJoint#component1", "Windrower position component index")
	schema:register(XMLValueType.NODE_INDEX, ExtendedWindrower.BASE_XML_KEY .. ".drawbar.windrowerJoint#node", "Windrower position joint node")
	schema:register(XMLValueType.INT, ExtendedWindrower.BASE_XML_KEY .. ".drawbar.tedderJoint#component1", "Tedder position component index")
	schema:register(XMLValueType.NODE_INDEX, ExtendedWindrower.BASE_XML_KEY .. ".drawbar.tedderJoint#node", "Tedder position joint node")
	schema:register(XMLValueType.BOOL, ExtendedWindrower.BASE_XML_KEY .. ".drawbar#playAttachSound", "Play drawbar attach sound")
	schema:register(XMLValueType.INT, ExtendedWindrower.BASE_XML_KEY .. ".speedLimit#windrower", "Windrower speed limit")
	schema:register(XMLValueType.INT, ExtendedWindrower.BASE_XML_KEY .. ".speedLimit#tedder", "Tedder speed limit")
	SoundManager.registerSampleXMLPaths(schema, ExtendedWindrower.BASE_XML_KEY .. ".sounds", "work")
	schema:setXMLSpecializationType()

	local schemaSavegame = Vehicle.xmlSchemaSavegame

	schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)." .. ExtendedWindrower.SPEC_NAME .. "#mode", "Current mode")
	schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?)." .. ExtendedWindrower.SPEC_NAME .. "#isLowered", "Lower state")
end

function ExtendedWindrower.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "onPickupTriggerCallback", ExtendedWindrower.onPickupTriggerCallback)
	SpecializationUtil.registerFunction(vehicleType, "onModeTriggerCallback", ExtendedWindrower.onModeTriggerCallback)
	SpecializationUtil.registerFunction(vehicleType, "changeWorkMode", ExtendedWindrower.changeWorkMode)
	SpecializationUtil.registerFunction(vehicleType, "detachDrawbar", ExtendedWindrower.detachDrawbar)
	SpecializationUtil.registerFunction(vehicleType, "attachDrawbar", ExtendedWindrower.attachDrawbar)
	SpecializationUtil.registerFunction(vehicleType, "getIsModeAnimationPlaying", ExtendedWindrower.getIsModeAnimationPlaying)
	SpecializationUtil.registerFunction(vehicleType, "getIsWorking", ExtendedWindrower.getIsWorking)
end

function ExtendedWindrower.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanToggleTurnedOn", ExtendedWindrower.getCanToggleTurnedOn)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeTurnedOn", ExtendedWindrower.getCanBeTurnedOn)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsTurnedOn", ExtendedWindrower.getIsTurnedOn)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsWorkAreaActive", ExtendedWindrower.getIsWorkAreaActive)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanAIImplementContinueWork", ExtendedWindrower.getCanAIImplementContinueWork)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsInputAttacherActive", ExtendedWindrower.getIsInputAttacherActive)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getUseTurnedOnSchema", ExtendedWindrower.getUseTurnedOnSchema)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "doCheckSpeedLimit", ExtendedWindrower.doCheckSpeedLimit)
end

function ExtendedWindrower.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onDelete", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onReadStream", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onFinishAnimation", ExtendedWindrower)
	SpecializationUtil.registerEventListener(vehicleType, "onDraw", ExtendedWindrower)
end

function ExtendedWindrower:onLoad(savegame)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]
	local baseKey = ExtendedWindrower.BASE_XML_KEY

	spec.pickupTriggerNode = self.xmlFile:getValue(baseKey .. ".pickupTrigger#node", nil, self.components, self.i3dMappings)

	if spec.pickupTriggerNode ~= nil then
		addTrigger(spec.pickupTriggerNode, "onPickupTriggerCallback", self)
	end

	spec.isPlayerInPickupRange = false

	spec.modeTriggerNode = self.xmlFile:getValue(baseKey .. ".modeTrigger#node", nil, self.components, self.i3dMappings)

	if spec.modeTriggerNode ~= nil then
		addTrigger(spec.modeTriggerNode, "onModeTriggerCallback", self)
	end

	spec.isPlayerInModeRange = false

	spec.mode = ExtendedWindrower.MODE.TRANSPORT

	spec.windrowerToTedderAnimName = self.xmlFile:getValue(baseKey .. ".windrowerToTedder#animName")
	spec.windrowerAnimName = self.xmlFile:getValue(baseKey .. ".windrower#animName")
	spec.tedderAnimName = self.xmlFile:getValue(baseKey .. ".tedder#animName")

	spec.hasModeAnimations = self:getAnimationExists(spec.windrowerAnimName) and self:getAnimationExists(spec.tedderAnimName) and self:getAnimationExists(spec.windrowerToTedderAnimName)

	spec.speedLimits = {
		default = self.speedLimit,
		windrower = self.xmlFile:getValue(baseKey .. ".speedLimit#windrower", self.speedLimit),
		tedder = self.xmlFile:getValue(baseKey .. ".speedLimit#tedder", self.speedLimit)
	}

	spec.cruiseControlModLoaded = g_modIsLoaded["FS25_adjustWorkingSpeed"] or g_modIsLoaded["FS25_SpeedLimitOverride"]

	local drawbar = {}
	local componentJointIndex = self.xmlFile:getValue(baseKey .. ".drawbar#componentJointIndex")

	if componentJointIndex ~= nil then
		local jointDesc = self.componentJoints[componentJointIndex]

		if jointDesc ~= nil then
			drawbar = {
				defaultPos = {
					componentIndex = jointDesc.componentIndices[1],
					jointNode = jointDesc.jointNode
				},
				windrowerPos = {
					componentIndex = self.xmlFile:getValue(baseKey .. ".drawbar.windrowerJoint#component1"),
					jointNode = self.xmlFile:getValue(baseKey .. ".drawbar.windrowerJoint#node", nil, self.components, self.i3dMappings)
				},
				tedderPos = {
					componentIndex = self.xmlFile:getValue(baseKey .. ".drawbar.tedderJoint#component1"),
					jointNode = self.xmlFile:getValue(baseKey .. ".drawbar.tedderJoint#node", nil, self.components, self.i3dMappings)
				},
				jointDesc = jointDesc,
				attachSound = self.xmlFile:getValue(baseKey .. ".drawbar#playAttachSound", false)
			}
		end
	end

	spec.drawbar = drawbar
	spec.drawbarChangeData = nil

	spec.texts = {
		utf8ToLower(g_i18n:getText("ui_off")),
		g_i18n:getText("typeDesc_windrower"),
		g_i18n:getText("typeDesc_tedder")
	}

	if self.isClient then
		spec.workSample = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "work", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.workSoundPlaying = false
	end
end

function ExtendedWindrower:onLoadFinished(savegame)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if savegame ~= nil then
		local key = savegame.key .. "." .. ExtendedWindrower.SPEC_NAME
		local mode = savegame.xmlFile:getValue(key .. "#mode", spec.mode)
		local isLowered = savegame.xmlFile:getValue(key .. "#isLowered", self:getIsLowered())

		self:setPickupState(isLowered, true)
		self:changeWorkMode(mode, true)

		AnimatedVehicle.updateAnimations(self, 99999999, true)
	end

	spec.pickupActivatable = ExtendedWindrowerPickupActivatable.new(self)
	spec.modeActivatable = ExtendedWindrowerModeActivatable.new(self)
end

function ExtendedWindrower:saveToXMLFile(xmlFile, key, usedModNames)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	xmlFile:setValue(key .. "#mode", spec.mode)
	xmlFile:setValue(key .. "#isLowered", self:getIsLowered())
end

function ExtendedWindrower:onDelete()
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.pickupTriggerNode ~= nil then
		removeTrigger(spec.pickupTriggerNode)
	end

	g_currentMission.activatableObjectsSystem:removeActivatable(spec.pickupActivatable)

	if spec.modeTriggerNode ~= nil then
		removeTrigger(spec.modeTriggerNode)
	end

	g_currentMission.activatableObjectsSystem:removeActivatable(spec.modeActivatable)

	if spec.workSample ~= nil then
		g_soundManager:deleteSample(spec.workSample)
	end
end

function ExtendedWindrower:onReadStream(streamId, connection)
	if connection:getIsServer() then
		self:changeWorkMode(streamReadUIntN(streamId, 2), true)
		AnimatedVehicle.updateAnimations(self, 99999999, true)
	end
end

function ExtendedWindrower:onWriteStream(streamId, connection)
	if not connection:getIsServer() then
		local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

		streamWriteUIntN(streamId, spec.mode, 2)
	end
end

function ExtendedWindrower:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.isPlayerInPickupRange then
		spec.pickupActivatable:updateActionEvents()
		self:raiseActive()
	end

	if spec.isPlayerInModeRange then
		spec.modeActivatable:updateActionEvents()
		self:raiseActive()
	end

	if self.isClient then
		if spec.workSample ~= nil then
			local specSpeedRotatingParts = self.spec_speedRotatingParts
			local playWorkSample = specSpeedRotatingParts.speedRotatingParts[1] ~= nil and specSpeedRotatingParts.speedRotatingParts[1].isActive and self:getLastSpeed() > 3

			if playWorkSample then
				if not spec.workSoundPlaying then
					g_soundManager:playSample(spec.workSample)
					spec.workSoundPlaying = true
				end
			else
				if spec.workSoundPlaying then
					g_soundManager:stopSample(spec.workSample)
					spec.workSoundPlaying = false
				end
			end
		end

		local specPickup = self.spec_pickup
		local pickupActionEvent = specPickup.actionEvents[InputAction.LOWER_IMPLEMENT]

		if pickupActionEvent ~= nil then
			g_inputBinding:setActionEventActive(pickupActionEvent.actionEventId, false)
		end
	end
end

function ExtendedWindrower:onFinishAnimation(animName)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.hasModeAnimations then
		if animName == spec.windrowerToTedderAnimName or animName == spec.windrowerAnimName or animName == spec.tedderAnimName then
			self:attachDrawbar()
		end
	end
end

function ExtendedWindrower:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	if self.isClient then
		local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

		g_currentMission:addExtraPrintText(string.format("%s: %s", g_i18n:getText("configuration_workMode"), spec.texts[spec.mode]))

		if not self:getIsAIActive() then
			g_currentMission:addExtraPrintText(g_i18n:getText("info_unfoldWindrower"))
		end
	end
end

function ExtendedWindrower:onPickupTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if g_localPlayer ~= nil and otherId == g_localPlayer.rootNode then
		local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

		if onEnter then
			spec.isPlayerInPickupRange = true
			self:raiseActive()
			g_currentMission.activatableObjectsSystem:addActivatable(spec.pickupActivatable)
		else
			spec.isPlayerInPickupRange = false
			g_currentMission.activatableObjectsSystem:removeActivatable(spec.pickupActivatable)
		end
	end
end

function ExtendedWindrower:onModeTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
	if g_localPlayer ~= nil and otherId == g_localPlayer.rootNode then
		local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

		if onEnter then
			spec.isPlayerInModeRange = true
			self:raiseActive()
			g_currentMission.activatableObjectsSystem:addActivatable(spec.modeActivatable)
		else
			spec.isPlayerInModeRange = false
			g_currentMission.activatableObjectsSystem:removeActivatable(spec.modeActivatable)
		end
	end
end

function ExtendedWindrower:changeWorkMode(newMode, noEventSend)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.mode ~= newMode then
		ExtendedWindrowerChangeWorkModeEvent.sendEvent(self, newMode, noEventSend)

		local lastMode = spec.mode

		local position = spec.drawbar.defaultPos
		local speedLimit = spec.speedLimits.default
		local dropFunc = Windrower.processDropArea

		if newMode == ExtendedWindrower.MODE.TRANSPORT then
			if lastMode == ExtendedWindrower.MODE.WINDROWER then
				self:playAnimation(spec.windrowerAnimName, -1, nil, true)
			else
				self:playAnimation(spec.tedderAnimName, -1, nil, true)
			end
		elseif newMode == ExtendedWindrower.MODE.WINDROWER then
			if lastMode == ExtendedWindrower.MODE.TRANSPORT then
				self:playAnimation(spec.windrowerAnimName, 1, nil, true)
			else
				self:playAnimation(spec.windrowerToTedderAnimName, -1, nil, true)
			end

			position = spec.drawbar.windrowerPos
			speedLimit = spec.speedLimits.windrower
		elseif newMode == ExtendedWindrower.MODE.TEDDER then
			if lastMode == ExtendedWindrower.MODE.TRANSPORT then
				self:playAnimation(spec.tedderAnimName, 1, nil, true)
			else
				self:playAnimation(spec.windrowerToTedderAnimName, 1, nil, true)
			end

			position = spec.drawbar.tedderPos
			speedLimit = spec.speedLimits.tedder
			dropFunc = Tedder.processDropArea
		end

		self:detachDrawbar(position)

		if not spec.cruiseControlModLoaded then
			self.speedLimit = speedLimit
		end

		self.processDropArea = dropFunc
		spec.mode = newMode
	end
end

function ExtendedWindrower:detachDrawbar(position)
	if position ~= nil then
		local componentIndex = position.componentIndex
		local jointNode = position.jointNode

		if componentIndex ~= nil and jointNode ~= nil then
			local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]
			local jointDesc = spec.drawbar.jointDesc

			if jointDesc.componentIndices[1] ~= componentIndex or jointDesc.jointNode ~= jointNode then
				if self.isServer then
					local attacherVehicle = self:getAttacherVehicle()

					if attacherVehicle ~= nil then
						attacherVehicle:detachImplementByObject(self)
					end
				end

				if spec.drawbar.attachSound then
					self:playDetachSound()
				end

				local drawbarComponentNode = self.components[jointDesc.componentIndices[2]].node

				setVisibility(drawbarComponentNode, false)
				removeFromPhysics(drawbarComponentNode)

				if self.isServer and jointDesc.jointIndex ~= 0 then
					removeJoint(jointDesc.jointIndex)
					jointDesc.jointIndex = 0
				end

				spec.drawbarChangeData = {
					componentIndex = componentIndex,
					jointNode = jointNode
				}

				if not spec.hasModeAnimations then
					self:attachDrawbar()
				end
			end
		end
	end
end

function ExtendedWindrower:attachDrawbar()
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.drawbarChangeData ~= nil then
		local jointDesc = spec.drawbar.jointDesc
		local drawbarComponentIndex = jointDesc.componentIndices[2]
		local drawbarComponentNode = self.components[drawbarComponentIndex].node
		local componentIndex = spec.drawbarChangeData.componentIndex
		local jointNode = spec.drawbarChangeData.jointNode
		local x, y, z = getWorldTranslation(jointNode)
		local xRot, yRot, zRot = getWorldRotation(jointNode)

		self:setWorldPosition(x, y, z, xRot, yRot, zRot, drawbarComponentIndex, true)

		if spec.drawbar.attachSound then
			self:playAttachSound()
		end

		addToPhysics(drawbarComponentNode)
		setVisibility(drawbarComponentNode, true)

		jointDesc.componentIndices[1] = componentIndex
		jointDesc.jointNode = jointNode
		jointDesc.jointNodeActor1 = jointDesc.jointNode

		if self.isServer then
			jointDesc.jointLocalPoses = {}

			local trans = {localToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0)}
			local rot = {localRotationToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0)}

			jointDesc.jointLocalPoses[1] = {trans=trans, rot=rot}

			trans = {localToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0)}
			rot = {localRotationToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0)}

			jointDesc.jointLocalPoses[2] = {trans=trans, rot=rot}

			self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc)
		end

		for _, collisionPair in pairs(self.collisionPairs) do
			local component1Node = collisionPair.component1.node
			local component2Node = collisionPair.component2.node

			if component1Node == drawbarComponentNode or component2Node == drawbarComponentNode then
				setPairCollision(component1Node, component2Node, collisionPair.enabled)
			end
		end

		spec.drawbarChangeData = nil
	end
end

function ExtendedWindrower:getIsModeAnimationPlaying()
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if not spec.hasModeAnimations then
		return false
	end

	return self:getIsAnimationPlaying(spec.windrowerToTedderAnimName) or self:getIsAnimationPlaying(spec.windrowerAnimName) or self:getIsAnimationPlaying(spec.tedderAnimName)
end

function ExtendedWindrower:getIsWorking()
	local specWindrower = self.spec_windrower
	local specTedder = self.spec_tedder

	return specWindrower.isWorking or specTedder.isWorking
end

function ExtendedWindrower:getCanToggleTurnedOn(superFunc)
	return false
end

function ExtendedWindrower:getCanBeTurnedOn(superFunc)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.mode == ExtendedWindrower.MODE.TRANSPORT then
		return false
	end

	return superFunc(self)
end

function ExtendedWindrower:getIsTurnedOn(superFunc)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if self:getIsLowered() and spec.mode ~= ExtendedWindrower.MODE.TRANSPORT then
		return true
	end

	return superFunc(self)
end

function ExtendedWindrower:getIsWorkAreaActive(superFunc, workArea)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if not self:getIsLowered() or self:getIsModeAnimationPlaying() then
		return false
	end

	if workArea.type == WorkAreaType.TEDDER and spec.mode ~= ExtendedWindrower.MODE.TEDDER then
		return false
	end

	if workArea.type == WorkAreaType.WINDROWER and spec.mode ~= ExtendedWindrower.MODE.WINDROWER then
		return false
	end

	return superFunc(self, workArea)
end

function ExtendedWindrower:getCanAIImplementContinueWork(superFunc)
	local canContinue, stopAI, stopReason = superFunc(self)
	local spec = self[ExtendedWindrower.SPEC_ENTRY_NAME]

	if spec.mode == ExtendedWindrower.MODE.TRANSPORT then
		return false, true, nil
	end

	if canContinue and self:getAINeedsLowering() then
		local specPickup = self.spec_pickup

		if specPickup.animationName ~= "" then
			local time = self:getAnimationTime(specPickup.animationName)

			canContinue = time == 0 or time == 1
		end
	end

	return canContinue, stopAI, stopReason
end

function ExtendedWindrower:getIsInputAttacherActive(superFunc, inputAttacherJoint)
	if self:getIsModeAnimationPlaying() then
		return false
	end

	return superFunc(self, inputAttacherJoint)
end

function ExtendedWindrower:getUseTurnedOnSchema(superFunc)
	return false
end

function ExtendedWindrower:doCheckSpeedLimit(superFunc)
	return superFunc(self) or self:getIsTurnedOn()
end

function ExtendedWindrower:getLoweringActionText()
	local text = g_i18n:getText("input_EXTENDED_WINDROWER_PICKUP")

	if self:getIsLowered() then
		text = g_i18n:getText("action_raiseWindrower")
	end

	return text
end


ExtendedWindrowerPickupActivatable = {}

local ExtendedWindrowerPickupActivatable_mt = Class(ExtendedWindrowerPickupActivatable)

function ExtendedWindrowerPickupActivatable.new(vehicle)
	local self = setmetatable({}, ExtendedWindrowerPickupActivatable_mt)

	self.vehicle = vehicle
	self.activateText = ""

	return self
end

function ExtendedWindrowerPickupActivatable:registerCustomInput(inputContext)
	if inputContext ~= PlayerInputComponent.INPUT_CONTEXT_NAME then
		return
	end

	local _, actionEventIdPickup = g_inputBinding:registerActionEvent(InputAction.EXTENDED_WINDROWER_PICKUP, self, self.actionEventPickup, false, true, false, false)

	g_inputBinding:setActionEventTextPriority(actionEventIdPickup, GS_PRIO_VERY_HIGH)
	g_inputBinding:setActionEventTextVisibility(actionEventIdPickup, true)

	self.actionEventIdPickup = actionEventIdPickup
end

function ExtendedWindrowerPickupActivatable:removeCustomInput(inputContext)
	g_inputBinding:removeActionEventsByTarget(self)

	self.actionEventIdPickup = nil
end

function ExtendedWindrowerPickupActivatable:actionEventPickup(actionName, inputValue, callbackState, isAnalog)
	Pickup.actionEventTogglePickup(self.vehicle, actionName, inputValue, callbackState, isAnalog)
end

function ExtendedWindrowerPickupActivatable:updateActionEvents()
	if self.actionEventIdPickup ~= nil then
		local vehicle = self.vehicle
		local specPickup = vehicle.spec_pickup

		g_inputBinding:setActionEventText(self.actionEventIdPickup, ExtendedWindrower.getLoweringActionText(vehicle))
		g_inputBinding:setActionEventActive(self.actionEventIdPickup, not vehicle:getIsAIActive() and not vehicle:getIsWorking() and vehicle:getCanChangePickupState(specPickup, not specPickup.isLowered))
	end
end

function ExtendedWindrowerPickupActivatable:getHasAccess(farmId)
	return g_currentMission.accessHandler:canFarmAccessOtherId(farmId, self.vehicle:getOwnerFarmId())
end

function ExtendedWindrowerPickupActivatable:run()
end


ExtendedWindrowerModeActivatable = {}

local ExtendedWindrowerModeActivatable_mt = Class(ExtendedWindrowerModeActivatable)

function ExtendedWindrowerModeActivatable.new(vehicle)
	local self = setmetatable({}, ExtendedWindrowerModeActivatable_mt)

	self.vehicle = vehicle
	self.activateText = ""

	return self
end

function ExtendedWindrowerModeActivatable:registerCustomInput(inputContext)
	if inputContext ~= PlayerInputComponent.INPUT_CONTEXT_NAME then
		return
	end

	local _, actionEventIdTransport = g_inputBinding:registerActionEvent(InputAction.EXTENDED_WINDROWER_TRANSPORT_MODE, self, self.actionCallback, false, true, false, false)

	g_inputBinding:setActionEventTextPriority(actionEventIdTransport, GS_PRIO_VERY_HIGH)
	g_inputBinding:setActionEventTextVisibility(actionEventIdTransport, true)

	self.actionEventIdTransport = actionEventIdTransport

	local _, actionEventIdWindrower = g_inputBinding:registerActionEvent(InputAction.EXTENDED_WINDROWER_WINDROWER_MODE, self, self.actionCallback, false, true, false, false)

	g_inputBinding:setActionEventTextPriority(actionEventIdWindrower, GS_PRIO_VERY_HIGH)
	g_inputBinding:setActionEventTextVisibility(actionEventIdWindrower, true)

	self.actionEventIdWindrower = actionEventIdWindrower

	local _, actionEventIdTedder = g_inputBinding:registerActionEvent(InputAction.EXTENDED_WINDROWER_TEDDER_MODE, self, self.actionCallback, false, true, false, false)

	g_inputBinding:setActionEventTextPriority(actionEventIdTedder, GS_PRIO_VERY_HIGH)
	g_inputBinding:setActionEventTextVisibility(actionEventIdTedder, true)

	self.actionEventIdTedder = actionEventIdTedder
end

function ExtendedWindrowerModeActivatable:removeCustomInput(inputContext)
	g_inputBinding:removeActionEventsByTarget(self)

	self.actionEventIdTransport = nil
	self.actionEventIdWindrower = nil
	self.actionEventIdTedder = nil
end

function ExtendedWindrowerModeActivatable:actionCallback(actionName, inputValue, callbackState, isAnalog)
	local vehicle = self.vehicle
	local mode = ExtendedWindrower.MODE.TRANSPORT

	if actionName == InputAction.EXTENDED_WINDROWER_WINDROWER_MODE then
		mode = ExtendedWindrower.MODE.WINDROWER
	elseif actionName == InputAction.EXTENDED_WINDROWER_TEDDER_MODE then
		mode = ExtendedWindrower.MODE.TEDDER
	end

	vehicle:changeWorkMode(mode)
end

function ExtendedWindrowerModeActivatable:updateActionEvents()
	local vehicle = self.vehicle
	local spec = vehicle[ExtendedWindrower.SPEC_ENTRY_NAME]
	local eventActive = not vehicle:getIsAIActive() and not vehicle:getIsWorking() and not vehicle:getIsModeAnimationPlaying()
	local currentMode = spec.mode

	if self.actionEventIdTransport ~= nil then
		g_inputBinding:setActionEventActive(self.actionEventIdTransport, eventActive and currentMode ~= ExtendedWindrower.MODE.TRANSPORT)
	end

	if self.actionEventIdWindrower ~= nil then
		g_inputBinding:setActionEventActive(self.actionEventIdWindrower, eventActive and currentMode ~= ExtendedWindrower.MODE.WINDROWER)
	end

	if self.actionEventIdTedder ~= nil then
		g_inputBinding:setActionEventActive(self.actionEventIdTedder, eventActive and currentMode ~= ExtendedWindrower.MODE.TEDDER)
	end
end

function ExtendedWindrowerModeActivatable:getHasAccess(farmId)
	return g_currentMission.accessHandler:canFarmAccessOtherId(farmId, self.vehicle:getOwnerFarmId())
end

function ExtendedWindrowerModeActivatable:run()
end