--[[
Copyright (C) Achimobil, 2022

Author: Achimobil
Date: 15.01.2022
Version: 0.3.0.0

Contact:
https://forum.giants-software.com
https://discord.gg/Va7JNnEkcW

History:
V 1.0.0.0 @ 15.01.2022 - Release Version.

Important:
Free for use in other mods - no permission needed, only provide my name.
No changes are to be made to this script without permission from Achimobil

Frei verwendbar - keine erlaubnis nötig, Namensnennung im Mod erforderlich.
An diesem Skript dürfen ohne Genehmigung von Achimobil keine Änderungen vorgenommen werden
]]



APalletSilo = {}

PalletSiloActivatable = {}

local PalletSiloActivatable_mt = Class(PalletSiloActivatable, Object)

---Creates a new instance of the class
-- @param bool isServer true if we are server
-- @param bool isClient true if we are client
-- @param table customMt meta table
-- @return table self returns the instance
function PalletSiloActivatable.new(placable, isServer, customMt)
    local self = Object.new(isServer, isClient, customMt or PalletSiloActivatable_mt)

    self.placable = placable
    self.activateText = g_i18n:getText("ExtractPallets");

    return self
end

---Called when press activate. In the test cases there were no parameters
function PalletSiloActivatable:run()    
    local spec = self.placable.spec_aPalletSilo
    
    local availableItemsInStorages = {};
    
    -- was liegt im Lager?
    for _, storage in pairs (self.placable.spec_silo.storages) do
        for fillTypeIndex, fillLevel in pairs (storage.fillLevels) do
            if (fillLevel > 1) then
                if availableItemsInStorages[fillTypeIndex] == nil then
                    availableItemsInStorages[fillTypeIndex] = {};
                    availableItemsInStorages[fillTypeIndex].fillTypeIndex = fillTypeIndex;
                    availableItemsInStorages[fillTypeIndex].fillLevel = 0;
                    
                    -- name in meiner Sprache holen
                    availableItemsInStorages[fillTypeIndex].title = g_currentMission.fillTypeManager.fillTypes[fillTypeIndex].title
                end
                
                local currentAvailableItem = availableItemsInStorages[fillTypeIndex];
                currentAvailableItem.fillLevel = currentAvailableItem.fillLevel + fillLevel;
            end
        end
    end
    
    -- umsortieren, damit die beiden listen den gleichen index haben
    local selectableOptions = {}
    local options = {};
    
    for _, availableItem in pairs (availableItemsInStorages) do
        table.insert(selectableOptions, availableItem);
        table.insert(options, availableItem.title .. " (" .. math.floor(availableItem.fillLevel) .. " l)");
    end
    
    
    -- Wählen was ausgelagert werden soll aus dem was da ist
    local dialogArguments = {
        text = g_i18n:getText("ChooseWhatToPutOut"),
        title = g_i18n:getText("PalletSilo"),
        options = options,
        target = self,
        args = selectableOptions,
        callback = self.fillTypeSelected
    }
    
    --TODO: hack to reset the "remembered" option (i.e. solve a bug in the game engine)
    local dialog = g_gui.guis["OptionDialog"]
    if dialog ~= nil then
        dialog.target:setOptions({""}) -- Add fake option to force a "reset"
    end

    g_gui:showOptionDialog(dialogArguments)
end

---
function PalletSiloActivatable:fillTypeSelected(selectedOption, args)
    local spec = self.placable.spec_aPalletSilo

    -- parameter auswerten
    local selectedArg = args[selectedOption];
    if selectedArg == nil then return end
    local fillTypeIndex = selectedArg.fillTypeIndex;
    
    -- werte für spawner definieren
    spec.fillTypeIndex = fillTypeIndex;
    spec.fillUnitIndex = 1;
    spec.pendingLiters = selectedArg.fillLevel;
    
    -- berechnen der maximalen palettenanzahl
    local amountPerPallet = spec.palletSpawner.fillTypeIdToPallet[fillTypeIndex].capacity;
    local maxPallets = math.floor(selectedArg.fillLevel / amountPerPallet)
    if ((selectedArg.fillLevel - (maxPallets*amountPerPallet)) >= 1) then
        maxPallets = maxPallets + 1;
    end
    
    if(maxPallets == 0) then return end
    
    -- auswählbare palettenanzahl in liste eintragen
    local selectableOptions = {}
    local options = {};
    for i=1, maxPallets do
        table.insert(selectableOptions, {amount=i, amountPerPallet=amountPerPallet});
        table.insert(options, i .. " " .. g_i18n:getText("category_pallets"));
    end
    
    
    -- Wählen wieviel ausgelagert werden soll.
    local dialogArguments = {
        text = g_i18n:getText("ChooseAmountToPutOut"),
        title = g_i18n:getText("PalletSilo"),
        options = options,
        target = self,
        args = selectableOptions,
        callback = self.amountSelected
    }
    
    --TODO: hack to reset the "remembered" option (i.e. solve a bug in the game engine)
    local dialog = g_gui.guis["OptionDialog"]
    if dialog ~= nil then
        dialog.target:setOptions({""}) -- Add fake option to force a "reset"
    end

    g_gui:showOptionDialog(dialogArguments)
end

---
function PalletSiloActivatable:amountSelected(selectedOption, args)
    local spec = self.placable.spec_aPalletSilo

    -- anzahl möglicher palleten für eine neue Auswahl
    local selectedArg = args[selectedOption];
    if selectedArg == nil then return end
    local totalAmount = selectedArg.amount * selectedArg.amountPerPallet;
    
    -- todo: Anzahl paletten wählen
    spec.pendingLiters = math.min(spec.pendingLiters, totalAmount);
    
    SpawnPalletsAtSiloEvent.sendEvent(self.placable, self.placable.ownerFarmId, spec.fillUnitIndex, spec.pendingLiters, spec.fillTypeIndex)
    
