----------------------------------------------------------------------------------------------------
-- LiftingAxles
----------------------------------------------------------------------------------------------------
-- Purpose: Specialization for automatic lifting axle animation by fillUnit fillLevel
--
-- @author John Deere 6930 @VertexDezign
----------------------------------------------------------------------------------------------------

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

function LiftingAxles.initSpecialization(specializations)
    local schema = Vehicle.xmlSchema
    schema:setXMLSpecializationType("LiftingAxles")
    schema:register(XMLValueType.STRING, "vehicle.liftingAxles.liftingAxle(?)#animationName", "Animation name play.")
    schema:register(XMLValueType.FLOAT, "vehicle.liftingAxles.liftingAxle(?)#liftAtFillLevel", "Filllevel to lift the axle.")
    schema:register(XMLValueType.VECTOR_N, "vehicle.liftingAxles.liftingAxle(?)#fillUnitIndices", "Fill unit indices to look at.")
    schema:register(XMLValueType.BOOL, "vehicle.liftingAxles.liftingAxle(?)#inverted", "Boolean if animation is played in reverse.")
    schema:register(XMLValueType.VECTOR_N, "vehicle.liftingAxles.liftingAxle(?)#steeringAxleWheelIndices", "Deactivate steering angle wheel indices")
    schema:setXMLSpecializationType()
end

function LiftingAxles.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(AnimatedVehicle, specializations) and SpecializationUtil.hasSpecialization(FillUnit, specializations)
end

function LiftingAxles.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "loadLiftingAxle", LiftingAxles.loadLiftingAxle)
    SpecializationUtil.registerFunction(vehicleType, "updateDeactivatedSteeringAxle", LiftingAxles.updateDeactivatedSteeringAxle)
end

function LiftingAxles.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", LiftingAxles)
    SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", LiftingAxles)
    SpecializationUtil.registerEventListener(vehicleType, "onFillUnitFillLevelChanged", LiftingAxles)
end

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

    spec.liftingAxles = {}

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

---Called after load
function LiftingAxles:onPostLoad(savegame)
    local spec = self.spec_liftingAxles
    for _, liftingAxle in ipairs(spec.liftingAxles) do
        self:playAnimation(liftingAxle.animationName, 1, nil, true)
        AnimatedVehicle.updateAnimationByName(self, liftingAxle.animationName, 9999999)
        self:updateDeactivatedSteeringAxle(liftingAxle, 1)
    end
end

---Called on fillUnit fillLevel change
---@param fillUnitIndex integer
---@param fillLevelDelta number
---@param fillTypeIndex integer
---@param toolType integer
---@param fillPositionData table
---@param appliedDelta number
function LiftingAxles:onFillUnitFillLevelChanged(fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData, appliedDelta)
    local spec = self.spec_liftingAxles

    for _, liftingAxle in pairs(spec.liftingAxles) do
        local overallFillLevel = 0

        for _, index in ipairs(liftingAxle.fillUnitIndices) do
            if self:getFillUnitExists(index) then
                overallFillLevel = overallFillLevel + self:getFillUnitFillLevel(index)
            end
        end

        local direction = overallFillLevel >= liftingAxle.liftAtFillLevel and -1.0 or 1.0
        if direction ~= 0 and not self:getIsAnimationPlaying(liftingAxle.animationName) then
            if liftingAxle.inverted then
                direction = -direction
            end

            self:playAnimation(liftingAxle.animationName, direction, nil, true)
            self:setAnimationStopTime(liftingAxle.animationName, direction > 0 and 1.0 or 0.0)
            self:updateDeactivatedSteeringAxle(liftingAxle, direction)
        end
    end
end

---Loads lifting axle entry from xmlFile
---@param xmlFile table
---@param key string
---@param liftingAxle table
---@return boolean loaded Returns true if loading was successfull, false otherwise
function LiftingAxles:loadLiftingAxle(xmlFile, key, liftingAxle)
    local fillUnitIndices = xmlFile:getValue(key .. "#fillUnitIndices", nil, true)
    if fillUnitIndices == nil then
        return false
    end

    liftingAxle.fillUnitIndices = fillUnitIndices
    liftingAxle.animationName = self.xmlFile:getValue(key .. "#animationName")
    if liftingAxle.animationName == nil or liftingAxle.animationName == "" then
        return false
    end

    liftingAxle.liftAtFillLevel = self.xmlFile:getValue(key .. "#liftAtFillLevel", 1.0)
    liftingAxle.inverted = self.xmlFile:getValue(key .. "#inverted", false)
    liftingAxle.steeringAxleWheelIndices = self.xmlFile:getValue(key .. "#steeringAxleWheelIndices", nil, true)

    return true
end

---Updates steering axle angle of lifting axle
---@param liftingAxle table
---@param direction number
function LiftingAxles:updateDeactivatedSteeringAxle(liftingAxle, direction)
    if liftingAxle.steeringAxleWheelIndices == nil then
        return
    end

    local wheels = self:getWheels()
    for _, wheelIndex in ipairs(liftingAxle.steeringAxleWheelIndices) do
        local wheel = wheels[wheelIndex]

        if wheel ~= nil then
            if wheel.steeringAxleRotMinBackUp == nil then
                wheel.steeringAxleRotMinBackUp = wheel.steeringAxleRotMin
            end
            if wheel.steeringAxleRotMaxBackUp == nil then
                wheel.steeringAxleRotMaxBackUp = wheel.steeringAxleRotMax
            end

            if direction == 1 then
                wheel.steeringAxleRotMin = 0
                wheel.steeringAxleRotMax = 0
            elseif direction == -1 then
                wheel.steeringAxleRotMin = wheel.steeringAxleRotMinBackUp
                wheel.steeringAxleRotMax = wheel.steeringAxleRotMaxBackUp
            end
        end
    end
end
