--[[
    AttachConnectionHosesManually

    Changes the attacher joints behavior to allow attaching Hoses manually.

	@author: 		BayernGamers
	@date: 			31.05.2025
	@version:		1.0

	History:		v1.0 @31.05.2025 - initial implementation in FS25
                    ------------------------------------------------------------------------------------------------------

	
	License:        Terms:
                        Usage:
                            Feel free to use this work as-is as long as you adhere to the following terms:
						Attribution:
							You must give appropriate credit to the original author when using this work.
						No Derivatives:
							You may not alter, transform, or build upon this work in any way.
						Usage:
							The work may be used for personal and commercial purposes, provided it is not modified or adapted.
						Additional Clause:
							This script may not be converted, adapted, or incorporated into any other game versions or platforms except by GIANTS Software.
]]
source(Utils.getFilename("scripts/utils/Logger.lua", g_currentModDirectory))
source(Utils.getFilename("scripts/utils/ExtendedSpecializationUtil.lua", g_currentModDirectory))
source(Utils.getFilename("scripts/events/ToggleHoseAttachStateEvent.lua", g_currentModDirectory))
source(Utils.getFilename("scripts/AttachImplementsManuallySettingsManager.lua", g_currentModDirectory))
source(Utils.getFilename("scripts/utils/ConnectionHoseUtil.lua", g_currentModDirectory))

local log = Logger.new(true, Logger.DEBUG_LEVELS.HIGH, "AttachConnectionHosesManually.lua")

AttachConnectionHosesManually = {}
AttachConnectionHosesManually.MOD_DIRECTORY = g_currentModDirectory
AttachConnectionHosesManually.MOD_NAME = g_currentModName

function AttachConnectionHosesManually.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(ConnectionHoses, specializations) and SpecializationUtil.hasSpecialization(AttachImplementsManually, specializations)
end

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

    schema:setXMLSpecializationType("AttachImplementsManually")
    schema:register(XMLValueType.BOOL, "vehicle.attachImplementsManually.hookLift#invertHoseStatus", "In case a hooklift's folding state is inverted, invert the hose attach status as well")
    schema:setXMLSpecializationType()
    
    local schemaSavegame = Vehicle.xmlSchemaSavegame
    schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).FS25_attachImplementsManually.attachConnectionHosesManually.attachedImplement(?)#jointIndex", "Index of attacherJoint")
    schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).FS25_attachImplementsManually.attachConnectionHosesManually.attachedImplement(?)#attachedVehicleUniqueId", "Unique id of attached vehicle")
    schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).FS25_attachImplementsManually.attachConnectionHosesManually.attachedImplement(?)#inputJointIndex", "Index of input attacher joint on the attached vehicle")

    schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?).FS25_attachImplementsManually.attachConnectionHosesManually.attachedImplement(?)#hosesAttached")
end

function AttachConnectionHosesManually.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onPreLoad", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdate", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onDelete", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onRegisterExternalActionEvents", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onReadStream", AttachConnectionHosesManually)
    SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", AttachConnectionHosesManually)

    ExtendedSpecializationUtil.registerOverwrittenEventListener(vehicleType, "onPostAttach", ConnectionHoses, AttachConnectionHosesManually)
    ExtendedSpecializationUtil.registerOverwrittenEventListener(vehicleType, "onPreDetach", ConnectionHoses, AttachConnectionHosesManually)
end

function AttachConnectionHosesManually.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "loadAttachedConnectionHosesFromXMLFile", AttachConnectionHosesManually.loadAttachedConnectionHosesFromXMLFile)
    SpecializationUtil.registerFunction(vehicleType, "onAttachConnectionHosesManuallyVehicleLoaded", AttachConnectionHosesManually.onAttachConnectionHosesManuallyVehicleLoaded)
    SpecializationUtil.registerFunction(vehicleType, "toggleConnectionHoseAttachState", AttachConnectionHosesManually.toggleConnectionHoseAttachState)
end

function AttachConnectionHosesManually.registerOverwrittenFunctions(vehicleType)
    ExtendedSpecializationUtil.registerAppendedFunction(vehicleType, "onAttacherJointsVehicleLoaded", AttachConnectionHosesManually.onAttachConnectionHosesManuallyVehicleLoaded)
end

function AttachConnectionHosesManually:onPreLoad(savegame)
    self.spec_attachConnectionHosesManually = {}
end

function AttachConnectionHosesManually:onLoad(savegame)
    local spec = self.spec_attachConnectionHosesManually

    spec.actionEventAttachHosesData = nil
    spec.modSettingsManager = AttachImplementsManuallySettingsManager.getInstance()
    spec.attachedHoses = {}
    spec.connectionHosesAttachmentDataToLoad = {}
    spec.connectionHosesToAttach = {}
    spec.delayedMountingHoses = true
    spec.dt = 0
    spec.invertHoseStatusForHookLift = self.xmlFile:getValue("vehicle.attachImplementsManually.hookLift#invertHoseStatus", false)

    g_messageCenter:subscribe(MessageType.VEHICLE_RESET, AttachConnectionHosesManually.onVehicleReset, self)
