#include "../config/events.lua"
#include "helpers/entity.lua"
#include "helpers/timer.lua"

function init()
    joints = {}
    vehs = {}
    limits_min, limits_max = {}, {}
    disabled = nil
    RegisterListenerTo(EVENTS.STATS_FILLED, "OnStatsFilled")
end

function OnStatsFilled()
    local list = ListKeys("level.players")
    for i = 1, #list do
        local veh = GetInt("level.players." .. i)
        local is_garbagetruck = GetTagValue(veh, "id") == "garbagetruck"
        local veh_joints = cEntity:GetVehicleJoints(veh)
        for j = 1, #veh_joints do repeat
            local joint = veh_joints[j]
            if HasTag(joint, "thresher") then break end
            if HasTag(joint, "roll") then break end
            if GetJointType(joint) == "prismatic" then break end
            if is_garbagetruck and not HasTag(joint, "turn") then break end

            joints[#joints+1] = joint
            local min, max = GetJointLimits(joint)
            limits_min[#limits_min+1] = min
            limits_max[#limits_max+1] = max
        until true end
        vehs[i] = #joints
    end
    UnregisterListener(EVENTS.STATS_FILLED, "OnStatsFilled")
    nextVeh = (function(self)
        local i = 1
        return coroutine.wrap(function()
            while true do
                coroutine.yield(vehs[i], i)
                i = i%#vehs+1
            end
        end)
    end)()
    timer_manager:CreateTimer("joints_fixer_update", 0.1, true, ProcessVehicle)
end

function updateJointsArr(joint, i)
    Delete(joint)
    table.remove(joints, i)
    table.remove(limits_min, i)
    table.remove(limits_max, i)
    if #joints == 0 then
        disabled = cEntity:EnqueueDeletion()
        return false
    end
    local found
    for j = 1, #vehs do
        local ul, ll = vehs[j], j == 1 and 0 or vehs[j-1]
        if i > ll and i <= ul then
            found = j
            break
        end
    end
    if found then
        for j = found, #vehs do
            vehs[j] = vehs[j] - 1
        end
    end
    return true
end

function ProcessVehicle()
    local ul, index = nextVeh()
    local ll = index == 1 and 1 or vehs[index-1]+1

    for i = ul, ll, -1 do
        local joint = joints[i]
        if IsJointBroken(joint) or jointedShapesAreBroken(joint) then
            if not updateJointsArr(joint, i) then
                return
            end
        else
            local cur = GetJointMovement(joint)
            local max = limits_max[i]
            if (cur > max and cur - max > 15) then
                SetJointMotorTarget(joint, max)
            else
                local min = limits_min[i]
                if (cur < min and min - cur > 15) then
                    SetJointMotorTarget(joint, min)
                end
            end
        end
    end
end

function jointedShapesAreBroken(joint)
    local list = GetJointShapes(joint)
    local n_broken = 0
    for i = 1, #list do
        local shape = list[i]
        if HasTag(shape, "broken") then
            n_broken = n_broken + 1
        else
            local body = GetShapeBody(shape)
            if GetBodyVehicle(body) == 0 or IsBodyJointedToStatic(body) and not IsBodyDynamic(GetShapeBody(GetJointOtherShape(joint, shape))) then
                return true
            end
        end
    end
    return n_broken == #list
end

function update(dt)
    if disabled then return end
    timer_manager:Tick(dt)
end