end

---
function PalletSiloActivatable:getPalletCallback(pallet, result, fillTypeIndex)
    local spec = self.placable.spec_aPalletSilo
    spec.spawnPending = false

    if pallet ~= nil then
        if result == PalletSpawner.RESULT_SUCCESS then
            pallet:emptyAllFillUnits(true)
        end

        local delta = pallet:addFillUnitFillLevel(self.placable.ownerFarmId, spec.fillUnitIndex, spec.pendingLiters, fillTypeIndex, ToolType.UNDEFINED)
        spec.pendingLiters = math.max(spec.pendingLiters - delta, 0)
        
        -- Filllevel aus silo abziehen
        for _, storage in ipairs(self.placable.spec_silo.storages) do
            local available = storage.fillLevels[fillTypeIndex];
            if available ~= null and available > 0 then
                local moved = math.min(delta, available)
                storage:setFillLevel(available - moved, fillTypeIndex)

                delta = delta - moved
            end

            if delta <= 0.001 then
                break
            end
        end
        
        if spec.pendingLiters > 5 then
            self:updatePallets()
        end
    end
end

---
function PalletSiloActivatable:updatePallets()
    if self.isServer then
        local spec = self.placable.spec_aPalletSilo
        if not spec.spawnPending and spec.pendingLiters > 5 then
            spec.spawnPending = true
            
            spec.palletSpawner:spawnPallet(self.placable.ownerFarmId, spec.fillTypeIndex, self.getPalletCallback, self)
        end
    end
end

---
function APalletSilo.prerequisitesPresent(specializations)
    return true
end

---
function APalletSilo.initSpecialization()    
    local schema = Placeable.xmlSchema
    schema:setXMLSpecializationType("APalletSilo")
    
    local baseXmlPath = "placeable.aPalletSilo"
    
    schema:register(XMLValueType.NODE_INDEX, baseXmlPath .. "#triggerNode", "Trigger node for access menu")
    PalletSpawner.registerXMLPaths(schema, baseXmlPath .. ".palletSpawner")

    schema:setXMLSpecializationType()
    
    PlaceableSilo.INFO_TRIGGER_NUM_DISPLAYED_FILLTYPES = 25;
end

---
function APalletSilo.registerFunctions(placeableType)
    SpecializationUtil.registerFunction(placeableType, "onTriggerNodeCallback", APalletSilo.onTriggerNodeCallback)
end

---
function APalletSilo.registerOverwrittenFunctions(placeableType)
end

---
function APalletSilo.registerEventListeners(placeableType)
    SpecializationUtil.registerEventListener(placeableType, "onLoad", APalletSilo)
    SpecializationUtil.registerEventListener(placeableType, "onDelete", APalletSilo)
    SpecializationUtil.registerEventListener(placeableType, "onFinalizePlacement", APalletSilo)
    SpecializationUtil.registerEventListener(placeableType, "onRegisterActionEvents", APalletSilo)
end

---
function APalletSilo:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
end

---Called on loading
-- @param table savegame savegame
function APalletSilo:onLoad(savegame)

    local baseXmlPath = "placeable.aPalletSilo"
            
    -- hier für server und client
    self.spec_aPalletSilo = {}
    local spec = self.spec_aPalletSilo
    spec.available = false;
    
    spec.triggerNode = self.xmlFile:getValue(baseXmlPath.."#triggerNode", nil, self.components, self.i3dMappings);
    if spec.triggerNode ~= nil then
        if not CollisionFlag.getHasFlagSet(spec.triggerNode, CollisionFlag.TRIGGER_PLAYER) then
            Logging.xmlWarning(self.xmlFile, "Info trigger collison mask is missing bit 'TRIGGER_PLAYER' (%d)", CollisionFlag.getBit(CollisionFlag.TRIGGER_PLAYER))
        end
    end
    
        spec.activatable = PalletSiloActivatable.new(self, self.isServer)
        
    spec.palletSpawner = PalletSpawner.new()
    spec.palletSpawner:load(self.components, self.xmlFile, baseXmlPath .. ".palletSpawner", self.customEnvironment, self.i3dMappings)
    
    spec.initialized = true;
end

---
function APalletSilo:onDelete()
    local spec = self.spec_aPalletSilo

    if spec.triggerNode ~= nil then
        removeTrigger(spec.triggerNode)
        spec.triggerNode = nil
    end
end
---
function APalletSilo:onFinalizePlacement()
    local spec = self.spec_aPalletSilo
    if spec.triggerNode ~= nil then
        addTrigger(spec.triggerNode, "onTriggerNodeCallback", self)
    end
end

---
function APalletSilo:onTriggerNodeCallback(triggerId, otherId, onEnter, onLeave, onStay)
    local spec = self.spec_aPalletSilo
    if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then

        if onEnter then
            g_currentMission.activatableObjectsSystem:addActivatable(spec.activatable)
        else
            g_currentMission.activatableObjectsSystem:removeActivatable(spec.activatable)
        end
    end
end

-- local rX, rY, rZ = getRotation(place.node);
-- print("place.node rX:"..rX.." rY:"..rY.." rZ:"..rZ);

-- print("loadingPattern")
-- DebugUtil.printTableRecursively(loadingPattern,"_",0,2)