end

function AttachConnectionHosesManually:onLoadFinished(savegame)
    local spec = self.spec_connectionHoses
    local spec_attachImplementsManually = self.spec_attachImplementsManually

    if spec_attachImplementsManually ~= nil and spec_attachImplementsManually.externalControlTrigger ~= nil then
        local names = {
            --"attachImplementManually",
            --"attachPTOManually",
            "attachHosesManually",
        }
        local funcKey = ""

        for _, name in ipairs(names) do
            SpecializationUtil.raiseEvent(self, "onRegisterExternalActionEvents", spec_attachImplementsManually.externalControlTrigger, name, self.xmlFile, funcKey)
        end
    end

    self:loadAttachedConnectionHosesFromXMLFile(savegame)
end

function AttachConnectionHosesManually:saveToXMLFile(xmlFile, key, usedModNames)
    local spec = self.spec_attacherJoints
    local spec_attachConnectionHosesManually = self.spec_attachConnectionHosesManually

    if spec.attacherJoints ~= nil then
        for index, implement in ipairs(spec.attachedImplements) do
            if implement.object ~= nil then
                local attacherJointKey = string.format("%s.attachedImplement(%d)", key, index - 1)
                
                xmlFile:setValue(attacherJointKey .. "#jointIndex", implement.jointDescIndex)
                xmlFile:setValue(attacherJointKey .. "#attachedVehicleUniqueId", implement.object:getUniqueId())
                xmlFile:setValue(attacherJointKey .. "#inputJointIndex", implement.inputJointDescIndex)

                xmlFile:setValue(attacherJointKey .. "#hosesAttached", spec_attachConnectionHosesManually.attachedHoses[implement.jointDescIndex] ~= nil)
            end
        end
    end
end

function AttachConnectionHosesManually:loadAttachedConnectionHosesFromXMLFile(savegame)
    local spec = self.spec_attachConnectionHosesManually

    if savegame ~= nil and savegame.xmlFile ~= nil then
        local xmlFile = savegame.xmlFile

        for index, attachedImplementKey in xmlFile:iterator(savegame.key..".FS25_attachImplementsManually.attachConnectionHosesManually.attachedImplement") do
            local ptoAttachmentData = {}
            ptoAttachmentData.jointIndex = xmlFile:getValue(attachedImplementKey .. "#jointIndex")
            ptoAttachmentData.attachedVehicleUniqueId = xmlFile:getValue(attachedImplementKey .. "#attachedVehicleUniqueId")
            ptoAttachmentData.inputJointIndex = xmlFile:getValue(attachedImplementKey .. "#inputJointIndex")
            ptoAttachmentData.hosesAttached = xmlFile:getValue(attachedImplementKey .. "#hosesAttached", false)

            local vehicle = g_currentMission.vehicleSystem:getVehicleByUniqueId(ptoAttachmentData.attachedVehicleUniqueId)
            table.insert(spec.connectionHosesAttachmentDataToLoad, ptoAttachmentData)

            if vehicle ~= nil then
                self:onAttachConnectionHosesManuallyVehicleLoaded(vehicle)
            end
        end
    end
end

function AttachConnectionHosesManually:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
    local spec = self.spec_attachConnectionHosesManually
    local spec_attachImplementsManually = self.spec_attachImplementsManually
    local externalControlTrigger = spec_attachImplementsManually ~= nil and spec_attachImplementsManually.externalControlTrigger or nil
    local isPlayerInRange = externalControlTrigger ~= nil and externalControlTrigger.isPlayerInRange
    local isActive = self.getIsActive ~= nil and self:getIsActive()

    if not self.isClient then
        return
    end

    if not isActive and not isPlayerInRange then
        return
    end

    spec.dt = spec.dt + dt

    if spec.dt >= 100 then
        AttachConnectionHosesManually.updateActionEvents(self)
        spec.dt = 0
    end
end

function AttachConnectionHosesManually:onWriteStream(streamId, connection)
    local spec = self.spec_attachConnectionHosesManually
    log:printDevInfo("onWriteStream called for vehicle: " .. tostring(self:getName()), Logger.DEBUG_LEVELS.HIGH)

    local numAttachedHoses = 0
    -- Fix weird # behavior
    for _, _ in pairs(spec.attachedHoses) do
        numAttachedHoses = numAttachedHoses + 1
    end
    log:printDevInfo("numAttachedHoses: " .. tostring(numAttachedHoses), Logger.DEBUG_LEVELS.HIGH)

    streamWriteInt8(streamId, numAttachedHoses)
    for index, attachedHose in pairs(spec.attachedHoses) do
        log:printDevInfo("Writing attached hose for jointDescIndex: " .. tostring(index) .. " with attachedHose: " .. tostring(attachedHose), Logger.DEBUG_LEVELS.HIGH)
        streamWriteInt8(streamId, index)
        streamWriteBool(streamId, attachedHose)
    end
