-- ============================================================================
-- === HandToolFuelingSilo.Lua
-- === Mod by [LSMT] Modding Team 
-- === LS25 /FS25
-- === Script by [LSMT] BaTt3RiE @ 2025
-- === Ver 1.0.0.0
-- ============================================================================

HandToolFuelingSilo = {}

local DEBUG = false

local function debugPrint(...)
    if DEBUG then
        print(...)
    end
end

print("[HandToolFuelingSilo] Script loaded")

function HandToolFuelingSilo.initSpecialization()
    local schema = HandTool.xmlSchema
    schema:setXMLSpecializationType("FuelingSilo")
    
    schema:register(XMLValueType.FLOAT, "handTool.fueling#fillRate", "Liters per second", 5.0)
    schema:register(XMLValueType.STRING, "handTool.fueling#fillType", "Fill type (DIESEL or DEF)", "DIESEL")
    schema:register(XMLValueType.NODE_INDEX, "handTool.fueling.vehicleTrigger#node", "Trigger node for vehicle detection")
    schema:register(XMLValueType.NODE_INDEX, "handTool.fueling#soundNode", "Node for 3D sound position")
    
    SoundManager.registerSampleXMLPaths(schema, "handTool.fueling.sounds", "fueling")
    
    schema:register(XMLValueType.NODE_INDEX, "handTool.fueling#particleNode", "Particle spawn position")
    schema:register(XMLValueType.STRING, "handTool.fueling#particleFile", "Particle I3D file path")
    schema:register(XMLValueType.NODE_INDEX, "handTool.fueling#rotationNode", "Node to rotate during fueling")
    schema:register(XMLValueType.ANGLE, "handTool.fueling#targetRotation", "Target Y rotation when fueling", 0)
    schema:register(XMLValueType.FLOAT, "handTool.fueling#rotationSpeed", "Rotation speed (rad/s)", 2.0)
    
    schema:setXMLSpecializationType()
end

function HandToolFuelingSilo.prerequisitesPresent(specializations)
    return true
end

function HandToolFuelingSilo.registerFunctions(handToolType)
    SpecializationUtil.registerFunction(handToolType, "startFueling", HandToolFuelingSilo.startFueling)
    SpecializationUtil.registerFunction(handToolType, "stopFueling", HandToolFuelingSilo.stopFueling)
    SpecializationUtil.registerFunction(handToolType, "performFueling", HandToolFuelingSilo.performFueling)
    SpecializationUtil.registerFunction(handToolType, "vehicleTriggerCallback", HandToolFuelingSilo.vehicleTriggerCallback)
    SpecializationUtil.registerFunction(handToolType, "loadParticleSystem", HandToolFuelingSilo.loadParticleSystem)
    SpecializationUtil.registerFunction(handToolType, "startParticles", HandToolFuelingSilo.startParticles)
    SpecializationUtil.registerFunction(handToolType, "stopParticles", HandToolFuelingSilo.stopParticles)
    SpecializationUtil.registerFunction(handToolType, "updateRotation", HandToolFuelingSilo.updateRotation)
end

function HandToolFuelingSilo.registerEventListeners(handToolType)
    SpecializationUtil.registerEventListener(handToolType, "onLoad", HandToolFuelingSilo)
    SpecializationUtil.registerEventListener(handToolType, "onDelete", HandToolFuelingSilo)
    SpecializationUtil.registerEventListener(handToolType, "onUpdate", HandToolFuelingSilo)
    SpecializationUtil.registerEventListener(handToolType, "onRegisterActionEvents", HandToolFuelingSilo)
    SpecializationUtil.registerEventListener(handToolType, "onHeldEnd", HandToolFuelingSilo)
end

