ParticleSystemUtil = {}
function ParticleSystemUtil.loadParticleSystemDesc(particle, vehicle, xmlFile, key)
  local gravity = getXMLFloat(xmlFile, key .. "#particleGravity")
  if gravity == nil then
    return false
  end
  particle.particleEmitNode = Utils.indexToObject(vehicle.components, getXMLString(xmlFile, key .. "#emitNode"))
  particle.particleGravity = gravity / 1000000
  particle.particleVelocity = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#particleVelocity"), 20) * 0.001
  particle.particleGrowth = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#particleGrowth"), 1) * 0.001
  particle.particleSize = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#particleSize"), 4)
  particle.cuttingArea = vehicle.cuttingAreas[getXMLInt(xmlFile, key .. "#cuttingArea")]
  local startIndex = {
    getTranslation(particle.cuttingArea.start)
  }
  local widthIndex = {
    getTranslation(particle.cuttingArea.width)
  }
  local heightIndex = {
    getTranslation(particle.cuttingArea.height)
  }
  particle.cuttingAreaHalfDirX = (widthIndex[1] - startIndex[1] + (heightIndex[1] - startIndex[1])) / 2
  particle.cuttingAreaHalfDirZ = (widthIndex[3] - startIndex[3] + (heightIndex[3] - startIndex[3])) / 2
  particle.amount = 0
  particle.amountTrailerScale = 1
  if vehicle.isClient then
    for _, ps in pairs(particle.particleSystems) do
      if type(ps) == "table" then
        particle.originalLifespan = getParticleSystemLifespan(ps.geometry)
      end
    end
  end
  return true
end
function ParticleSystemUtil.updateParticleSystem(particle, vehicle, useRaycast)
  if vehicle.isServer and particle.particleEmitNode ~= nil then
    local x0, y0, z0 = getWorldTranslation(particle.particleEmitNode)
    local dx, dy, dz = localDirectionToWorld(particle.particleEmitNode, 0, -1, 0)
    local vx = dx * particle.particleVelocity
    local vy = dy * particle.particleVelocity
    local vz = dz * particle.particleVelocity
    local maxT = 50 / particle.particleVelocity
    local colX = x0 + vx * maxT
    local colZ = z0 + vz * maxT
    local colT = maxT
    local hitTrailer
    local hitTerrain = false
    if useRaycast then
      do
        local stepT = 3 / particle.particleVelocity
        local sx = x0
        local sy = y0
        local sz = z0
        for t = 2 * stepT, maxT, stepT do
          local x = x0 + vx * t
          local z = z0 + vz * t
          local y = y0 + vy * t + particle.particleGravity * 0.5 * t * t
          local dx = x - sx
          local dy = y - sy
          local dz = z - sz
          local dist = math.sqrt(dx * dx + dy * dy + dz * dz)
          ParticleSystemUtil.lastHitTerrain = false
          ParticleSystemUtil.lastHitTrailer = nil
          ParticleSystemUtil.lastHitDistance = 0
          raycastAll(sx, sy, sz, dx, dy, dz, "ParticleSystemUtil.findCollisionRaycastCallback", dist, vehicle)
          hitTerrain = ParticleSystemUtil.lastHitTerrain
          hitTrailer = ParticleSystemUtil.lastHitTrailer
          sx = x
          sy = y
          sz = z
          if hitTerrain or hitTrailer ~= nil then
            local hitDist = ParticleSystemUtil.lastHitDistance
            colT = Utils.lerp(t - stepT, t, ParticleSystemUtil.lastHitDistance / dist)
            colX = x0 + vx * colT
            colZ = z0 + vz * colT
            break
          end
          if Vehicle.debugRendering then
            drawDebugPoint(x, y, z, 0, 0, 1, 1)
          end
        end
      end
    else
      local stepT = 1 / particle.particleVelocity
      for t = 2 * stepT, maxT, stepT do
        local x = x0 + vx * t
        local z = z0 + vz * t
        local y = y0 + vy * t + particle.particleGravity * 0.5 * t * t
        local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)
        if y <= h then
          hitTerrain = true
          colX = x
          colZ = z
          colT = t
          break
        end
        if Vehicle.debugRendering then
          drawDebugPoint(x, y, z, 0, 0, 1, 1)
        end
      end
    end
    if hitTerrain then
      do
        local targetX, targetY, targetZ = worldToLocal(getParent(particle.cuttingArea.start), colX, colY, colZ)
        local startX, startY, startZ = targetX - particle.cuttingAreaHalfDirX, targetY, targetZ + particle.cuttingAreaHalfDirZ
        local widthX, widthY, widthZ = targetX + particle.cuttingAreaHalfDirX, targetY, targetZ + particle.cuttingAreaHalfDirZ
        local heightX, heightY, heightZ = targetX - particle.cuttingAreaHalfDirX, targetY, targetZ - particle.cuttingAreaHalfDirZ
        setTranslation(particle.cuttingArea.start, startX, startY, startZ)
        setTranslation(particle.cuttingArea.width, widthX, widthY, widthZ)
        setTranslation(particle.cuttingArea.height, heightX, heightY, heightZ)
        if Vehicle.debugRendering then
          local x, y, z = getWorldTranslation(particle.cuttingArea.start)
          y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) + 0.1
          local x1, y1, z1 = getWorldTranslation(particle.cuttingArea.width)
          y1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1) + 0.1
          local x2, y2, z2 = getWorldTranslation(particle.cuttingArea.height)
          y2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2) + 0.1
          drawDebugPoint(colX, getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, colX, 0, colZ) + 0.1, colZ, 1, 1, 1, 1)
          drawDebugPoint(x, y, z, 1, 0, 0, 1)
          drawDebugPoint(x1, y1, z1, 0, 1, 0, 1)
          drawDebugPoint(x2, y2, z2, 0, 0, 1, 1)
          drawDebugLine(x, y, z, 1, 0, 0, x1, y1, z1, 0, 1, 0)
          drawDebugLine(x, y, z, 1, 0, 0, x2, y2, z2, 0, 0, 1)
        end
      end
    elseif hitTrailer ~= nil then
      local litersPerSecond = vehicle.sprayLitersPerSecond[vehicle.currentFillType]
      if litersPerSecond == nil then
        litersPerSecond = vehicle.defaultSprayLitersPerSecond
      end
      local deltaFillLevel = litersPerSecond * dt * 0.001
      hitTrailer:setFillLevel(hitTrailer.fillLevel + deltaFillLevel, vehicle.currentFillType)
    end
    for _, ps in pairs(particle.particleSystems) do
      if type(ps) == "table" then
        local lifespan = math.min(particle.originalLifespan, colT)
        setParticleSystemLifespan(ps.geometry, lifespan, true)
      end
    end
  end
end
function ParticleSystemUtil.findCollisionRaycastCallback(vehicle, transformId, x, y, z, distance)
  if transformId == g_currentMission.terrainRootNode then
    ParticleSystemUtil.lastHitTerrain = true
    ParticleSystemUtil.lastHitDistance = distance
    return false
  else
    local trailer = g_currentMission.nodeToVehicle[transformId]
    if trailer ~= nil and trailer.exactFillRootNode == transformId and trailer.allowFillType ~= nil and trailer:allowFillType(vehicle.currentFillType) and trailer.getAllowFillFromAir ~= nil and trailer:getAllowFillFromAir() and trailer.fillLevel < trailer.capacity then
      ParticleSystemUtil.lastHitTrailer = trailer
      ParticleSystemUtil.lastHitDistance = distance
      return false
    end
  end
  return true
end
