HDProductivityEvent = {}
local HDProductivityEvent_mt = Class(HDProductivityEvent, Event)
InitEventClass(HDProductivityEvent, "HDProductivityEvent")

function HDProductivityEvent.emptyNew()
    return Event.new(HDProductivityEvent_mt)
end

function HDProductivityEvent.new(placeable, productionId, productivity, isRequest, isSyncRequest)
    local self = HDProductivityEvent.emptyNew()
    self.placeable = placeable
    self.productionId = productionId or ""
    self.productivity = productivity or 100
    self.isRequest = isRequest == true
    self.isSyncRequest = isSyncRequest == true
    return self
end

function HDProductivityEvent:writeStream(streamId, connection)
    NetworkUtil.writeNodeObject(streamId, self.placeable)
    streamWriteString(streamId, self.productionId)
    streamWriteUIntN(streamId, self.productivity, 9)
    streamWriteBool(streamId, self.isRequest)
    streamWriteBool(streamId, self.isSyncRequest)
end

function HDProductivityEvent:readStream(streamId, connection)
    self.placeable = NetworkUtil.readNodeObject(streamId)
    self.productionId = streamReadString(streamId)
    self.productivity = streamReadUIntN(streamId, 9)
    self.isRequest = streamReadBool(streamId)
    self.isSyncRequest = streamReadBool(streamId)
    self:run(connection)
end

function HDProductivityEvent:run(connection)
    if self.placeable == nil or HDPlaceableUpgrade.isRelayProductionPoint(self.placeable) then
        return
    end

    if self.isSyncRequest then
        if g_server ~= nil and connection ~= nil then
            local productivity = HDPlaceableUpgrade.getProductivity(self.placeable, self.productionId)
            connection:sendEvent(HDProductivityEvent.new(
                self.placeable,
                self.productionId,
                productivity,
                false,
                false
            ))
        end
        return
    end

    if self.isRequest then
        if g_server == nil then
            return
        end
        local farmId = g_currentMission:getFarmId(connection)
        if farmId == nil or self.placeable:getOwnerFarmId() ~= farmId then
            Logging.warning("[HD Building Upgrades] Productivity request rejected for production '%s'", self.productionId)
            return
        end
        if HDPlaceableUpgrade.setProductivity(self.placeable, self.productionId, self.productivity) then
            Logging.info(
                "[HD Building Upgrades] Productivity changed: building=%s production=%s productivity=%d%%",
                self.placeable:getName(),
                self.productionId,
                self.productivity
            )
            g_server:broadcastEvent(HDProductivityEvent.new(
                self.placeable,
                self.productionId,
                self.productivity,
                false,
                false
            ), false)
        end
        return
    end

    HDPlaceableUpgrade.setProductivity(self.placeable, self.productionId, self.productivity)

    -- GUI refresh only, gated the same way as HDLevelSyncEvent: InGameMenuProductionFrame's lists
    -- are only valid once the menu is open and productionChainManager is initialized. Refreshing
    -- unconditionally caused 'attempt to index nil with getProductionPointsForFarmId' when a
    -- productivity response arrived before the menu/production chain manager was ready. The
    -- productivity value itself is always applied above regardless of this gate.
    if g_gui ~= nil and g_gui.currentGuiName == "InGameMenu"
        and g_currentMission ~= nil and g_currentMission.productionChainManager ~= nil
        and g_inGameMenu ~= nil and g_inGameMenu.pageProduction ~= nil then
        local frame = g_inGameMenu.pageProduction
        if frame.updateProductionLists ~= nil then
            frame:updateProductionLists()
        end
        if frame.updateMenuButtons ~= nil then
            frame:updateMenuButtons()
        end
    end
end

function HDProductivityEvent.sendRequest(placeable, productionId, productivity)
    if g_server ~= nil then
        if HDPlaceableUpgrade.setProductivity(placeable, productionId, productivity) then
            g_server:broadcastEvent(HDProductivityEvent.new(
                placeable,
                productionId,
                productivity,
                false,
                false
            ), false)
        end
    elseif g_client ~= nil then
        g_client:getServerConnection():sendEvent(HDProductivityEvent.new(
            placeable,
            productionId,
            productivity,
            true,
            false
        ))
    end
end

function HDProductivityEvent.sendSyncRequest(placeable, productionId)
    if g_server == nil and g_client ~= nil then
        g_client:getServerConnection():sendEvent(HDProductivityEvent.new(
            placeable,
            productionId,
            100,
            false,
            true
        ))
    end
end