end

function AttachConnectionHosesManually:onReadStream(streamId, connection)
    local spec = self.spec_attachConnectionHosesManually
    log:printDevInfo("onReadStream called for vehicle: " .. tostring(self:getName()), Logger.DEBUG_LEVELS.HIGH)

    local numAttachedHoses = streamReadInt8(streamId)
    log:printDevInfo("numAttachedHoses: " .. tostring(numAttachedHoses), Logger.DEBUG_LEVELS.HIGH)
    for i = 1, numAttachedHoses do
        local jointDescIndex = streamReadInt8(streamId)
        local attachedHose = streamReadBool(streamId)
        log:printDevInfo("Read attached hose for jointDescIndex: " .. tostring(jointDescIndex) .. " with attachedHose: " .. tostring(attachedHose), Logger.DEBUG_LEVELS.HIGH)

        if attachedHose then
            log:printDevInfo("Marking hoses for jointDescIndex: " .. tostring(jointDescIndex) .. " as 'has to be attached'", Logger.DEBUG_LEVELS.HIGH)
            spec.connectionHosesToAttach[jointDescIndex] = true
        end
    end

    log:printDevInfo("onReadStream: Got delayedMountingHoses: " .. tostring(spec.delayedMountingHoses), Logger.DEBUG_LEVELS.HIGH)
    if self.isClient and not spec.delayedMountingHoses and numAttachedHoses > 0 then
        for index, _ in pairs(spec.connectionHosesToAttach) do
            local implement = nil

            if self.getImplementFromAttacherJointIndex ~= nil then
                implement = self:getImplementFromAttacherJointIndex(index)
            end

            if implement ~= nil and implement.object ~= nil then
                log:printDevInfo("onReadStream: Attaching hoses for implement: " .. tostring(implement.object:getName()) .. " at joint index: " .. tostring(index), Logger.DEBUG_LEVELS.HIGH)
                self:toggleConnectionHoseAttachState(implement.object)
                spec.connectionHosesToAttach[index] = nil
            else
                for i, attachedImplement in pairs(self.spec_attacherJoints.attachedImplements) do
                    log:printDevInfo("onReadStream: Index: " .. tostring(i) .. " Implement: " .. tostring(attachedImplement), Logger.DEBUG_LEVELS.HIGH)
                    if attachedImplement ~= nil and attachedImplement.object ~= nil then
                        local attacherJointIndex = self:getAttacherJointIndexFromObject(attachedImplement.object)
                        log:printDevInfo("onReadStream: Attacher joint index: " .. tostring(attacherJointIndex), Logger.DEBUG_LEVELS.HIGH)
                        if attacherJointIndex ~= nil and attacherJointIndex == index then
                            log:printDevInfo("onReadStream: Attaching hoses for implement: " .. tostring(attachedImplement.object:getName()) .. " at joint index: " .. tostring(index), Logger.DEBUG_LEVELS.HIGH)
                            self:toggleConnectionHoseAttachState(attachedImplement.object)
                            spec.connectionHosesToAttach[index] = nil
                            break
                        end
                    end
                end
            end
        end

        numAttachedHoses = 0
        for i, _ in pairs(spec.connectionHosesToAttach) do
            numAttachedHoses = numAttachedHoses + 1
        end
    end

    log:printDevInfo("onReadStream: Number of hoses to attach after processing: " .. tostring(numAttachedHoses), Logger.DEBUG_LEVELS.HIGH)
    if numAttachedHoses == 0 then
        log:printDevInfo("onReadStream: No hoses to attach found, resetting table", Logger.DEBUG_LEVELS.HIGH)
        spec.connectionHosesToAttach = {}
    end
end