function HandToolFuelingSilo:onLoad(xmlFile, baseDirectory)
    if self.spec_fuelingSilo == nil then
        self.spec_fuelingSilo = {}
    end
    
    local spec = self.spec_fuelingSilo
    
    spec.isContinuousFueling = false
    spec.fillRate = xmlFile:getValue("handTool.fueling#fillRate", 5.0)
    
    local fillTypeName = xmlFile:getValue("handTool.fueling#fillType", "DIESEL")
    spec.fillType = g_fillTypeManager:getFillTypeIndexByName(fillTypeName)
    
    if spec.fillType == nil then
        Logging.xmlWarning(xmlFile, "Invalid fillType '%s' - using DIESEL", fillTypeName)
        spec.fillType = FillType.DIESEL
    end

    spec.vehicleTriggerNode = xmlFile:getValue("handTool.fueling.vehicleTrigger#node", nil, self.components, self.i3dMappings)
    
    if spec.vehicleTriggerNode ~= nil then
        addTrigger(spec.vehicleTriggerNode, "vehicleTriggerCallback", self)
        debugPrint(string.format("[HandToolFuelingSilo] Vehicle trigger registered: %s", getName(spec.vehicleTriggerNode)))
    else
        print("[HandToolFuelingSilo] WARNING: No vehicle trigger node defined!")
    end
    
    spec.vehiclesInTrigger = {}
    spec.soundNode = xmlFile:getValue("handTool.fueling#soundNode", nil, self.components, self.i3dMappings)
    
    if self.isClient then
        spec.fuelingSample = g_soundManager:loadSampleFromXML(
            xmlFile, "handTool.fueling.sounds", "fueling", baseDirectory,
            spec.soundNode, 1, AudioGroup.VEHICLE, self.i3dMappings, self
        )
    end

    spec.particleNode = xmlFile:getValue("handTool.fueling#particleNode", nil, self.components, self.i3dMappings)
    local particleFile = xmlFile:getValue("handTool.fueling#particleFile")
    
    spec.rotationNode = xmlFile:getValue("handTool.fueling#rotationNode", nil, self.components, self.i3dMappings)
    spec.targetRotation = xmlFile:getValue("handTool.fueling#targetRotation", 0)
    spec.rotationSpeed = xmlFile:getValue("handTool.fueling#rotationSpeed", 2.0)
    
    spec.isFueling = false
    spec.targetVehicle = nil
    spec.targetVehicleId = nil
    spec.siloPlaceable = nil
    spec.activateActionEventId = nil
    spec.particleSystem = nil
    spec.particleRootNode = nil
    
    spec.currentRotation = 0
    spec.originalRotation = 0
    spec.isRotating = false
    
    if spec.rotationNode ~= nil then
        local rx, ry, rz = getRotation(spec.rotationNode)
        spec.originalRotation = ry
        spec.currentRotation = ry
        debugPrint(string.format("[HandToolFuelingSilo] Rotation node found - Original Y: %.2f°", math.deg(ry)))
    end
    
    if spec.particleNode ~= nil and particleFile ~= nil then
        self:loadParticleSystem(particleFile, baseDirectory)
    end
    
    debugPrint(string.format("[HandToolFuelingSilo] Loaded - FillRate: %.1fL/s, FillType: %s", 
        spec.fillRate, g_fillTypeManager:getFillTypeNameByIndex(spec.fillType)))
end

function HandToolFuelingSilo:vehicleTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    local spec = self.spec_fuelingSilo
    
    if spec == nil then return end
    
    local vehicle = g_currentMission:getNodeObject(otherActorId)
    
    if vehicle ~= nil and vehicle.spec_fillUnit ~= nil then
        local hasFillType = false
        for i = 1, #vehicle.spec_fillUnit.fillUnits do
            local fillUnit = vehicle.spec_fillUnit.fillUnits[i]
            if fillUnit.supportedFillTypes[spec.fillType] then
                hasFillType = true
                break
            end
        end
        
        if hasFillType then
            if onEnter then
                table.insert(spec.vehiclesInTrigger, vehicle)
                debugPrint(string.format("[HandToolFuelingSilo] Vehicle entered trigger: %s", vehicle:getFullName()))
            elseif onLeave then
                for i = #spec.vehiclesInTrigger, 1, -1 do
                    if spec.vehiclesInTrigger[i] == vehicle then
                        table.remove(spec.vehiclesInTrigger, i)
                        debugPrint(string.format("[HandToolFuelingSilo] Vehicle left trigger: %s", vehicle:getFullName()))
                        
                        if spec.targetVehicle == vehicle then
                            self:stopFueling()
                            spec.isContinuousFueling = false
                        end
                        break
                    end
                end
            end
        end
    end
