HeapLoadTrigger = {}
local HeapLoadTrigger_mt = Class(HeapLoadTrigger, Object)

function HeapLoadTrigger.prerequisitesPresent(specializations)
    return true
end

InitObjectClass(HeapLoadTrigger, "HeapLoadTrigger")

function HeapLoadTrigger.registerXMLPaths(schema, basePath)
        schema:register(XMLValueType.NODE_INDEX, basePath .. ".spawnArea(?)#startNode", "")
        schema:register(XMLValueType.NODE_INDEX, basePath .. ".spawnArea(?)#widthNode", "")
        schema:register(XMLValueType.NODE_INDEX, basePath .. ".spawnArea(?)#heightNode", "")
        schema:register(XMLValueType.FLOAT, basePath .. "#fillLitersPerSecond", "Fill liters per second")
        schema:register(XMLValueType.STRING, basePath .. "#fillType", "Supported fill type")
        SoundManager.registerSampleXMLPaths(schema, basePath .. ".sounds", "spawn")
        EffectManager.registerEffectXMLPaths(schema, basePath .. ".effectNodes")
end

LoadTrigger.registerXMLPaths = Utils.appendedFunction(LoadTrigger.registerXMLPaths, HeapLoadTrigger.registerXMLPaths)

function HeapLoadTrigger.new(isServer, isClient, customMt)
        local self = Object.new(isServer, isClient, customMt or HeapLoadTrigger_mt)
        self.isActive = true
        self.amountToTip = 0
        self.areEffectsActive = false
        self.effectsEndTime = 0
        self.effectsDuration = 5000
        self.heapLoadTriggerDirtyFlag = self:getNextDirtyFlag()

        return self
end

function HeapLoadTrigger:load(components, xmlFile, xmlNode, i3dMappings, rootNode)
        local litersPerSecond = xmlFile:getValue(xmlNode .. "#fillLitersPerSecond", 150)

        if litersPerSecond <= 0 then
                Logging.xmlError(xmlFile, "litersPerSecond may not be 0 or negative for heap load trigger '%s'", xmlNode)

                return
        end

        self.fillLitersPerMS = litersPerSecond * 1000
        self.fillLitersMax = 150
        self.spawnAreas = {}

        xmlFile:iterate(xmlNode .. ".spawnArea", function (_, areaKey)
                local startNode = xmlFile:getValue(areaKey .. "#startNode", nil, components, i3dMappings)

                if startNode == nil then
                        Logging.xmlError(xmlFile, "Missing startNode for spawnArea '%s'", areaKey)

                        return
                end

                local widthNode = xmlFile:getValue(areaKey .. "#widthNode", nil, components, i3dMappings)

                if widthNode == nil then
                        Logging.xmlError(xmlFile, "Missing widthNode for spawnArea '%s'", areaKey)

                        return
                end

                local heightNode = xmlFile:getValue(areaKey .. "#heightNode", nil, components, i3dMappings)

                if heightNode == nil then
                        Logging.xmlError(xmlFile, "Missing heightNode for spawnArea '%s'", areaKey)

                        return
                end

                local spawnArea = {
                        lineOffset = 0,
                        start = startNode,
                        width = widthNode,
                        height = heightNode
                }

                table.insert(self.spawnAreas, spawnArea)
        end)

        local fillTypeName = xmlFile:getString(xmlNode .. "#fillType", "woodchips")
        self.fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName)

        if self.fillTypeIndex == nil then
                Logging.xmlError(xmlFile, "Invalid filltype '%s' for HeapLoadTrigger '%s'", fillTypeName, xmlNode)

                return false
        end

        self.fillTypes = {
                [self.fillTypeIndex] = true
        }
        local _, baseDirectory = Utils.getModNameAndBaseDirectory(xmlFile:getFilename())

        if self.isClient then
                self.samples = {
                        spawn = g_soundManager:loadSampleFromXML(xmlFile, xmlNode .. ".sounds", "spawn", baseDirectory, components, 0, AudioGroup.ENVIRONMENT, i3dMappings, self)
                }
                self.effects = g_effectManager:loadEffect(xmlFile, xmlNode .. ".effectNodes", components, self, i3dMappings)

                g_effectManager:setEffectTypeInfo (self.effects, self.fillTypeIndex)
        end

        self:raiseActive()

        return true
end

function HeapLoadTrigger:delete()
        HeapLoadTrigger:superClass().delete(self)

        if self.samples ~= nil then
                g_soundManager:deleteSamples(self.samples)
        end

        if self.effects ~= nil then
                g_effectManager:deleteEffects(self.effects)
        end