function AttachConnectionHosesManually:onPostAttach(attacherVehicle, inputJointDescIndex, jointDescIndex)
    local spec = attacherVehicle.spec_attachConnectionHosesManually

    if spec ~= nil then
        if self.isClient then
            log:printDevInfo("onPostAttach called with attachable: " .. tostring(self:getName()) .. "for vehicle: " .. tostring(attacherVehicle:getName()), Logger.DEBUG_LEVELS.HIGH)

            local numAttachedHoses = 0
            for index, _ in pairs(spec.connectionHosesToAttach) do
                numAttachedHoses = numAttachedHoses + 1

                if index == jointDescIndex then
                    log:printDevInfo("onPostAttach: toggling hose attach state", Logger.DEBUG_LEVELS.HIGH)
                    attacherVehicle:toggleConnectionHoseAttachState(self)
                    spec.connectionHosesToAttach[index] = nil
                end
            end

            if self:getIsSynchronized() and numAttachedHoses == 0 then
                log:printDevInfo("onPostAttach : No hoses to attach found, resetting table", Logger.DEBUG_LEVELS.HIGH)
                spec.connectionHosesToAttach = {}
            end
        end

        local info = {
            attacherVehicle = attacherVehicle,
            attachable = self,
            attacherVehicleJointDescIndex = jointDescIndex,
            attachableJointDescIndex = inputJointDescIndex
        }

        log:printDevInfo("onPreAttach called isServer:" .. tostring(self.isServer), Logger.DEBUG_LEVELS.HIGH, Logger.DEBUG_LEVELS.HIGH)
        log:printDevInfo("getEnableAttachImplementsManually: " .. tostring(spec.modSettingsManager:getEnableAttachImplementsManually()), Logger.DEBUG_LEVELS.HIGH)
        log:printDevInfo("getIsManualAttachForNexatDisabled: " .. tostring(AttachImplementsManually.getIsManualAttachForNexatDisabled(self)), Logger.DEBUG_LEVELS.HIGH)
        --log:printDevInfo("getAttachHosesIfAttachedFromInside: " .. tostring(AttacherJointUtil.getAttachHosesIfAttachedFromInside(info)))
        log:printDevInfo("getUsesFrontloaderToolAttacher: " .. tostring(AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true)), Logger.DEBUG_LEVELS.HIGH)
        if self.isServer then
            if not spec.modSettingsManager:getEnableAttachImplementsManually() or AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true) or AttachImplementsManually.getIsManualAttachForNexatDisabled(self)
                or AttacherJointUtil.getAttachHosesIfAttachedFromInside(info)
            then
                log:printDevInfo("Calling original onPostAttach", Logger.DEBUG_LEVELS.HIGH)
                ConnectionHoses.onPostAttach(self, attacherVehicle, inputJointDescIndex, jointDescIndex)
                spec.attachedHoses[jointDescIndex] = true
            end
        end

        spec.delayedMountingHoses = false
        log:printDevInfo("onPostAttach: delayedMountingHoses set to false for vehicle: " .. tostring(attacherVehicle:getName()), Logger.DEBUG_LEVELS.HIGH)
    else
        ConnectionHoses.onPostAttach(self, attacherVehicle, inputJointDescIndex, jointDescIndex)
    end
end