end

function HandToolFuelingSilo:loadParticleSystem(particleFile, baseDirectory)
    local spec = self.spec_fuelingSilo
    
    local fullPath = Utils.getFilename(particleFile, baseDirectory)
    debugPrint(string.format("[HandToolFuelingSilo] Loading particle system: %s", fullPath))
    
    local rootNode = loadI3DFile(fullPath, false, false, false)
    
    if rootNode ~= nil and rootNode ~= 0 then
        debugPrint(string.format("[HandToolFuelingSilo] Particle I3D loaded: %s (ID: %d)", getName(rootNode), rootNode))
        
        local particleSystemNode = nil
        local numChildren = getNumOfChildren(rootNode)
        
        if numChildren > 0 then
            particleSystemNode = getChildAt(rootNode, 0)
        else
            particleSystemNode = rootNode
        end
        
        if spec.particleNode ~= nil then
            link(spec.particleNode, particleSystemNode)
            setTranslation(particleSystemNode, 0, 0, 0)
            setRotation(particleSystemNode, 0, 0, 0)
        end
        
        setVisibility(particleSystemNode, false)
        if getHasClassId(particleSystemNode, ClassIds.PARTICLE_SYSTEM) then
            setEmitCountScale(particleSystemNode, 0)
        end
        
        spec.particleRootNode = rootNode
        spec.particleSystem = particleSystemNode
        
        debugPrint("[HandToolFuelingSilo] Particle system loaded and linked")
    else
        print(string.format("[HandToolFuelingSilo] ERROR: Failed to load particle I3D: %s", fullPath))
    end
end

function HandToolFuelingSilo:startParticles()
    local spec = self.spec_fuelingSilo
    
    if spec.particleSystem ~= nil then
        setVisibility(spec.particleSystem, true)
        
        if getHasClassId(spec.particleSystem, ClassIds.PARTICLE_SYSTEM) then
            setEmitCountScale(spec.particleSystem, 1.0)
            debugPrint("[HandToolFuelingSilo] Particle system started")
        end
    end
end

function HandToolFuelingSilo:stopParticles()
    local spec = self.spec_fuelingSilo
    
    if spec.particleSystem ~= nil then
        if getHasClassId(spec.particleSystem, ClassIds.PARTICLE_SYSTEM) then
            setEmitCountScale(spec.particleSystem, 0)
        end
        
        setVisibility(spec.particleSystem, false)
        debugPrint("[HandToolFuelingSilo] Particle system stopped")
    end
end

function HandToolFuelingSilo:updateRotation(dt)
    local spec = self.spec_fuelingSilo
    
    if spec.rotationNode == nil then return end
    
    local targetRot = spec.isRotating and spec.targetRotation or spec.originalRotation
    local alpha = 1 - math.exp(-spec.rotationSpeed * (dt / 1000))
    spec.currentRotation = spec.currentRotation + (targetRot - spec.currentRotation) * alpha
    
    local _, ry, rz = getRotation(spec.rotationNode)
    setRotation(spec.rotationNode, spec.currentRotation, ry, rz)
end

function HandToolFuelingSilo:onDelete()
    local spec = self.spec_fuelingSilo
    
    if spec.vehicleTriggerNode ~= nil then
        removeTrigger(spec.vehicleTriggerNode)
    end
    
    if self.isClient and spec.fuelingSample ~= nil then
        g_soundManager:deleteSample(spec.fuelingSample)
        spec.fuelingSample = nil
    end

    if spec ~= nil and spec.particleRootNode ~= nil then
        delete(spec.particleRootNode)
        spec.particleRootNode = nil
        spec.particleSystem = nil
        debugPrint("[HandToolFuelingSilo] Particle system deleted")
    end
end