end

function HeapLoadTrigger:readStream(streamId, connection, objectId)
        HeapLoadTrigger:superClass().readStream(self, streamId, connection, objectId)

        local areEffectsActive = streamReadBool(streamId)

        self:setEffectsActive(areEffectsActive)
end

function HeapLoadTrigger:writeStream(streamId, connection)
        HeapLoadTrigger:superClass().writeStream(self, streamId, connection)
        streamWriteBool(streamId, self.areEffectsActive)
end

function HeapLoadTrigger:readUpdateStream(streamId, timestamp, connection)
        HeapLoadTrigger:superClass().readUpdateStream(self, streamId, timestamp, connection)

        local areEffectsActive = streamReadBool(streamId)

        self:setEffectsActive(areEffectsActive)
end

function HeapLoadTrigger:writeUpdateStream(streamId, connection, dirtyMask)
        HeapLoadTrigger:superClass().writeUpdateStream(self, streamId, connection, dirtyMask)
        streamWriteBool(streamId, self.areEffectsActive)
end

function HeapLoadTrigger:setSource(object)
        assert(object.getSupportedFillTypes ~= nil)
        assert(object.getAllFillLevels ~= nil)
        assert(object.addFillLevelToFillableObject ~= nil)
        assert(object.getIsFillAllowedToFarm ~= nil)

        self.source = object
end

function HeapLoadTrigger:setIsActive(isActive)
        self.isActive = isActive

        if isActive then
                self:raiseActive()
        end
end

function HeapLoadTrigger:update(dt)
        if self.isServer then
                if self.isActive then
                        local litersPerMs = self.fillLitersPerMS
                        local farmId = self.ownerFarmId
                        local fillLevel = self.source:getFillLevel(self.fillTypeIndex, farmId)
                        self.amountToTip = math.min(self.amountToTip + litersPerMs * dt, fillLevel, self.fillLitersMax)

                        if g_densityMapHeightManager:getMinValidLiterValue(self.fillTypeIndex) < self.amountToTip then
                                self:setEffectsActive(true)

                                self.effectsEndTime = g_time + self.effectsDuration

                                for _, spawnArea in ipairs(self.spawnAreas) do
                                        if g_densityMapHeightManager:getMinValidLiterValue(self.fillTypeIndex) < self.amountToTip then
                                                local lsx, lsy, lsz, lex, ley, lez, radius = DensityMapHeightUtil.getLineByArea(spawnArea.start, spawnArea.width, spawnArea.height, false)
                                                local dropped, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(nil, self.amountToTip, self.fillTypeIndex, lsx, lsy, lsz, lex, ley, lez, radius, radius, spawnArea.lineOffset, nil, nil, nil)
                                                spawnArea.lineOffset = lineOffset
                                                self.amountToTip = math.max(self.amountToTip - dropped, 0)

                                                self.source:removeFillLevel(self.fillTypeIndex, dropped, farmId)

                                                if self.amountToTip == 0 then
                                                        break
                                                end
                                        end
                                end
                        end

                        self:raiseActive()
                end

                if self.areEffectsActive then
                        if g_time < self.effectsEndTime then
                                self:raiseActive()
                        else
                                self:setEffectsActive(false)
                        end
                end
        end
end

function HeapLoadTrigger:setEffectsActive(isActive)
        if self.areEffectsActive ~= isActive then
                self.areEffectsActive = isActive

                if self.isServer then
                        self:raiseDirtyFlags(self.heapLoadTriggerDirtyFlag)
                end

                if self.samples ~= nil then
                        if isActive then
                                if not g_soundManager:getIsSamplePlaying(self.samples.spawn) then
                                        g_soundManager:playSample(self.samples.spawn)
                                end
                        elseif g_soundManager:getIsSamplePlaying(self.samples.spawn) then
                                g_soundManager:stopSample(self.samples.spawn)
                        end
                end

                if self.effects ~= nil then
                        if isActive then
                                g_effectManager:startEffects(self.effects)
                        else
                                g_effectManager:stopEffects(self.effects)
                        end
                end
        end
end

function HeapLoadTrigger:getCurrentFillType()
        return self.fillTypeIndex
end

function HeapLoadTrigger:getIsFillTypeSupported(fillTypeIndex)
        return self.fillTypeIndex == fillTypeIndex
end

function HeapLoadTrigger:getSupportAILoading()
        return false
end