
MudSystemSettingsSyncEvent = MudSystemSettingsSyncEvent or {}
local MudSystemSettingsSyncEvent_mt = Class(MudSystemSettingsSyncEvent, Event)

local EVENT_NAME = "MudSystemPhysics.MudSystemSettingsSyncEvent"

if MudSystemSettingsSyncEvent.eventId == nil then
    InitEventClass(MudSystemSettingsSyncEvent, EVENT_NAME)
end

function MudSystemSettingsSyncEvent.emptyNew()
    return Event.new(MudSystemSettingsSyncEvent_mt)
end

function MudSystemSettingsSyncEvent.new(isRequest, snapshot)
    local self = MudSystemSettingsSyncEvent.emptyNew()
    self.isRequest = isRequest == true
    self.snapshot = snapshot
    return self
end

local function ensureStreamId(streamId, connection)
    if type(streamId) ~= "number" and type(connection) == "number" then
        return connection, streamId
    end
    return streamId, connection
end

function MudSystemSettingsSyncEvent:writeStream(streamId, connection)
    streamId, connection = ensureStreamId(streamId, connection)
    if type(streamId) ~= "number" then
        return
    end

    streamWriteBool(streamId, self.isRequest)

    if not self.isRequest then
        local settings = _G["MudSystemSettings"]
        local items = settings and settings.items

        if items ~= nil then
            for i, item in ipairs(items) do
                local v = self.snapshot and self.snapshot[i]
                if item.type == "bool" then
                    streamWriteBool(streamId, v == true)
                else
                    streamWriteFloat32(streamId, tonumber(v) or 0)
                end
            end
        end
    end
end

function MudSystemSettingsSyncEvent:readStream(streamId, connection)
    streamId, connection = ensureStreamId(streamId, connection)
    if type(streamId) ~= "number" then
        return
    end

    self.isRequest = streamReadBool(streamId)
    if not self.isRequest then
        local settings = _G["MudSystemSettings"]
        if settings ~= nil and settings.items ~= nil then
            self.snapshot = {}
            for i, item in ipairs(settings.items) do
                if item.type == "bool" then
                    self.snapshot[i] = streamReadBool(streamId)
                else
                    self.snapshot[i] = streamReadFloat32(streamId)
                end
            end
        end
    end
    self:run(connection)
end

local function isMasterConnection(connection)
    if connection == nil or g_currentMission == nil
        or g_currentMission.userManager == nil then return false end
    local userManager = g_currentMission.userManager
    if userManager.getIsConnectionMasterUser == nil then return false end
    local ok, isMaster = pcall(
        userManager.getIsConnectionMasterUser, userManager, connection
    )
    return ok and isMaster == true
end

function MudSystemSettingsSyncEvent:run(connection)
    local settings = _G["MudSystemSettings"]
    if settings == nil then return end

    if self.isRequest then
        -- Every joining client may read the authoritative server settings.
        if g_server ~= nil and connection ~= nil and connection.sendEvent ~= nil
            and settings.collectSnapshot ~= nil then
            connection:sendEvent(MudSystemSettingsSyncEvent.new(
                false, settings:collectSnapshot()
            ))
        end
        return
    end

    if self.snapshot == nil then return end

    if g_server ~= nil then
        -- Only a logged-in server administrator may change shared settings.
        if connection == nil or connection.getIsServer == nil
            or connection:getIsServer() or not isMasterConnection(connection) then
            return
        end
        if settings.applySnapshot ~= nil then
            settings:applySnapshot(self.snapshot, true)
        end
        print("[MudSystemSettings] Shared settings accepted from administrator and saved")
        -- Re-collect after server-side clamping and broadcast the canonical values.
        local canonical = settings.collectSnapshot ~= nil
            and settings:collectSnapshot() or self.snapshot
        g_server:broadcastEvent(
            MudSystemSettingsSyncEvent.new(false, canonical), false
        )
    else
        -- Clients only accept snapshots coming from their server.
        if connection ~= nil and connection.getIsServer ~= nil
            and not connection:getIsServer() then return end
        if settings.applySnapshot ~= nil then
            settings:applySnapshot(self.snapshot, false)
        end
        if settings.markServerSnapshotReceived ~= nil then
            settings:markServerSnapshotReceived()
        end
    end
end

function MudSystemSettingsSyncEvent.sendRequest()
    if g_client ~= nil and g_client.getServerConnection ~= nil then
        g_client:getServerConnection():sendEvent(MudSystemSettingsSyncEvent.new(true))
    end
end

function MudSystemSettingsSyncEvent.sendSnapshot(snapshot)
    if snapshot == nil then
        return
    end

    if g_server ~= nil then
        local settings = _G["MudSystemSettings"]
        if settings ~= nil and settings.applySnapshot ~= nil then
            settings:applySnapshot(snapshot, true)
            if settings.collectSnapshot ~= nil then
                snapshot = settings:collectSnapshot()
            end
        end
        g_server:broadcastEvent(
            MudSystemSettingsSyncEvent.new(false, snapshot), false
        )
    elseif g_client ~= nil and g_client.getServerConnection ~= nil then
        local isAdmin = (g_currentMission ~= nil) and (g_currentMission.isMasterUser == true)
        if not isAdmin then
            return
        end
        g_client:getServerConnection():sendEvent(MudSystemSettingsSyncEvent.new(false, snapshot))
    end
end
