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

HandToolFuelingDisplaySilo = {}

local DEBUG = false

local function debugPrint(...)
    if DEBUG then
        print(string.format("[FuelingDisplaySilo] %s", string.format(...)))
    end
end

function HandToolFuelingDisplaySilo.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(PlaceableSilo, specializations)
end

function HandToolFuelingDisplaySilo.registerXMLPaths(schema, baseKey)
    schema:setXMLSpecializationType("HandToolFuelingDisplaySilo")
    local displayKey = baseKey .. ".fuelingDisplays.display(?)"
    schema:register(XMLValueType.STRING, displayKey .. "#side", "Display side (left or right)", "left")
    schema:register(XMLValueType.NODE_INDEX, displayKey .. ".pricePerLiter#digit1", "Preis/L Nachkomma 3")
    schema:register(XMLValueType.NODE_INDEX, displayKey .. ".pricePerLiter#digit2", "Preis/L Nachkomma 2")
    schema:register(XMLValueType.NODE_INDEX, displayKey .. ".pricePerLiter#digit3", "Preis/L Nachkomma 1")
    schema:register(XMLValueType.NODE_INDEX, displayKey .. ".pricePerLiter#digit4", "Preis/L Vor-Komma 1")
    schema:register(XMLValueType.NODE_INDEX, displayKey .. ".pricePerLiter#digit5", "Preis/L Vor-Komma 2")
    
    for i = 1, 6 do
        schema:register(XMLValueType.NODE_INDEX, displayKey .. ".liters#digit" .. i, "Liter digit " .. i)
    end
    
    for i = 1, 7 do
        schema:register(XMLValueType.NODE_INDEX, displayKey .. ".totalPrice#digit" .. i, "Total price digit " .. i)
    end
    
    schema:setXMLSpecializationType()
end

function HandToolFuelingDisplaySilo.registerEventListeners(placeableType)
    SpecializationUtil.registerEventListener(placeableType, "onLoad", HandToolFuelingDisplaySilo)
    SpecializationUtil.registerEventListener(placeableType, "onDelete", HandToolFuelingDisplaySilo)
    SpecializationUtil.registerEventListener(placeableType, "onWriteStream", HandToolFuelingDisplaySilo)
    SpecializationUtil.registerEventListener(placeableType, "onReadStream", HandToolFuelingDisplaySilo)
end

function HandToolFuelingDisplaySilo.registerFunctions(placeableType)
    SpecializationUtil.registerFunction(placeableType, "updateFuelingDisplay", HandToolFuelingDisplaySilo.updateFuelingDisplay)
    SpecializationUtil.registerFunction(placeableType, "resetFuelingDisplay", HandToolFuelingDisplaySilo.resetFuelingDisplay)
    SpecializationUtil.registerFunction(placeableType, "clearFuelingDisplayByIndex", HandToolFuelingDisplaySilo.clearFuelingDisplayByIndex)
    SpecializationUtil.registerFunction(placeableType, "getDisplayIndexBySide", HandToolFuelingDisplaySilo.getDisplayIndexBySide)
    SpecializationUtil.registerFunction(placeableType, "setDigitOnDisplay", HandToolFuelingDisplaySilo.setDigitOnDisplay)
    SpecializationUtil.registerFunction(placeableType, "getFuelingDisplayForSide", HandToolFuelingDisplaySilo.getFuelingDisplayForSide)
end

