----------------------------------------------------------------------------------------------------
-- ChangeableDischargeFillUnits
----------------------------------------------------------------------------------------------------
-- Purpose: Specialization for changeable discharge fillUnits
--
-- @author John Deere 6930 @VertexDezign, fs19-base by rafftnix
----------------------------------------------------------------------------------------------------

---@class ChangeableDischargeFillUnits
ChangeableDischargeFillUnits = {}
ChangeableDischargeFillUnits.MOD_NAME = g_currentModName

function ChangeableDischargeFillUnits.initSpecialization(specializations)
    local schema = Vehicle.xmlSchema
    schema:setXMLSpecializationType("ChangeableDischargeFillUnits")

    schema:register(XMLValueType.VECTOR_N, "vehicle.changeableDischargeFillUnits#fillUnitConfigIds", "Fill unit configuration to activate.")
    schema:register(XMLValueType.INT, "vehicle.changeableDischargeFillUnits.dischargeFillUnit(?)#fillUnitIndex", "Fill unit index for discharging.", nil, true)
    schema:register(XMLValueType.STRING, "vehicle.changeableDischargeFillUnits.dischargeFillUnit(?)#displayText", "Display text for discharging.", nil, true)

    schema:setXMLSpecializationType()
end

function ChangeableDischargeFillUnits.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Dischargeable, specializations) and SpecializationUtil.hasSpecialization(FillUnit, specializations)
end

function ChangeableDischargeFillUnits.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "loadChangeableDischargeFillUnit", ChangeableDischargeFillUnits.loadChangeableDischargeFillUnit)
    SpecializationUtil.registerFunction(vehicleType, "setDischargeFillUnit", ChangeableDischargeFillUnits.setDischargeFillUnit)
    SpecializationUtil.registerFunction(vehicleType, "updateChangeableFillUnitActionEvents", ChangeableDischargeFillUnits.updateChangeableFillUnitActionEvents)
end

function ChangeableDischargeFillUnits.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", ChangeableDischargeFillUnits)
    SpecializationUtil.registerEventListener(vehicleType, "onReadStream", ChangeableDischargeFillUnits)
    SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", ChangeableDischargeFillUnits)
    SpecializationUtil.registerEventListener(vehicleType, "onDischargeStateChanged", ChangeableDischargeFillUnits)
    SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", ChangeableDischargeFillUnits)
end

---Called on load
function ChangeableDischargeFillUnits:onLoad(savegame)
    self.spec_changeableDischargeFillUnits = self[string.format("spec_%s.changeableDischargeFillUnits", ChangeableDischargeFillUnits.MOD_NAME)]
    local spec = self.spec_changeableDischargeFillUnits

    local configIds = self.xmlFile:getValue("vehicle.changeableDischargeFillUnits#fillUnitConfigIds", nil, true)
    if configIds ~= nil and self.configurations["fillUnit"] ~= nil then
        local pass = false

        for _, configId in ipairs(configIds) do
            if self.configurations["fillUnit"] == configId then
                pass = true
                break
            end
        end

        if not pass then
            return
        end
    end

    spec.changeableUnits = {}

    self.xmlFile:iterate("vehicle.changeableDischargeFillUnits.dischargeFillUnit", function (_, partKey)
        local liftingAxle = {}
        if self:loadChangeableDischargeFillUnit(self.xmlFile, partKey, liftingAxle) then
            table.insert(spec.changeableUnits, liftingAxle)
        end
    end)

    spec.currentUnitIndex = #spec.changeableUnits > 1 and 1 or nil
end

---Called on read stream
function ChangeableDischargeFillUnits:onReadStream(streamId, connection)
    local spec = self.spec_changeableDischargeFillUnits

    if spec.currentUnitIndex ~= nil then
        local fillUnitIndex = streamReadUInt8(streamId)
        self:setDischargeFillUnit(fillUnitIndex, true)
    end
end