function HandToolFuelingSilo:onUpdate(dt)
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo

    self:updateRotation(dt)

    if not self:getIsHeld() and not spec.isContinuousFueling then
        return
    end

    spec.targetVehicle = nil
    
    if #spec.vehiclesInTrigger > 0 then
        spec.targetVehicle = spec.vehiclesInTrigger[1]
    end

    if spec.isFueling and spec.targetVehicle ~= nil then
        local vehicle = spec.targetVehicle
        local litersToAdd = spec.fillRate * (dt / 1000)
        
        if vehicle.spec_fillUnit then
            local fillUnitIndex = nil
            for i = 1, #vehicle.spec_fillUnit.fillUnits do
                local fillUnit = vehicle.spec_fillUnit.fillUnits[i]
                if fillUnit.supportedFillTypes[spec.fillType] then
                    fillUnitIndex = i
                    break
                end
            end

            if fillUnitIndex == nil then
                local fillTypeName = g_fillTypeManager:getFillTypeNameByIndex(spec.fillType)
                g_currentMission:showBlinkingWarning(
                    string.format(g_i18n:getText("warning_noMatchingTank"), fillTypeName),
                    2000
                )
                debugPrint(string.format("[HandToolFuelingSilo] Vehicle has no %s tank!", fillTypeName))
                self:stopFueling()
                spec.isContinuousFueling = false
                return
            end

            local fillLevel = vehicle:getFillUnitFillLevel(fillUnitIndex)
            local capacity = vehicle:getFillUnitCapacity(fillUnitIndex)

            if fillLevel < capacity then
                local actualFilled = math.min(litersToAdd, capacity - fillLevel)
                
                if g_client ~= nil then
                    g_client:getServerConnection():sendEvent(
                        HandToolFuelingSiloEvent.new(self, vehicle, actualFilled, spec.fillType)
                    )
                else
                    self:performFueling(vehicle, actualFilled, spec.fillType, dt)
                end
            else
                local fillTypeName = g_fillTypeManager:getFillTypeNameByIndex(spec.fillType)
                g_currentMission:showBlinkingWarning(
                    string.format(g_i18n:getText("warning_tankFull"), fillTypeName),
                    2000
                )
                debugPrint("[HandToolFuelingSilo] Tank full!")
                HandToolFuelingSiloSoundEvent.sendEvent(self, false)
                
                self:stopFueling()
                spec.isContinuousFueling = false
            end
        end
    end
    
    if spec.activateActionEventId ~= nil then
        if spec.targetVehicle == nil then
            g_inputBinding:setActionEventText(spec.activateActionEventId, 
                g_i18n:getText("action_fuelVehicle"))
        else
            local vehicleName = spec.targetVehicle:getFullName()
            g_inputBinding:setActionEventText(spec.activateActionEventId, 
                string.format(g_i18n:getText("action_fuelVehicleTarget"), vehicleName))
        end
    end
end