function AttachConnectionHosesManually:onPreDetach(attacherVehicle, implement)
    local spec = attacherVehicle.spec_attachConnectionHosesManually
    
    log:printDevInfo("onPreDetach called with implement: " .. tostring(implement.object:getName()) .. " for vehicle: " .. tostring(attacherVehicle:getName()), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("isDeleting: " .. tostring(implement.object:getIsBeingDeleted()), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("isDeleting(attacherVehicle): " .. tostring(attacherVehicle:getIsBeingDeleted()), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("isReconfiguring(self): " .. tostring(implement.object.isReconfiguring), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("isReconfiguring(attacherVehicle): " .. tostring(attacherVehicle.isReconfiguring), Logger.DEBUG_LEVELS.HIGH)

    local function callPreDetach()
        log:printDevInfo("onPreDetach: Detaching hoses for implement: " .. tostring(implement.object:getName()), Logger.DEBUG_LEVELS.HIGH)
        ConnectionHoses.onPreDetach(self, attacherVehicle, implement)

        if spec ~= nil then
            spec.attachedHoses[implement.jointDescIndex] = nil
        end
    end

    log:printDevInfo("onPreDetach called isServer:" .. tostring(self.isServer), Logger.DEBUG_LEVELS.HIGH)
    if self.isServer then
        local info = {
            attacherVehicle = attacherVehicle,
            attachable = implement.object,
            attacherVehicleJointDescIndex = implement.jointDescIndex,
            attachableJointDescIndex = implement.inputJointDescIndex
        }

        if (spec ~= nil and spec.modSettingsManager ~= nil and not spec.modSettingsManager:getEnableAttachImplementsManually()) or AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true) or AttachImplementsManually.getIsManualAttachForNexatDisabled(self)
            or AttacherJointUtil.getAttachHosesIfAttachedFromInside(info)
        then
            callPreDetach()
        end
    end

    if spec == nil or implement.object:getIsBeingDeleted() or implement.object.isReconfiguring or attacherVehicle.isReconfiguring or attacherVehicle:getIsBeingDeleted() then
        callPreDetach()
    end
end

function AttachConnectionHosesManually:onRegisterExternalActionEvents(trigger, name, xmlFile, key)
    local spec = self.spec_attachConnectionHosesManually

    if name == "attachHosesManually" then
        self:registerExternalActionEvent(trigger, name, AttachConnectionHosesManually.externalActionEventRegister, AttachConnectionHosesManually.externalActionEventUpdate)
    end
end

function AttachConnectionHosesManually.externalActionEventRegister(data, vehicle)
    local spec = vehicle.spec_attachConnectionHosesManually
    local spec_attacherJoints = vehicle.spec_attacherJoints
    local spec_attachImplementsManually = vehicle.spec_attachImplementsManually


    local function createVehicleActionEvent(func)
        return function (...)
            return func(vehicle, ...)
        end
    end

    if spec_attachImplementsManually.externalControlTrigger.isPlayerInRange then
        local actionEvent = createVehicleActionEvent(AttachConnectionHosesManually.actionEventAttachHoses)
        local _, actionEventId = g_inputBinding:registerActionEvent(InputAction.AIM_ATTACH_HOSES, data, actionEvent, false, true, false, true)
        data.actionEventId = actionEventId
        g_inputBinding:setActionEventText(data.actionEventId, g_i18n:getText("input_AIM_ATTACH_HOSES"))
        g_inputBinding:setActionEventTextPriority(data.actionEventId, GS_PRIO_VERY_HIGH)
        g_inputBinding:setActionEventActive(data.actionEventId, false)
    end

    spec.actionEventAttachHosesData = data
end

function AttachConnectionHosesManually.externalActionEventUpdate(data, vehicle)
    local spec = vehicle.spec_attachConnectionHosesManually
    local spec_attachImplementsManually = vehicle.spec_attachImplementsManually
    local isDetaching = false

    local attachedVehicle = spec_attachImplementsManually.closestVehicleInPlayerRange
    local isHookLiftTrailer = vehicle.spec_hookLiftTrailer ~= nil

    if spec_attachImplementsManually ~= nil and spec_attachImplementsManually.isDetachingImplement then
        isDetaching = true
    end

    if spec.modSettingsManager:getEnableAttachImplementsManually() and not isDetaching and not AttachImplementsManually.getIsManualAttachForNexatDisabled(vehicle) then
        if attachedVehicle ~= nil then
            local implementIndex = vehicle:getImplementIndexByObject(attachedVehicle)
            local jointDescIndex = vehicle:getAttacherJointIndexFromImplementIndex(implementIndex)

            local info = {
                attacherVehicle = vehicle,
                attachable = attachedVehicle,
                attacherVehicleJointDescIndex = jointDescIndex,
                attachableJointDescIndex = attachedVehicle:getActiveInputAttacherJointDescIndex()
            }
            local usesFrontloaderFastConnector = AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true)
            local attachesHosesIfAttachedFromInside = AttacherJointUtil.getAttachHosesIfAttachedFromInside(info)
            
            --[[
            local hasConnectionHoses = false
            if SpecializationUtil.hasSpecialization(ConnectionHoses, attachedVehicle.specializations) then
                local spec_connectionHoses = attachedVehicle.spec_connectionHoses

                hasConnectionHoses = #spec_connectionHoses.toolConnectorHoses > 0 or #spec_connectionHoses.hoseNodes > 0 or #spec_connectionHoses.customHoseTargets > 0 or #spec_connectionHoses.customHoses > 0 or #spec_connectionHoses.hoseSkipNodes > 0
            end
            ]]

            local inputAttacherJointIndex = attachedVehicle:getActiveInputAttacherJointDescIndex()
            local hasConnectionHoses = ConnectionHoseUtil.getHasInputConnectionHoses(attachedVehicle, inputAttacherJointIndex)

            g_inputBinding:setActionEventActive(data.actionEventId, hasConnectionHoses)

            if usesFrontloaderFastConnector or attachesHosesIfAttachedFromInside then
                g_inputBinding:setActionEventTextVisibility(data.actionEventId, false)
            end

            if isHookLiftTrailer then
                local inputAttacherJointDescIndex = attachedVehicle:getActiveInputAttacherJointDescIndex()
                local inputAttacherJoint = attachedVehicle:getInputAttacherJointByJointDescIndex(inputAttacherJointDescIndex)

                if inputAttacherJoint ~= nil and inputAttacherJoint.jointType == AttacherJoints.JOINT_TYPE_HOOKLIFT then

                    if (not spec.invertHoseStatusForHookLift and vehicle.getIsUnfolded ~= nil and not vehicle:getIsUnfolded()) or (spec.invertHoseStatusForHookLift and vehicle.getIsUnfolded ~= nil and vehicle:getIsUnfolded()) then
                        g_inputBinding:setActionEventActive(data.actionEventId, false)
                    else
                        g_inputBinding:setActionEventActive(data.actionEventId, hasConnectionHoses)
                    end
                end
            end

            if spec.attachedHoses[jointDescIndex] == nil then
                g_inputBinding:setActionEventText(data.actionEventId, g_i18n:getText("input_AIM_ATTACH_HOSES"))
                g_inputBinding:setActionEventTextPriority(data.actionEventId, GS_PRIO_VERY_HIGH)
            else
                if attachedVehicle.getIsTurnedOn == nil or (attachedVehicle.getIsTurnedOn ~= nil and not attachedVehicle:getIsTurnedOn()) then
                    g_inputBinding:setActionEventText(data.actionEventId, g_i18n:getText("input_AIM_DETACH_HOSES"))
                    g_inputBinding:setActionEventTextPriority(data.actionEventId, GS_PRIO_VERY_HIGH)
                else
                    g_inputBinding:setActionEventTextVisibility(data.actionEventId, false)
                end
            end
        else
            --g_inputBinding:setActionEventActive(data.actionEventId, false)
        end
    else
        if data ~= nil then
            g_inputBinding:setActionEventActive(data.actionEventId, false)
        end
    end
end

function AttachConnectionHosesManually:actionEventAttachHoses(actionName, actionName, inputValue, callbackState, isAnalog)
    local spec = self.spec_attachConnectionHosesManually
    local spec_attachImplementsManually = self.spec_attachImplementsManually
    local spec_attacherJoints = self.spec_attacherJoints

    log:printDevInfo("self: " .. tostring(self:getName()), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("spec_attachImplementsManually: " .. tostring(spec_attachImplementsManually), Logger.DEBUG_LEVELS.HIGH)
    local attachedVehicle = spec_attachImplementsManually.closestVehicleInPlayerRange
    log:printDevInfo("attachedVehicle: " .. tostring(attachedVehicle), Logger.DEBUG_LEVELS.HIGH)
    log:printDevInfo("isServer: " .. tostring(self.isServer), Logger.DEBUG_LEVELS.HIGH)

    if self.isServer then
        self:toggleConnectionHoseAttachState(attachedVehicle)
    else
        ToggleHoseAttachStateEvent.sendEvent(self, attachedVehicle, false)
    end
end

function AttachConnectionHosesManually:updateActionEvents()
    local spec = self.spec_attachConnectionHosesManually
    local spec_attachImplementsManually = self.spec_attachImplementsManually

    if spec_attachImplementsManually.externalControlTrigger ~= nil and spec_attachImplementsManually.externalControlTrigger.isPlayerInRange and spec.actionEventAttachHosesData ~= nil then
        AttachConnectionHosesManually.externalActionEventUpdate(spec.actionEventAttachHosesData, self)
    end
end

function AttachConnectionHosesManually:onDelete()
    g_messageCenter:unsubscribe(MessageType.VEHICLE_RESET, self)
end

function AttachConnectionHosesManually:onAttachConnectionHosesManuallyVehicleLoaded(vehicle)
    local spec = self.spec_attachConnectionHosesManually

    if spec.connectionHosesAttachmentDataToLoad ~= nil then
        local vehicleUniqueId = vehicle:getUniqueId()

        for index = #spec.connectionHosesAttachmentDataToLoad, 1, -1 do
            local hosesAttachmentData = spec.connectionHosesAttachmentDataToLoad[index]

            if hosesAttachmentData.attachedVehicleUniqueId == vehicleUniqueId then
                log:printDevInfo("onAttachConnectionHosesManuallyVehicleLoaded: Found matching vehicle with uniqueId: " .. tostring(vehicleUniqueId), Logger.DEBUG_LEVELS.HIGH)
                log:printDevInfo("Attaching hoses for vehicle: " .. tostring(vehicle:getName()), Logger.DEBUG_LEVELS.HIGH)
                local jointIndex = hosesAttachmentData.jointIndex
                local inputJointIndex = hosesAttachmentData.inputJointIndex
                local hosesAttached = hosesAttachmentData.hosesAttached

                if hosesAttached and spec.attachedHoses[jointIndex] == nil then
                    vehicle:connectHosesToAttacherVehicle(self, inputJointIndex, jointIndex)
                    vehicle:connectCustomHosesToAttacherVehicle(self, inputJointIndex, jointIndex)

                    log:printDevInfo("Hoses attached for vehicle: " .. tostring(vehicle:getName()), Logger.DEBUG_LEVELS.HIGH)
                    spec.attachedHoses[jointIndex] = true
                end

                table.remove(spec.connectionHosesAttachmentDataToLoad, index)
            end
        end
    end
end

function AttachConnectionHosesManually:toggleConnectionHoseAttachState(attachedVehicle, jointDescIndex, inputJointDescIndex)
    log:printDevInfo("toggleConnectionHoseAttachState called with attachedVehicle: " .. tostring(attachedVehicle) .. " for vehicle: " .. tostring(self:getName()), Logger.DEBUG_LEVELS.HIGH)
    local spec = self.spec_attachConnectionHosesManually

    if attachedVehicle ~= nil then
        log:printDevInfo("attachedVehicle: " .. tostring(attachedVehicle:getName()), Logger.DEBUG_LEVELS.HIGH)
        local implementIndex = self:getImplementIndexByObject(attachedVehicle)
        jointDescIndex = jointDescIndex or self:getAttacherJointIndexFromImplementIndex(implementIndex)
        inputJointDescIndex = inputJointDescIndex or attachedVehicle.spec_attachable.inputAttacherJointDescIndex

        log:printDevInfo("implementIndex: " .. tostring(implementIndex), Logger.DEBUG_LEVELS.HIGH)
        log:printDevInfo("jointDescIndex: " .. tostring(jointDescIndex), Logger.DEBUG_LEVELS.HIGH)
        log:printDevInfo("inputJointDescIndex: " .. tostring(inputJointDescIndex), Logger.DEBUG_LEVELS.HIGH)


        local loadFromSavegame = false
        if self.getAttachedImplements ~= nil then
            for _, implement in pairs(self:getAttachedImplements()) do
                if implement.object == attachedVehicle then
                    loadFromSavegame = implement.loadFromSavegame
                    break
                end
            end 
        end

        local info = {
            attacherVehicle = self,
            attachable = attachedVehicle,
            attacherVehicleJointDescIndex = jointDescIndex,
            attachableJointDescIndex = inputJointDescIndex
        }

        if spec.attachedHoses[jointDescIndex] == nil then

            if not loadFromSavegame then
                if AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true) then
                    g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hosesCannotBeAttachedWhenUsingAutoConnector"), 2000)
                    return
                end

                if AttacherJointUtil.getAttachHosesIfAttachedFromInside(info) then
                    g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hosesAreAutomaticallyAttachedWhenAttachingFromInside"), 2000)
                    return
                end
            end

            log:printDevInfo("Attaching hoses", Logger.DEBUG_LEVELS.HIGH)
            attachedVehicle:connectHosesToAttacherVehicle(self, inputJointDescIndex, jointDescIndex)
            attachedVehicle:connectCustomHosesToAttacherVehicle(self, inputJointDescIndex, jointDescIndex)

            spec.attachedHoses[jointDescIndex] = true
        else
            local spec_connectionHoses = attachedVehicle.spec_connectionHoses

            local inputJointDescIndex = attachedVehicle:getActiveInputAttacherJointDescIndex()

            if not loadFromSavegame then
                if attachedVehicle.getIsTurnedOn ~= nil and attachedVehicle:getIsTurnedOn() then
                    g_currentMission:showBlinkingWarning(g_i18n:getText("warning_turnOffImplementFirst"), 2000)
                    return
                end

                if AttacherJointUtil.getUsesFrontloaderToolAttacher(info, true) then
                    g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hosesCannotBeDetachedWhenUsingAutoConnector"), 2000)
                    return
                end

                if AttacherJointUtil.getAttachHosesIfAttachedFromInside(info) then
                    g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hosesAreAutomaticallyDetachedWhenAttachingFromInside"), 2000)
                    return
                end
            end

            log:printDevInfo("Detaching hoses", Logger.DEBUG_LEVELS.HIGH)
            local hoses = attachedVehicle:getConnectionHosesByInputAttacherJoint(inputJointDescIndex)
            for _, hose in ipairs(hoses) do
                attachedVehicle:disconnectHose(hose)
            end

            for i=#spec_connectionHoses.updateableHoses, 1, -1 do
                local hose = spec_connectionHoses.updateableHoses[i]
                if hose.connectedObject == self then
                    attachedVehicle:disconnectHose(hose)
                end
            end

            -- remove delayed mounting if we detach the implement
            local attacherVehicleSpec = self.spec_connectionHoses
            if attacherVehicleSpec ~= nil then
                for _, toolConnector in pairs(attacherVehicleSpec.toolConnectorHoses) do
                    if toolConnector.delayedMounting ~= nil then
                        if toolConnector.delayedMounting.sourceObject == attachedVehicle then
                            toolConnector.delayedMounting = nil
                        end
                    end
                end
            end

            local customHoses = spec_connectionHoses.customHosesByInputAttacher[inputJointDescIndex]
            log:printDevInfo("customHoses: " .. tostring(customHoses), Logger.DEBUG_LEVELS.HIGH)
            if customHoses ~= nil then
                log:printDevInfo("Num custom hoses: " .. tostring(#customHoses), Logger.DEBUG_LEVELS.HIGH)
                for i=1, #customHoses do
                    local customHose = customHoses[i]
                    log:printDevInfo("Found custom hose at index " .. i .. ". IsActive: " .. tostring(customHose.isActive), Logger.DEBUG_LEVELS.HIGH)
                    if customHose.isActive then
                        log:printDevInfo("Disconnecting custom hose [" .. i .. "]: " .. tostring(customHose.connectedTarget), Logger.DEBUG_LEVELS.HIGH)
                        attachedVehicle:disconnectCustomHoseNode(customHose, customHose.connectedTarget)
                    end
                end
            end

            local customTargets = spec_connectionHoses.customHoseTargetsByInputAttacher[inputJointDescIndex]
            log:printDevInfo("customTargets: " .. tostring(customTargets), Logger.DEBUG_LEVELS.HIGH)
            if customTargets ~= nil then
                log:printDevInfo("Num custom targets: " .. tostring(#customTargets), Logger.DEBUG_LEVELS.HIGH)
                for i=1, #customTargets do
                    local customTarget = customTargets[i]
                    log:printDevInfo("Found custom hose target at index " .. i .. ". IsActive: " .. tostring(customTarget.isActive), Logger.DEBUG_LEVELS.HIGH)
                    if customTarget.isActive then
                        log:printDevInfo("Disconnecting custom hose target [" .. i .. "]: " .. tostring(customTarget.connectedHose), Logger.DEBUG_LEVELS.HIGH)
                        attachedVehicle:disconnectCustomHoseNode(customTarget.connectedHose, customTarget)
                    end
                end
            end

            spec.attachedHoses[jointDescIndex] = nil
        end

        if attachedVehicle.spec_lights ~= nil and self.spec_lights ~= nil then
            local spec_lights = self.spec_lights

            if spec.attachedHoses[jointDescIndex] then
                attachedVehicle:setLightsTypesMask(spec_lights.lightsTypesMask, true, true)
                attachedVehicle:setBeaconLightsVisibility(spec_lights.beaconLightsActive, true, true)
                attachedVehicle:setTurnLightState(spec_lights.turnLightState, true, true)
                attachedVehicle:setBrakeLightsVisibility(spec_lights.brakeLightsVisible, true, true)
                attachedVehicle:setReverseLightsVisibility(spec_lights.reverseLightsVisible, true, true)
            else
                attachedVehicle:setLightsTypesMask(0, true, true)
                attachedVehicle:deactivateBeaconLights()
                attachedVehicle:setTurnLightState(false, true, true)
                attachedVehicle:setBrakeLightsVisibility(false)
                attachedVehicle:setReverseLightsVisibility(false)
            end
        end
    end
end

function AttachConnectionHosesManually:onVehicleReset(vehicle)
    if vehicle == self then
        local spec = self.spec_attachConnectionHosesManually

        if spec ~= nil then
            spec.attachedHoses = {}
            spec.connectionHosesAttachmentDataToLoad = {}
            spec.connectionHosesToAttach = {}
        end
    end
end

function AttachConnectionHosesManually.getHasSkipNodeConnection(vehicle)
    local isSkipNodeConnected = false

    if vehicle.getAttacherVehicle ~= nil then
        local attacherVehicle = vehicle:getAttacherVehicle()

        if attacherVehicle ~= nil and attacherVehicle.spec_connectionHoses ~= nil and attacherVehicle.spec_connectionHoses.hoseSkipNodes ~= nil and #attacherVehicle.spec_connectionHoses.hoseSkipNodes > 0 then
            if attacherVehicle.getAttacherVehicle ~= nil then
                local attacherVehicleParent = attacherVehicle:getAttacherVehicle()

                for _, hoseSkipNode in pairs(attacherVehicle.spec_connectionHoses.hoseSkipNodes) do
                    local attacherJointIndex = hoseSkipNode.attacherJointIndex
                    local inputAttacherJointIndex = hoseSkipNode.inputAttacherJointIndex

                    if attacherVehicleParent ~= nil then
                        local attacherVehicleParentImplementIndex = attacherVehicleParent:getImplementIndexByObject(attacherVehicle)
                        local attacherVehicleParentJointDescIndex = attacherVehicleParent:getAttacherJointIndexFromImplementIndex(attacherVehicleParentImplementIndex)

                        if attacherVehicleParent.spec_attachConnectionHosesManually ~= nil and attacherVehicleParent.spec_attachConnectionHosesManually.attachedHoses ~= nil and attacherVehicleParent.spec_attachConnectionHosesManually.attachedHoses[attacherVehicleParentJointDescIndex] ~= nil then
                            isSkipNodeConnected = true
                            break
                        end
                    end
                end
            end
        end
    end

    return isSkipNodeConnected
end