---Called on write stream
function ChangeableDischargeFillUnits:onWriteStream(streamId, connection)
    local spec = self.spec_changeableDischargeFillUnits

    if spec.currentUnitIndex ~= nil then
        streamWriteUInt8(streamId, spec.currentUnitIndex)
    end
end

---Called on dischrage state changed
---@param dischargeState integer
function ChangeableDischargeFillUnits:onDischargeStateChanged(dischargeState)
    self:updateChangeableFillUnitActionEvents()
end

---Loads changeable discharge fill unit entry from xmlFile
---@param xmlFile table
---@param key string
---@param dischargeFillUnit table
---@return boolean loaded Returns true if loading was successfull, false otherwise
function ChangeableDischargeFillUnits:loadChangeableDischargeFillUnit(xmlFile, key, dischargeFillUnit)
    local fillUnitIndex = xmlFile:getValue(key .. "#fillUnitIndex")
    local displayText = xmlFile:getValue(key .. "#displayText")

    if fillUnitIndex == nil or displayText == nil then
        return false
    end

    dischargeFillUnit.fillUnitIndex = fillUnitIndex
    dischargeFillUnit.displayText = g_i18n:convertText(displayText, self.customEnvironment)

    return true
end

---Sets current discharge unit
---@param unitIndex integer
---@param noEventSend boolean
function ChangeableDischargeFillUnits:setDischargeFillUnit(unitIndex, noEventSend)
    local spec = self.spec_changeableDischargeFillUnits

    if spec.currentUnitIndex == nil then
        return
    end

    if unitIndex ~= spec.currentUnitIndex then
        if spec.changeableUnits[unitIndex] == nil then
            unitIndex = 1
        end

        spec.currentUnitIndex = unitIndex
        ChangeableDischargeFillUnitChangeEvent.sendEvent(self, unitIndex, noEventSend)

        local dischargeNode = self:getCurrentDischargeNode()
        dischargeNode.fillUnitIndex = spec.changeableUnits[unitIndex].fillUnitIndex

        self:updateChangeableFillUnitActionEvents()
    end
end

-------------------
-- Action Events --
-------------------

---Called on register action events
function ChangeableDischargeFillUnits:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
	if self.isClient then
        local spec = self.spec_changeableDischargeFillUnits

        if spec.currentUnitIndex ~= nil then
            self:clearActionEventsTable(spec.actionEvents)

            if isActiveForInputIgnoreSelection then
                local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.IMPLEMENT_EXTRA2, self, ChangeableDischargeFillUnits.changeDischargeFillUnit, false, true, false, true, nil)
                g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_HIGH)
                spec.actionEventId = actionEventId

                self:updateChangeableFillUnitActionEvents()
            end
        end
    end
end

---ActionEvent callback changeDischargeFillUnit
function ChangeableDischargeFillUnits.changeDischargeFillUnit(self, actionName, inputValue, callbackState, isAnalog)
    local spec = self.spec_changeableDischargeFillUnits

    if spec.currentUnitIndex == nil then
        return
    end

    if self:getDischargeState() == Dischargeable.DISCHARGE_STATE_OFF then
        local nextUnitIndex = spec.currentUnitIndex + 1
        if nextUnitIndex > #spec.changeableUnits then
            nextUnitIndex = 1
        end

        self:setDischargeFillUnit(nextUnitIndex)
    end
end

---Updates action event text
function ChangeableDischargeFillUnits:updateChangeableFillUnitActionEvents()
    local spec = self.spec_changeableDischargeFillUnits

    if spec.actionEventId == nil or spec.currentUnitIndex == nil then
        return
    end

    local textActive = self:getDischargeState() == Dischargeable.DISCHARGE_STATE_OFF
	g_inputBinding:setActionEventActive(spec.actionEventId, textActive)

    if textActive then
        local text = spec.changeableUnits[spec.currentUnitIndex].displayText
        g_inputBinding:setActionEventText(spec.actionEventId, text)
    end
end