function HandToolFuelingDisplaySilo:onLoad(savegame)
    self.spec_handToolFuelingDisplaySilo = {}
    local spec = self.spec_handToolFuelingDisplaySilo
    
    spec.displays = {}
    spec.displaysBySide = {}
    
    local xmlFile = self.xmlFile
    local i = 0
    
    debugPrint("=== LADE TANKVORGANG-DISPLAYS ===")
    
    while true do
        local basePath = string.format("placeable.fuelingDisplays.display(%d)", i)
        
        if not xmlFile:hasProperty(basePath) then
            break
        end
        
        local side = xmlFile:getValue(basePath .. "#side", "left")
        
        local display = {
            side = side,
            index = i,
            pricePerLiterNodes = {},
            litersNodes = {},
            totalPriceNodes = {},
            isDisplayActive = false,
            lastSyncTime = 0,
            minSyncInterval = 100,
            lastSyncedLiters = 0,
            lastSyncedCost = 0,
            lastSyncedPrice = 0,
            hasPendingUpdate = false,
            pendingLiters = 0,
            pendingCost = 0,
            pendingPrice = 0
        }
        
        debugPrint("Loading display %d (side: %s) from: %s", i, side, basePath)
        
        for j = 1, 5 do
            local key = string.format("%s.pricePerLiter#digit%d", basePath, j)
            local node = xmlFile:getValue(key, nil, self.components, self.i3dMappings)
            if node ~= nil then
                display.pricePerLiterNodes[j] = node
            end
        end
        
        for j = 1, 6 do
            local key = string.format("%s.liters#digit%d", basePath, j)
            local node = xmlFile:getValue(key, nil, self.components, self.i3dMappings)
            if node ~= nil then
                display.litersNodes[j] = node
            end
        end
        
        for j = 1, 7 do
            local key = string.format("%s.totalPrice#digit%d", basePath, j)
            local node = xmlFile:getValue(key, nil, self.components, self.i3dMappings)
            if node ~= nil then
                display.totalPriceNodes[j] = node
            end
        end
        
        spec.displays[i] = display
        spec.displaysBySide[side] = display
        
        debugPrint("Display %d loaded (side: %s): %d price, %d liters, %d total nodes", 
            i, side, #display.pricePerLiterNodes, #display.litersNodes, #display.totalPriceNodes)
        
        self:clearFuelingDisplayByIndex(i)
        
        i = i + 1
    end
    
    debugPrint("Tankvorgang-Display System initialisiert (%d Displays)", i)
end

function HandToolFuelingDisplaySilo:updateFuelingDisplay(displaySide, litersAdded, pricePerLiter, totalLiters, totalCost)
    local spec = self.spec_handToolFuelingDisplaySilo
    
    if spec == nil or spec.displaysBySide[displaySide] == nil then
        debugPrint("WARNUNG: Kein Display für Seite '%s'", displaySide)
        return
    end
    
    local display = spec.displaysBySide[displaySide]
    local displayIndex = display.index
    
    display.isDisplayActive = true
    
    local priceInMillicents = math.floor(pricePerLiter * 1000 + 0.5)
    self:setDigitOnDisplay(display.pricePerLiterNodes, priceInMillicents, 5)
    
    local litersInCentiliters = math.floor(totalLiters * 100 + 0.5)
    self:setDigitOnDisplay(display.litersNodes, litersInCentiliters, 6)
    
    local priceInCents = math.floor(totalCost * 100 + 0.5)
    self:setDigitOnDisplay(display.totalPriceNodes, priceInCents, 7)
    
    debugPrint("Display [%s]: %.3f€/L | %.2fL | %.2f€", displaySide, pricePerLiter, totalLiters, totalCost)
    
    if g_server ~= nil then
        local currentTime = g_currentMission.time
        local timeSinceLastSync = currentTime - display.lastSyncTime
        
        local litersRounded = math.floor(totalLiters * 100 + 0.5)
        local costRounded = math.floor(totalCost * 100 + 0.5)
        local priceRounded = math.floor(pricePerLiter * 1000 + 0.5)
        
        local hasChanged = (litersRounded ~= display.lastSyncedLiters) or
                           (costRounded ~= display.lastSyncedCost) or
                           (priceRounded ~= display.lastSyncedPrice)
        
        if hasChanged then
            if timeSinceLastSync >= display.minSyncInterval then
                display.lastSyncTime = currentTime
                display.lastSyncedLiters = litersRounded
                display.lastSyncedCost = costRounded
                display.lastSyncedPrice = priceRounded
                display.hasPendingUpdate = false
                
                g_server:broadcastEvent(
                    SiloFuelingDisplayUpdateEvent.new(self, displayIndex, totalLiters, totalCost, pricePerLiter),
                    nil, nil, self
                )
                
                debugPrint("Event sent (display %d): %.2fL, %.2f€, %.3f€/L", displayIndex, totalLiters, totalCost, pricePerLiter)
            else
                display.hasPendingUpdate = true
                display.pendingLiters = totalLiters
                display.pendingCost = totalCost
                display.pendingPrice = pricePerLiter
            end
        end
    end
end

function HandToolFuelingDisplaySilo:resetFuelingDisplay(displaySide)
    local spec = self.spec_handToolFuelingDisplaySilo
    
    if spec == nil or spec.displaysBySide[displaySide] == nil then
        debugPrint("WARNUNG: Kein Display für Reset auf Seite '%s'", displaySide)
        return
    end
    
    local display = spec.displaysBySide[displaySide]
    self:clearFuelingDisplayByIndex(display.index)
end

function HandToolFuelingDisplaySilo:clearFuelingDisplayByIndex(displayIndex)
    local spec = self.spec_handToolFuelingDisplaySilo
    
    if spec == nil or spec.displays[displayIndex] == nil then
        return
    end
    
    local display = spec.displays[displayIndex]
    
    debugPrint("Display %d (%s) cleared", displayIndex, display.side)
    
    display.isDisplayActive = false
    display.lastSyncedLiters = 0
    display.lastSyncedCost = 0
    display.lastSyncedPrice = 0
    display.hasPendingUpdate = false
    
    self:setDigitOnDisplay(display.pricePerLiterNodes, 0, 5)
    self:setDigitOnDisplay(display.litersNodes, 0, 6)
    self:setDigitOnDisplay(display.totalPriceNodes, 0, 7)
    
    if g_server ~= nil then
        g_server:broadcastEvent(SiloFuelingDisplayClearEvent.new(self, displayIndex), nil, nil, self)
    end
end

function HandToolFuelingDisplaySilo:setDigitOnDisplay(nodes, value, numDigits)
    if nodes == nil or #nodes == 0 then
        return
    end
    
    if numDigits == 5 then
        local priceInMillicents = math.floor(value + 0.5)
        
        local digit1 = math.floor(priceInMillicents % 10)
        local digit2 = math.floor((priceInMillicents % 100) / 10)
        local digit3 = math.floor((priceInMillicents % 1000) / 100)
        local digit4 = math.floor((priceInMillicents % 10000) / 1000)
        local digit5 = math.floor(priceInMillicents / 10000)
        
        if nodes[1] ~= nil then setShaderParameter(nodes[1], "index", digit1, 0, 0, 0, false) end
        if nodes[2] ~= nil then setShaderParameter(nodes[2], "index", digit2, 0, 0, 0, false) end
        if nodes[3] ~= nil then setShaderParameter(nodes[3], "index", digit3, 0, 0, 0, false) end
        if nodes[4] ~= nil then setShaderParameter(nodes[4], "index", digit4, 0, 0, 0, false) end
        if nodes[5] ~= nil then setShaderParameter(nodes[5], "index", digit5, 0, 0, 0, false) end
    else
        for i = 1, numDigits do
            local divisor = 10 ^ (i - 1)
            local digit = math.floor((value / divisor) % 10)
            
            if nodes[i] ~= nil then
                setShaderParameter(nodes[i], "index", digit, 0, 0, 0, false)
            end
        end
    end
end

function HandToolFuelingDisplaySilo:getDisplayIndexBySide(side)
    local spec = self.spec_handToolFuelingDisplaySilo
    if spec ~= nil and spec.displaysBySide[side] ~= nil then
        return spec.displaysBySide[side].index
    end
    debugPrint("WARNING: No display found for side '%s', using 0", side)
    return 0
end

function HandToolFuelingDisplaySilo:getFuelingDisplayForSide(side)
    local spec = self.spec_handToolFuelingDisplaySilo
    return spec.displaysBySide[side]
end

function HandToolFuelingDisplaySilo:onWriteStream(streamId, connection)
    local spec = self.spec_handToolFuelingDisplaySilo
    
    if not connection:getIsServer() then
        local numDisplays = 0
        for _ in pairs(spec.displays) do
            numDisplays = numDisplays + 1
        end
        streamWriteUInt8(streamId, numDisplays)
        
        for i = 0, numDisplays - 1 do
            local display = spec.displays[i]
            if display ~= nil then
                streamWriteBool(streamId, display.isDisplayActive)
                
                if display.isDisplayActive then
                    streamWriteFloat32(streamId, display.lastSyncedLiters / 100)
                    streamWriteFloat32(streamId, display.lastSyncedCost / 100)
                    streamWriteFloat32(streamId, display.lastSyncedPrice / 1000)
                end
            else
                streamWriteBool(streamId, false)
            end
        end
    end
end

function HandToolFuelingDisplaySilo:onReadStream(streamId, connection)
    local spec = self.spec_handToolFuelingDisplaySilo
    
    if connection:getIsServer() then
        local numDisplays = streamReadUInt8(streamId)
        
        for i = 0, numDisplays - 1 do
            local isActive = streamReadBool(streamId)
            
            if isActive then
                local totalLiters = streamReadFloat32(streamId)
                local totalCost = streamReadFloat32(streamId)
                local pricePerLiter = streamReadFloat32(streamId)
                
                if spec.displays[i] ~= nil then
                    local display = spec.displays[i]
                    display.isDisplayActive = true
                    
                    local priceInMillicents = math.floor(pricePerLiter * 1000 + 0.5)
                    self:setDigitOnDisplay(display.pricePerLiterNodes, priceInMillicents, 5)
                    
                    local litersInCentiliters = math.floor(totalLiters * 100 + 0.5)
                    self:setDigitOnDisplay(display.litersNodes, litersInCentiliters, 6)
                    
                    local priceInCents = math.floor(totalCost * 100 + 0.5)
                    self:setDigitOnDisplay(display.totalPriceNodes, priceInCents, 7)
                    
                    debugPrint("Display %d synced on join: %.2fL, %.2f€", i, totalLiters, totalCost)
                end
            else
                if spec.displays[i] ~= nil then
                    self:clearFuelingDisplayByIndex(i)
                end
            end
        end
    end
end

function HandToolFuelingDisplaySilo:onDelete()
    debugPrint("Tankvorgang-Display System deaktiviert")
end

SiloFuelingDisplayUpdateEvent = {}
local SiloFuelingDisplayUpdateEvent_mt = Class(SiloFuelingDisplayUpdateEvent, Event)
InitEventClass(SiloFuelingDisplayUpdateEvent, "SiloFuelingDisplayUpdateEvent")

function SiloFuelingDisplayUpdateEvent.emptyNew()
    return Event.new(SiloFuelingDisplayUpdateEvent_mt)
end

function SiloFuelingDisplayUpdateEvent.new(placeable, displayIndex, totalLiters, totalCost, pricePerLiter)
    local self = SiloFuelingDisplayUpdateEvent.emptyNew()
    self.placeable = placeable
    self.displayIndex = displayIndex
    self.totalLiters = totalLiters
    self.totalCost = totalCost
    self.pricePerLiter = pricePerLiter
    return self
end

function SiloFuelingDisplayUpdateEvent:readStream(streamId, connection)
    self.placeable = NetworkUtil.readNodeObject(streamId)
    self.displayIndex = streamReadUInt8(streamId)
    self.totalLiters = streamReadFloat32(streamId)
    self.totalCost = streamReadFloat32(streamId)
    self.pricePerLiter = streamReadFloat32(streamId)
    self:run(connection)
end

function SiloFuelingDisplayUpdateEvent:writeStream(streamId, connection)
    NetworkUtil.writeNodeObject(streamId, self.placeable)
    streamWriteUInt8(streamId, self.displayIndex)
    streamWriteFloat32(streamId, self.totalLiters)
    streamWriteFloat32(streamId, self.totalCost)
    streamWriteFloat32(streamId, self.pricePerLiter)
end

function SiloFuelingDisplayUpdateEvent:run(connection)
    if self.placeable ~= nil and self.placeable:getIsSynchronized() then
        local spec = self.placeable.spec_handToolFuelingDisplaySilo
        
        if spec.displays[self.displayIndex] ~= nil then
            local display = spec.displays[self.displayIndex]
            display.isDisplayActive = true
            
            local priceInMillicents = math.floor(self.pricePerLiter * 1000 + 0.5)
            self.placeable:setDigitOnDisplay(display.pricePerLiterNodes, priceInMillicents, 5)
            
            local litersInCentiliters = math.floor(self.totalLiters * 100 + 0.5)
            self.placeable:setDigitOnDisplay(display.litersNodes, litersInCentiliters, 6)
            
            local priceInCents = math.floor(self.totalCost * 100 + 0.5)
            self.placeable:setDigitOnDisplay(display.totalPriceNodes, priceInCents, 7)
        end
    end
end

SiloFuelingDisplayClearEvent = {}
local SiloFuelingDisplayClearEvent_mt = Class(SiloFuelingDisplayClearEvent, Event)
InitEventClass(SiloFuelingDisplayClearEvent, "SiloFuelingDisplayClearEvent")

function SiloFuelingDisplayClearEvent.emptyNew()
    return Event.new(SiloFuelingDisplayClearEvent_mt)
end

function SiloFuelingDisplayClearEvent.new(placeable, displayIndex)
    local self = SiloFuelingDisplayClearEvent.emptyNew()
    self.placeable = placeable
    self.displayIndex = displayIndex
    return self
end

function SiloFuelingDisplayClearEvent:readStream(streamId, connection)
    self.placeable = NetworkUtil.readNodeObject(streamId)
    self.displayIndex = streamReadUInt8(streamId)
    self:run(connection)
end

function SiloFuelingDisplayClearEvent:writeStream(streamId, connection)
    NetworkUtil.writeNodeObject(streamId, self.placeable)
    streamWriteUInt8(streamId, self.displayIndex)
end

function SiloFuelingDisplayClearEvent:run(connection)
    if self.placeable ~= nil and self.placeable:getIsSynchronized() then
        local spec = self.placeable.spec_handToolFuelingDisplaySilo
        
        if spec ~= nil and spec.displays[self.displayIndex] ~= nil then
            local display = spec.displays[self.displayIndex]
            
            display.isDisplayActive = false
            display.lastSyncedLiters = 0
            display.lastSyncedCost = 0
            display.lastSyncedPrice = 0
            display.hasPendingUpdate = false
            
            self.placeable:setDigitOnDisplay(display.pricePerLiterNodes, 0, 5)
            self.placeable:setDigitOnDisplay(display.litersNodes, 0, 6)
            self.placeable:setDigitOnDisplay(display.totalPriceNodes, 0, 7)
            
            debugPrint("[ClearEvent] Display %d (%s) cleared (synced)", self.displayIndex, display.side)
        end
    end
end

print("[HandToolFuelingDisplaySilo] Script loaded - Tankvorgang-Display System")