function HandToolFuelingSilo:performFueling(vehicle, litersToAdd, fillType, dt)
    local spec = self.spec_fuelingSilo
    
    if g_server == nil then
        print("[HandToolFuelingSilo] ERROR: performFueling called on client!")
        return
    end
    
    local fillUnitIndex = nil
    for i = 1, #vehicle.spec_fillUnit.fillUnits do
        local fillUnit = vehicle.spec_fillUnit.fillUnits[i]
        if fillUnit.supportedFillTypes[fillType] then
            fillUnitIndex = i
            break
        end
    end
    
    if fillUnitIndex == nil then
        local fillTypeName = g_fillTypeManager:getFillTypeNameByIndex(spec.fillType)
        g_currentMission:showBlinkingWarning(
            string.format(g_i18n:getText("warning_noMatchingTank"), fillTypeName),
            2000
        )
        debugPrint(string.format("[HandToolFuelingSilo] Vehicle has no %s tank!", fillTypeName))
        HandToolFuelingSiloSoundEvent.sendEvent(self, false)
        
        self:stopFueling()
        spec.isContinuousFueling = false
        return
    end
    
    local pricePerLiter = 0
    local cost = 0
    
    if spec.siloPlaceable ~= nil then
        if not spec.siloPlaceable:canWithdrawFuel(fillType, litersToAdd) then
            local siloSpec = spec.siloPlaceable.spec_silo
            local siloFillLevel = 0
            
            if siloSpec ~= nil and siloSpec.fillLevels ~= nil then
                siloFillLevel = siloSpec.fillLevels[fillType] or 0
            end
            
            if siloFillLevel <= 0.01 then
                g_currentMission:showBlinkingWarning(g_i18n:getText("warning_siloEmpty"), 3000)
                debugPrint("[HandToolFuelingSilo] ❌ Silo leer - Tankvorgang gestoppt!")
                
                HandToolFuelingSiloSoundEvent.sendEvent(self, false)
                
                self:stopFueling()
                spec.isContinuousFueling = false
                return
            else
                debugPrint(string.format("[HandToolFuelingSilo] Nicht genug im Silo (%.2fL verfügbar)", siloFillLevel))
                return
            end
        end
        
        local withdrawn = spec.siloPlaceable:withdrawFuel(fillType, litersToAdd, vehicle:getOwnerFarmId())
        
        if withdrawn > 0 then
            vehicle:addFillUnitFillLevel(vehicle:getOwnerFarmId(), fillUnitIndex, withdrawn, fillType, ToolType.UNDEFINED)
            
            local vehicleFarmId = vehicle:getOwnerFarmId()
            local siloFarmId = spec.siloPlaceable:getOwnerFarmId()
            local isSameFarm = (vehicleFarmId == siloFarmId)
            pricePerLiter = spec.siloPlaceable:getSiloFuelPrice(fillType)
    
            if not isSameFarm then
                cost = withdrawn * pricePerLiter
                g_currentMission:addMoney(-cost, vehicleFarmId, MoneyType.PURCHASE_FUEL, true)
                g_currentMission:addMoney(cost, siloFarmId, MoneyType.SOLD_PRODUCTS, true)
                
                debugPrint(string.format("[HandToolFuelingSilo] Farm %d paid %.2f€ to Farm %d", 
                    vehicleFarmId, cost, siloFarmId))
            else
                cost = 0
                debugPrint(string.format("[HandToolFuelingSilo] FREE refuel for same farm (Farm %d)", vehicleFarmId))
            end

            spec.totalLitersFilled = (spec.totalLitersFilled or 0) + withdrawn
            spec.totalCost = (spec.totalCost or 0) + cost
            
            if spec.siloPlaceable.updateFuelingDisplay ~= nil then
                local displaySide = spec.holderDisplaySide or "left"
                
                spec.siloPlaceable:updateFuelingDisplay(
                    displaySide,
                    withdrawn,
                    pricePerLiter,
                    spec.totalLitersFilled,
                    spec.totalCost
                )
                
                debugPrint(string.format("[HandToolFuelingSilo] Display [%s] update: %.2fL | %.2f€", 
                    displaySide, spec.totalLitersFilled, spec.totalCost))
            end
        else
            debugPrint("[HandToolFuelingSilo] Silo während Tankvorgang leer geworden!")
            g_currentMission:showBlinkingWarning(g_i18n:getText("warning_siloEmpty"), 3000)
            
            HandToolFuelingSiloSoundEvent.sendEvent(self, false)
            
            self:stopFueling()
            spec.isContinuousFueling = false
            return
        end
    end
    
    spec.lastLogTime = (spec.lastLogTime or 0) + (dt or 16)
    if spec.lastLogTime >= 500 then
        local fillLevel = vehicle:getFillUnitFillLevel(fillUnitIndex)
        local capacity = vehicle:getFillUnitCapacity(fillUnitIndex)
        local percentage = math.floor((fillLevel / capacity) * 100)
        debugPrint(string.format("[HandToolFuelingSilo] %d%% | %.1fL/%.0fL | %.3f€/L | Gesamt: %.2f€", 
            percentage, spec.totalLitersFilled, capacity, pricePerLiter, spec.totalCost))
        spec.lastLogTime = 0
    end
end

function HandToolFuelingSilo:startFueling(vehicle)
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo
    
    if spec.isFueling then return end
    
    if vehicle == nil then
        if #spec.vehiclesInTrigger > 0 then
            vehicle = spec.vehiclesInTrigger[1]
        end
    end
    
    if vehicle == nil then
        debugPrint("[HandToolFuelingSilo] No vehicle in trigger - cannot start fueling!")
        return
    end
    
    spec.isFueling = true
    spec.targetVehicle = vehicle
    
    HandToolFuelingSiloSoundEvent.sendEvent(self, true)

    self:startParticles()
    spec.isRotating = true
    
    debugPrint(string.format("[HandToolFuelingSilo] Starting fueling: %s", vehicle:getFullName()))
