#include "../config/constants.lua"
#include "helpers/mathfr.lua"

function init()
    trigger = FindTrigger()
    tr = GetTriggerTransform(trigger)
end

function getWheels(veh)
    local list = GetEntityChildren(veh, "", true, "wheel")
    if #list == 0 then
        return
    end

    list.heights = {}
    for i = 1, #list do
        local shape = list[i]+1
        list[i] = GetShapeBody(shape)
        local _, _, y, s = GetShapeSize(shape)
        s = math.floor(s*100)/100
        list.heights[i] = y*s
    end

    return list
end

function isOnHead(tr, vehicle)
    local dir = {0, 1, 0}

    local mi, ma = GetBodyBounds(GetVehicleBody(vehicle))
    local center = VecLerp(mi, ma, 0.5)

    QueryRejectVehicle(vehicle)
    QueryRequire("physical")
    QueryLayerFilter(COLLISIONS.BLOCK_TERRAIN)
    local hit = QueryRaycast(center, TransformToParentVec(tr, dir), AVG_CAR_HEIGHT/2)
    return hit
end

function getFlyingState(tr, veh, wheels)
    local dir = TransformToParentVec(tr, {0, -1})
    local no_hits = 0
    for i = 1, #wheels do
        local body = wheels[i]
        local pos = GetBodyTransform(body).pos
        QueryRequire('physical large')
        QueryRejectVehicle(veh)
        QueryLayerFilter(COLLISIONS.BLOCK_TERRAIN)
        local h = QueryRaycast(pos, dir, wheels.heights[i])
        if not h then
            no_hits = no_hits + 1
        else
            break
        end
    end
    return no_hits == #wheels and not isOnHead(tr, veh)
end

local veh, wheels
local applied_force, was_in_trigger
function update(dt)
    veh = GetPlayerVehicle()
    if veh == 0 then return end

    if not was_in_trigger and not IsVehicleInTrigger(trigger, veh) then return end

    local tr = GetVehicleTransform(veh)

    wheels = wheels or getWheels(veh)
    if not getFlyingState(tr, veh, wheels) then applied_force, was_in_trigger = nil, nil return end

    if not IsVehicleInTrigger(trigger, veh) then applied_force, was_in_trigger = nil, nil return end

    local rx, _, rz = GetQuatEuler(tr.rot)
    -- limiting
    if ((rx > 30 and rx < 330) or (rx < -30 and rx > -330))
    or ((rz > 30 and rz < 330) or (rz < -30 and rz > -330)) then
        return
    end

    local v_b = GetVehicleBody(veh)
    local vel = GetBodyVelocity(v_b)
    local vel_l = VecLength(vel)
    if vel_l < 13 then return end

    was_in_trigger = true
    if not applied_force then
        local m = GetBodyMass(v_b)
        local jb = GetJointedBodies(v_b)
        local centers = {}
        for i = 1, #jb do
            local body = jb[i]
            local com = GetBodyCenterOfMass(body)
            local worldPoint = TransformToParentPoint(GetBodyTransform(body), com)
            centers[#centers+1] = worldPoint
        end
        local center = math:VecMidPoint(unpack(centers))
        ApplyBodyImpulse(v_b, center, {0, m * 3.3, 0})
        applied_force = true
    end
end