end

function HandToolFuelingSilo:stopFueling()
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo
    
    if spec.isFueling then
        debugPrint(string.format("[HandToolFuelingSilo] Stopped fueling - Total: %.1fL for %.2f€", 
            spec.totalLitersFilled or 0, spec.totalCost or 0))
        
        spec.isFueling = false
        
        HandToolFuelingSiloSoundEvent.sendEvent(self, false)
        self:stopParticles()
        spec.isRotating = false
        
    end
end

function HandToolFuelingSilo:onRegisterActionEvents()
    if self.spec_fuelingSilo == nil then return end
    if not self:getIsActiveForInput(true) then return end

    local spec = self.spec_fuelingSilo

    local _, actionEventId = self:addActionEvent(
        InputAction.ACTIVATE_HANDTOOL,
        self,
        HandToolFuelingSilo.actionEventFuel,
        true, true, true, true, nil
    )
    spec.activateActionEventId = actionEventId
    g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_fuelVehicle"))
    g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)

    local _, toggleEventId = self:addActionEvent(
        InputAction.TOGGLE_FUELING,
        self,
        HandToolFuelingSilo.actionEventToggleFueling,
        false, true, false, true, nil
    )
    spec.toggleFuelingActionEventId = toggleEventId
    g_inputBinding:setActionEventText(toggleEventId, g_i18n:getText("action_toggleContinuousFueling"))
    g_inputBinding:setActionEventTextPriority(toggleEventId, GS_PRIO_HIGH)
end

function HandToolFuelingSilo.actionEventToggleFueling(self, actionName, inputValue, inputValuePrevious, callbackState, isAnalog)
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo

    if inputValue > 0 then
        spec.isContinuousFueling = not spec.isContinuousFueling
        if spec.isContinuousFueling then
            if #spec.vehiclesInTrigger > 0 then
                self:startFueling(spec.vehiclesInTrigger[1])
            else
                spec.isContinuousFueling = false
                g_currentMission:showBlinkingWarning(
                    g_i18n:getText("warning_noVehicleInRange"),
                    2000
                )
            end
        else
            self:stopFueling()
        end
    end
end

function HandToolFuelingSilo.actionEventFuel(self, actionName, inputValue, inputValuePrevious, callbackState, isAnalog)
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo
    
    if inputValue > 0 then
        if not spec.isFueling then
            if #spec.vehiclesInTrigger > 0 then
                self:startFueling(spec.vehiclesInTrigger[1])
            else
                g_currentMission:showBlinkingWarning(
                    g_i18n:getText("warning_noVehicleInRange"),
                    2000
                )
                debugPrint("[HandToolFuelingSilo] No vehicle in range to fuel!")
            end
        end
    else
        if spec.isFueling then
            self:stopFueling()
        end
    end
end

function HandToolFuelingSilo:onHeldEnd()
    if self.spec_fuelingSilo == nil then return end
    local spec = self.spec_fuelingSilo
    if not spec.isContinuousFueling then
        self:stopFueling()
    end
end

print("[HandToolFuelingSilo] All functions registered")

HandToolFuelingSiloEvent = {}
local HandToolFuelingSiloEvent_mt = Class(HandToolFuelingSiloEvent, Event)

InitEventClass(HandToolFuelingSiloEvent, "HandToolFuelingSiloEvent")

function HandToolFuelingSiloEvent.emptyNew()
    local self = Event.new(HandToolFuelingSiloEvent_mt)
    return self
end

function HandToolFuelingSiloEvent.new(handTool, vehicle, litersToAdd, fillType)
    local self = HandToolFuelingSiloEvent.emptyNew()
    self.handTool = handTool
    self.vehicle = vehicle
    self.litersToAdd = litersToAdd
    self.fillType = fillType
    return self
end

function HandToolFuelingSiloEvent:readStream(streamId, connection)
    self.handTool = NetworkUtil.readNodeObject(streamId)
    self.vehicle = NetworkUtil.readNodeObject(streamId)
    self.litersToAdd = streamReadFloat32(streamId)
    self.fillType = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS)
    self:run(connection)
end

function HandToolFuelingSiloEvent:writeStream(streamId, connection)
    NetworkUtil.writeNodeObject(streamId, self.handTool)
    NetworkUtil.writeNodeObject(streamId, self.vehicle)
    streamWriteFloat32(streamId, self.litersToAdd)
    streamWriteUIntN(streamId, self.fillType, FillTypeManager.SEND_NUM_BITS)
end

function HandToolFuelingSiloEvent:run(connection)
    if g_server ~= nil and self.handTool ~= nil and self.vehicle ~= nil then
        self.handTool:performFueling(self.vehicle, self.litersToAdd, self.fillType, 16)
    end
end

HandToolFuelingSiloSoundEvent = {}
local HandToolFuelingSiloSoundEvent_mt = Class(HandToolFuelingSiloSoundEvent, Event)

InitEventClass(HandToolFuelingSiloSoundEvent, "HandToolFuelingSiloSoundEvent")

function HandToolFuelingSiloSoundEvent.emptyNew()
    return Event.new(HandToolFuelingSiloSoundEvent_mt)
end

function HandToolFuelingSiloSoundEvent.new(handTool, shouldPlay)
    local self = HandToolFuelingSiloSoundEvent.emptyNew()
    self.handTool = handTool
    self.shouldPlay = shouldPlay
    return self
end

function HandToolFuelingSiloSoundEvent:readStream(streamId, connection)
    self.handTool = NetworkUtil.readNodeObject(streamId)
    self.shouldPlay = streamReadBool(streamId)
    self:run(connection)
end

function HandToolFuelingSiloSoundEvent:writeStream(streamId, connection)
    NetworkUtil.writeNodeObject(streamId, self.handTool)
    streamWriteBool(streamId, self.shouldPlay)
end

function HandToolFuelingSiloSoundEvent:run(connection)
    if self.handTool ~= nil and self.handTool.spec_fuelingSilo ~= nil then
        local spec = self.handTool.spec_fuelingSilo
        
        if self.handTool.isClient then
            if spec.fuelingSample ~= nil then
                if self.shouldPlay then
                    g_soundManager:playSample(spec.fuelingSample)
                    debugPrint("[HandToolFuelingSilo] Sound STARTED (synced)")
                else
                    g_soundManager:stopSample(spec.fuelingSample)
                    debugPrint("[HandToolFuelingSilo] Sound STOPPED (synced)")
                end
            end
            
            if spec.particleSystem ~= nil then
                if self.shouldPlay then
                    setVisibility(spec.particleSystem, true)
                    if getHasClassId(spec.particleSystem, ClassIds.PARTICLE_SYSTEM) then
                        setEmitCountScale(spec.particleSystem, 1.0)
                        debugPrint("[HandToolFuelingSilo] Particles STARTED (synced)")
                    end
                else
                    if getHasClassId(spec.particleSystem, ClassIds.PARTICLE_SYSTEM) then
                        setEmitCountScale(spec.particleSystem, 0)
                    end
                    setVisibility(spec.particleSystem, false)
                    debugPrint("[HandToolFuelingSilo] Particles STOPPED (synced)")
                end
            end
            
            if spec.rotationNode ~= nil then
                spec.isRotating = self.shouldPlay
                if self.shouldPlay then
                    debugPrint("[HandToolFuelingSilo] Rotation STARTED (synced)")
                else
                    debugPrint("[HandToolFuelingSilo] Rotation STOPPED (synced)")
                end
            end
        end
    end

    if connection ~= nil and not connection:getIsServer() then
        g_server:broadcastEvent(self, false, connection, self.handTool)
    end
end

function HandToolFuelingSiloSoundEvent.sendEvent(handTool, shouldPlay)
    local event = HandToolFuelingSiloSoundEvent.new(handTool, shouldPlay)
    
    if g_currentMission:getIsServer() then
        g_server:broadcastEvent(event)
        event:run(nil)
    else
        g_client:getServerConnection():sendEvent(event)
        event:run(nil)
    end
end

print("[HandToolFuelingSilo] Network event registered")