--[[ ===================================================== ]]--
--[[           MH NPC Services Script by MaDHouSe          ]]--
--[[ ===================================================== ]]--

local QBCore = exports['qb-core']:GetCoreObject()
local stuckTimerCheck = Config.StuckTimerCheck
local stuckResetTimer = Config.StuckResetTimer
local company = {}
local count = 0
local phoneProp = 0
local waitTime = 60
local cooldownTime = Config.Cooldown
local jailTime = math.random(Config.MinJailTime, Config.MaxJailTime)
local isInJail = false
local followPedCamMode
local lastCoords = nil

local function DeletePhoneProp()
	if phoneProp ~= 0 then
		Citizen.InvokeNative(0xAE3CBE5BF394C9C9 , Citizen.PointerValueIntInitialized(phoneProp))
		phoneProp = 0
	end
end

local function CreatePhoneProp()
	DeletePhoneProp()
	RequestModel(Config.PhoneModel)
	while not HasModelLoaded(Config.PhoneModel) do Citizen.Wait(1) end
	phoneProp = CreateObject(Config.PhoneModel, 1.0, 1.0, 1.0, 1, 1, 0)
	local bone = GetPedBoneIndex(PlayerPedId(), 28422)
	AttachEntityToEntity(phoneProp, PlayerPedId(), bone, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1, 1, 0, 0, 2, 1)
end

local function loadAnimDict(dict)
    while (not HasAnimDictLoaded(dict)) do
        RequestAnimDict(dict)
        Wait(5)
    end
end

local function loadModel(model)
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(1) end
end

local function CallAnimation(job)
    if not QBCore.Functions.GetPlayerData().metadata['isdead'] then
        local dictionary = Config.CallAnimation.call.dictionary
        local animation = Config.CallAnimation.call.animation
        if not QBCore.Functions.GetPlayerData().job.name == "taxi" or not QBCore.Functions.GetPlayerData().job.name == "limousine" then
            if QBCore.Functions.GetPlayerData().job.name == "police" or QBCore.Functions.GetPlayerData().job.name == "ambulance" or QBCore.Functions.GetPlayerData().job.name == "mechanic" then
                dictionary = Config.CallAnimation.jobcall.dictionary
                animation = Config.CallAnimation.jobcall.animation
            end
        end
        loadAnimDict(dictionary)
        CreatePhoneProp()
        QBCore.Functions.Notify(Lang:t('notify.you_are_calling', {job = job}), "success")
        TaskPlayAnim(PlayerPedId(), dictionary, animation, 1.5, 1.0, -1, 50, 2.0, 0, 0, 0)
        Wait(8100)
        ClearPedTasks(PlayerPedId())
	DeletePhoneProp()
	Wait(500)
        if job == "mechanic" and IsPedInAnyVehicle(PlayerPedId(), false) then
            local vehicle = GetVehiclePedIsIn(PlayerPedId())
            SetVehicleDoorsLocked(vehicle, 0)
            TaskLeaveVehicle(PlayerPedId(), vehicle, 1)
        end
    end
end

local function CalculateTaxiPrice(job, from, to)
    local route = CalculateTravelDistanceBetweenPoints(from.x, from.y, from.z, to.x, to.y, to.z)
    local price = math.floor((route / 1000) * Config.Service[job].price + 0.5)
    return price
end

local function TempDisableControl()
    DisableAllControlActions(0)
    EnableControlAction(0, 1, true)
    EnableControlAction(0, 2, true)
    EnableControlAction(0, 245, true)
    EnableControlAction(0, 38, true)
    EnableControlAction(0, 322, true)
    EnableControlAction(0, 249, true)
    EnableControlAction(0, 46, true)
end

local function GetDistance(pos1, pos2)
    return #(vector3(pos1.x, pos1.y, pos1.z) - vector3(pos2.x, pos2.y, pos2.z))
end

local function SetCompanyData(job)
    company = {
        name = Config.Service[job].name,
        job = Config.Service[job].job,
        ped = Config.JobPeds[job].models[math.random(1, #Config.JobPeds[job].models)],
        plate = Config.Service[job].plate,
        home = Config.Service[job].home,
        color = Config.Service[job].color,
        checkin = Config.Service[job].checkin,
        vehicleDrop = Config.Service[job].vehicleDrop,
        playerDrop  = Config.Service[job].playerDrop,
        driveStyle = Config.Service[job].driveStyle,
        walkStyle = Config.Service[job].walkStyle,
        speed = Config.Service[job].speed,
        price = Config.Service[job].price,
        truck_offset = Config.Service[job].truck_offset,
        spawnRadius = Config.Service[job].spawnRadius,
        spotRadius = Config.Service[job].spotRadius,
        passengerSeat =  Config.Service[job].passengerSeat,
        isCalled = false,
        isWaiting = false,
        isReadyToGo = false,
        driveToPlayer = false,
        driveToCompany = false,
        driveToLocation = false,
        driveToMechanic = false,
        driverIsInVehicle = false,
        playerIsInVehicle = false,
        driveAway = false,
        isHandcuffed = false,
        isDriving = false,
        isEscorted = false,
        damage_vehicle = nil,
        coords = nil,
        vehicle = nil,
        driver = nil,
        codriver = nil,
        blip = nil,
    }
    count = 0
end

local function DriveToLocation(from, to)
    if company.driver ~= nil and company.vehicle ~= nil then
        company.coords = to
        if GetDistance(from, to) < 500 then
            TaskVehicleDriveToCoord(company.driver, company.vehicle, to.x, to.y, to.z, company.speed, 0, GetEntityModel(company.vehicle), company.driveStyle, 2.0, true)
        else
            TaskVehicleDriveToCoordLongrange(company.driver, company.vehicle, to.x, to.y, to.z, company.speed, company.driveStyle, 2.0)
        end
        SetPedKeepTask(company.driver, true)
	if company.job == "police" then SetVehicleSiren(company.vehicle, true) end
        if company.job == "ambulance" then SetVehicleSiren(company.vehicle, true) end
        company.isDriving = true
    end
end

local function DrawTxt(x, y, width, height, scale, text, r, g, b, a)
    SetTextFont(4)
    SetTextProportional(0)
    SetTextScale(scale, scale)
    SetTextColour(r, g, b, a)
    SetTextDropShadow(0, 0, 0, 0,255)
    SetTextEdge(2, 0, 0, 0, 255)
    SetTextDropShadow()
    SetTextOutline()
    SetTextEntry("STRING")
    AddTextComponentString(text)
    DrawText(x - width/2, y - height/2 + 0.005)
end

local function Reset()
    if company.vehicle ~= nil then DeleteEntity(company.vehicle) end
    if company.driver ~= nil then DeleteEntity(company.driver) end
    if company.codriver ~= nil then DeleteEntity(company.codriver) end
    company = {}
    company.isCalled = false
    company.isWaiting = false
    company.driveToPlayer = false
    company.driveToCompany = false
    company.driveToLocation = false
    company.driveToMechanic = false
    company.driverIsInVehicle = false
    company.playerIsInVehicle = false
    company.isHandcuffed = false
    company.isDriving = false
    company.isEscorted = false
    if QBCore.Functions.GetPlayerData().metadata['isdead'] or QBCore.Functions.GetPlayerData().metadata['inlaststand'] then
        company.sendNotify = true
    else
        company.sendNotify = false
    end
    lastCoords = nil
end

local function VehicleStuckCheck()
    if company.driver ~= nil and company.vehicle ~= nil and lastCoords ~= nil then
        if GetEntitySpeed(company.vehicle) < 0.9 then
            stuckTimerCheck = stuckTimerCheck - 1
            if stuckTimerCheck <= 0 then
                if GetDistance(GetEntityCoords(company.vehicle), lastCoords) <= 1.0 then
                    local coords = GetEntityCoords(company.vehicle)
                    local haeding = GetEntityHeading(company.vehicle)
                    local _, spawnPosition, spawnHeading = GetClosestVehicleNodeWithHeading(coords.x + 5.0, coords.y, coords.z, 0, 3, 0)
                    ClearAreaOfVehicles(spawnPosition, 15000, false, false, false, false, false)
                    SetEntityCoords(company.vehicle, spawnPosition, false, false, false, true)
                    SetEntityHeading(company.vehicle, haeding)
                    DriveToLocation(coords, company.coords)
                end
                stuckTimerCheck = stuckResetTimer
            end
        end
        lastCoords = nil
    end
end

local function CreateServicesBlips(entity, label)
    local blip = GetBlipFromEntity(entity)
    if not DoesBlipExist(blip) then
        blip = AddBlipForEntity(entity)
        SetBlipSprite(blip, 161)
        SetBlipScale(blip, 1.0)
        SetBlipColour(blip, company.color)
        SetBlipAsShortRange(blip, true)
        BeginTextCommandSetBlipName('STRING')
        AddTextComponentString(label)
        EndTextCommandSetBlipName(blip)
    end
    if GetBlipFromEntity(PlayerPedId()) == blip then
        RemoveBlip(blip)
    end
    return blip
end

local function LeaveTarget()
    if company.driver ~= nil and company.vehicle ~= nil then
        ClearPedTasks(company.driver)
        SetPedKeepTask(company.driver, true)
        company.coords = vector3(127.2708, 642.6805, 207.4947)
        DriveToLocation(GetEntityCoords(company.vehicle), company.coords) -- From To
        company.driveAway = true
        SetTimeout(15000, function()
            Reset()
        end)
    end
end

local function CheckIfCompanyPedIsDeath()
    if company.vehile ~= nil or company.driver ~= nil or company.codriver ~= nil then 
        if DoesEntityExist(company.driver) then
            if IsEntityDead(company.driver) then
                if company.codriver ~= nil then DeleteEntity(company.codriver) end
                DeleteEntity(company.driver)
                DeleteEntity(company.vehile)
                Reset()
            end
        end
    end
end

local function GoToMechanicShop()
    QBCore.Functions.TriggerCallback("qb-npcservices:server:CanIPayTheBill", function(isPaid)
        if isPaid then
            SetVehicleDoorOpen(company.damage_vehicle, 4, false, false)
            TaskTurnPedToFaceCoord(company.driver, GetEntityCoords(company.damage_vehicle), -1)
            TaskStartScenarioInPlace(company.driver, "PROP_HUMAN_BUM_BIN", 0, false)
            QBCore.Functions.Progressbar("towtruck", company.job.." is helping...", 10000, false, false, {
                disableMovement = false,
                disableCarMovement = false,
                disableMouse = false,
                disableCombat = false,
            }, {}, {}, {}, function() -- Done
                ClearPedTasks(company.driver)
                ClearPedTasks(PlayerPedId())
                AttachEntityToEntity(company.damage_vehicle, company.vehicle, 20, company.truck_offset.x, company.truck_offset.y, company.truck_offset.z, 0.0, 0.0, 0.0, false, false, false, false, 20, true)
                company.isWaiting = true
            end)
        else
            QBCore.Functions.Notify(Lang:t('notify.cant_pay',{price = company.price}), "error")
        end
    end, company.job, company.price)
end

local function HelpOnLocation()
    QBCore.Functions.TriggerCallback("qb-npcservices:server:CanIPayTheBill", function(isPaid)
        if isPaid then
            if company.job == "ambulance" then
                local dictionary = "mini@cpr@char_a@cpr_str"
                local animation = "cpr_pumpchest"
                loadAnimDict(dictionary)
                TaskTurnPedToFaceCoord(company.driver, GetEntityCoords(PlayerPedId()), -1)
                TaskPlayAnim(company.driver, dictionary, animation, 1.0, 1.0, -1, 9, 1.0, 0, 0, 0)
            end
            if company.job == "mechanic" then
                local animation = "PROP_HUMAN_BUM_BIN"
                SetVehicleUndriveable(company.damage_vehicle, true)
                SetVehicleDoorOpen(company.damage_vehicle, 4, false, false)
                TaskTurnPedToFaceCoord(company.driver, GetEntityCoords(company.damage_vehicle), -1)
                TaskStartScenarioInPlace(company.driver, animation, 0, false)
            end
            QBCore.Functions.Progressbar("jobstuff", company.job.." is helping", 10000, false, false, {
                disableMovement = false,
                disableCarMovement = false,
                disableMouse = false,
                disableCombat = true,
            }, {}, {}, {}, function() -- Done
                ClearPedTasks(company.driver)
                if company.job == "ambulance" then
                    ClearPedTasks(company.codriver)
                    TaskWarpPedIntoVehicle(company.codriver, company.vehicle, 0)
                    TriggerEvent("hospital:client:Revive")
                    StopScreenEffect('DeathFailOut')
                end
                if company.job == "mechanic" then
                    SetVehicleDoorShut(company.damage_vehicle, 4, false, false)
                    Wait(1000)
                    SetVehicleFixed(company.damage_vehicle)
                    SetVehicleEngineHealth(company.damage_vehicle, 1000.0)
                    SetVehicleBodyHealth(company.damage_vehicle, 1000.0)
                    SetVehicleOnGroundProperly(company.damage_vehicle)
                    SetVehicleUndriveable(company.damage_vehicle, false)
                end
                company.coords = company.home
                TriggerServerEvent('qb-npcservices:server:pay', company.job, company.price)
                LeaveTarget()
            end)
        else
            QBCore.Functions.Notify(Lang:t('notify.cant_pay',{price = company.price}), "error")
        end
    end, company.job, company.price)
end

local function GoToLocation()
    if DoesBlipExist(GetFirstBlipInfoId(8)) and not company.driveToLocation and not company.isDriving then
        SetBlockingOfNonTemporaryEvents(company.driver, true)
        local x, y, z = table.unpack(Citizen.InvokeNative(0xFA7C7F0AADF25D09, GetFirstBlipInfoId(8), Citizen.ResultAsVector()))
        PlayAmbientSpeech1(company.driver, "TAXID_BEGIN_JOURNEY", "SPEECH_PARAMS_FORCE_NORMAL")
        company.coords = vector3(x, y, z)
        DriveToLocation(GetEntityCoords(company.vehicle), company.coords)
        company.driveToLocation = true
    end
end

local function GoToCompany()
    QBCore.Functions.TriggerCallback("qb-npcservices:server:canPay", function(canPay)
        if canPay then
            company.coords = company.home
            if company.job == "police" or company.job == "ambulance" then
                SetVehicleSiren(company.vehicle, true)
                SetVehicleDoorsLocked(company.vehicle, 1)
                TaskWarpPedIntoVehicle(company.codriver, company.vehicle, 1)
                if company.job == "ambulance" then TaskWarpPedIntoVehicle(company.codriver, company.vehicle, 2) end
                if company.job == "police" then TaskWarpPedIntoVehicle(company.codriver, company.vehicle, 0) end
            end
            if company.job == 'mechanic' then
                PlayAmbientSpeech1(company.driver, "THANKS", "SPEECH_PARAMS_FORCE_NORMAL")
            end
            company.driveToCompany = true
            DriveToLocation(GetEntityCoords(company.vehicle), company.home)
        else
            PlayAmbientSpeech1(company.driver, "TAXID_NO_MONEY", "SPEECH_PARAMS_FORCE_NORMAL")
            QBCore.Functions.Notify(Lang:t('notify.cant_pay',{price = company.price}), "error")
        end
    end, company.job, company.price)
end

local function CreateServicesPed(coords, vehicle, seat)
    local model = GetHashKey(company.ped)
    loadModel(model)
    local ped = CreatePed(4, model, coords.x, coords.y, coords.z, 0, true, true)
    if company.job == 'police' then GiveWeaponToPed(ped, Config.Weapons[math.random(1, #Config.Weapons)], 999, false, true) end
    SetPedIntoVehicle(ped, vehicle, seat)
    SetEntityAsMissionEntity(ped, true, true)
    SetModelAsNoLongerNeeded(model)
    SetPedKeepTask(ped, true)
    SetBlockingOfNonTemporaryEvents(ped, true)
    SetPedSweat(ped, 100.0)
    return ped
end

local function SelectVehicleModel()
    local model = Config.Vehicles[company.job].models[math.random(1, #Config.Vehicles[company.job].models)]
    loadModel(model)
    if company.job == "mechanic" and company.damage_vehicle ~= nil then
        if GetVehicleEngineHealth(company.damage_vehicle) < Config.MinDamageForFlatbed then
            model = Config.Vehicles[company.job].models[1]
        else
            model = Config.Vehicles[company.job].models[2]
        end
    end
    return model
end

local function CreateServicesVehicle(coords, heading)
    ClearAreaOfVehicles(coords, 10000, false, false, false, false, false)
    local model = SelectVehicleModel()
    loadModel(model)
    local vehicle = CreateVehicle(model, coords, heading, true, true)
    SetEntityInvincible(vehicle, true)
    NetworkSetEntityInvisibleToNetwork(vehicle, true)
    SetEntityAsMissionEntity(vehicle, true, true)
    SetVehicleDirtLevel(vehicle, 0)
    SetVehicleBodyHealth(vehicle, 1000.0)
    SetVehicleEngineHealth(vehicle, 1000.0)
    SetVehicleFuelLevel(vehicle, 100.0)
    SetVehicleDoorsLocked(vehicle, 1)
	DecorSetFloat(vehicle, "_FUEL_LEVEL", GetVehicleFuelLevel(vehicle))
    SetEntityHeading(vehicle, heading)
    SetVehicleOnGroundProperly(vehicle)
    SetVehicleHasUnbreakableLights(vehicle, true)
    SetVehicleNumberPlateText(vehicle, company.plate)
    SetVehicleEngineOn(vehicle, true, true, false)
    SetVehicleLights(vehicle, 2)
    SetModelAsNoLongerNeeded(model)
    return vehicle
end

local function CallServices()
    if isInJail then return end
    if company.job == "taxi" or company.job == "limousine" then
        local waypoint = GetFirstBlipInfoId(8)
        if not DoesBlipExist(GetFirstBlipInfoId(8)) then
            if company.job == 'taxi' then
                return QBCore.Functions.Notify(Lang:t('job.taxi.missing_waypoint'), "error")
            else
                return QBCore.Functions.Notify(Lang:t('job.limousine.missing_waypoint'), "error")
            end
        end
    end
    local coords = GetEntityCoords(PlayerPedId())
    if Config.ForceFirstperson then followPedCamMode = GetFollowPedCamViewMode() end
    local _, spawnPosition, spawnHeading = GetClosestVehicleNodeWithHeading(coords.x + math.random(-company.spawnRadius, company.spawnRadius), coords.y + math.random(-company.spawnRadius, company.spawnRadius), coords.z, 0, 3, 0)
    company.vehicle = CreateServicesVehicle(spawnPosition, spawnHeading)
    company.driver = CreateServicesPed(spawnPosition, company.vehicle, -1)
    SetEntityAsMissionEntity(company.vehicle, true, true)
    SetEntityAsMissionEntity(company.driver, true, true)
    if company.job == "ambulance" then
        company.codriver = CreateServicesPed(spawnPosition, company.vehicle, 0)
    elseif company.job == "police" then
        company.codriver = CreateServicesPed(spawnPosition, company.vehicle, 0)
    end
    if company.driver ~= nil and company.vehicle ~= nil then
        if company.job == "police" or company.job == "ambulance" then SetVehicleSiren(vehicle, true) end
        company.blip = CreateServicesBlips(company.vehicle, company.name)
        DriveToLocation(spawnPosition, coords) -- From To
        company.driveToPlayer = true
	company.driverIsInVehicle = true
        if company.job == "police" then SetFakeWantedLevel(6) end
    end
end

local function WalkToVehicle()
    if isInJail then return end
    if not company.isEscorted then
        company.isEscorted = true
        company.isHandcuffed = false
        if company.job == "police" then
            local heading = GetEntityHeading(PlayerPedId())
            TriggerServerEvent("InteractSound_SV:PlayOnSource", "Cuff", 0.2)
            loadAnimDict("mp_arrest_paired")
            Wait(1000)
            TaskPlayAnim(PlayerPedId(), "mp_arrest_paired", "crook_p2_back_right", 3.0, 3.0, -1, 32, 0, 0, 0, 0 ,true, true, true)
            company.isHandcuffed = true
            Wait(2500)
        end
        TaskGoToCoordAnyMeans(company.driver, GetEntityCoords(company.vehicle), 2.0, 0, 0, company.walkStyle, 0xbf800000)
        AttachEntityToEntity(PlayerPedId(), company.driver, 11816, 0.0, -0.45, 0.0, 0.0, 0.0, 0.0, false, false, false, true, 2, true)
        GoToCompany()
    end
end

AddEventHandler('onResourceStart', function(resource)
    if resource == GetCurrentResourceName() then
        SetFakeWantedLevel(0)
        company = {}
        Reset()
        lastCoords = nil
    end
end)

RegisterNetEvent('qb-npcservices:client:sendService', function(callData)
    if isInJail then return end
    if #company == 0 and not company.isCalled then
	CallAnimation(callData.job)
        SetCompanyData(callData.job)
        company.isCalled = true
        company.price = callData.price
        if callData.job == "mechanic" then
            local vehicle, _ = QBCore.Functions.GetClosestVehicle(GetEntityCoords(PlayerPedId()))
            local props = QBCore.Functions.GetVehicleProperties(vehicle)
            QBCore.Functions.TriggerCallback('qb-npcservices:server:IsVehicleOwner', function(owned)
                if owned then
                    company.coords = GetEntityCoords(vehicle)
                    company.damage_vehicle = vehicle
                    QBCore.Functions.Notify(Lang:t('notify.call_company', {company = company.name}), "success", 8000)
                    Wait(8500)
                    CallServices()
                else
                    QBCore.Functions.Notify(Lang:t('error.not_the_owner'), "error")
                end
            end, props.plate)
        else
            QBCore.Functions.Notify(Lang:t('notify.call_company', {company = company.name}), "success", 8000)
            Wait(8500)
            CallServices()
        end
    end
end)

RegisterNetEvent('qb-npcservices:client:menu', function()
    if isInJail then return end
    local playerlist = {}
    QBCore.Functions.TriggerCallback('qb-npcservices:server:GetOnlinePlayers', function(online)
        for key, v in pairs(online) do
            if v.source == GetPlayerServerId(PlayerId()) then 
                playerlist[#playerlist + 1] = {value = v.source, text = "(ID:"..v.source..") "..v.fullname} 
            end
            local player, distance = QBCore.Functions.GetClosestPlayer()
            if player ~= -1 and distance < 2.5 then
                local playerId = GetPlayerServerId(player)
                if v.source ~= GetPlayerServerId(PlayerId()) then 
                    playerlist[#playerlist + 1] = {value = v.source, text = "(ID:"..v.source..") "..v.fullname} 
                end
            end
        end
        if playerlist == nil then
            return
        else
            QBCore.Functions.TriggerCallback("qb-npcservices:server:isEMSServices", function(isService)
                local menuData = {}
                if not IsPedInAnyVehicle(PlayerPedId()) then
                    if DoesBlipExist(GetFirstBlipInfoId(8)) then
                        menuData[#menuData + 1] = {value = "taxi", text = Lang:t('job.taxi.label') }
                        menuData[#menuData + 1] = {value = "limousine", text = Lang:t('job.limousine.label') }
                    end
                end
                if IsPedInAnyVehicle(PlayerPedId()) then
                    menuData[#menuData + 1] = {value = "mechanic", text = Lang:t('job.mechanic.label') }
                end
                menuData[#menuData + 1] = {value = "ambulance", text = Lang:t('job.ambulance.label') }
                if Config.UsePoliceAssist and QBCore.Functions.GetPlayerData().job.name == "police" and isService then
                    menuData[#menuData + 1] = { value = "police", text = Lang:t('job.police.label')}
                end
                local menu = exports["qb-input"]:ShowInput({
                    header = Lang:t('menu.title'),
                    submitText = "",
                    inputs = {
                        {
                            text = Lang:t('menu.select_player'),
                            name = "id",
                            type = "select",
                            options = playerlist,
                            isRequired = true
                        },
                        {
                            text = Lang:t('menu.select_company'),
                            name = "company",
                            type = "select",
                            options = menuData,
                            isRequired = true
                        }
                    }
                })
                if menu then
                    if not menu.id or not menu.company then return end
                    local call_data = {}
                    call_data.job = tostring(menu.company)
                    call_data.callerId = GetPlayerServerId(PlayerId())
                    call_data.price = Config.Service[call_data.job].price
                    if tonumber(menu.id) == GetPlayerServerId(PlayerId()) then
                        call_data.targetId = GetPlayerServerId(PlayerId())
                    else
                        call_data.targetId = tonumber(menu.id)
                    end
                    if call_data.job == 'taxi' or call_data.job == 'limousine' then
                        if DoesBlipExist(GetFirstBlipInfoId(8)) then
                            local x, y, z = table.unpack(Citizen.InvokeNative(0xFA7C7F0AADF25D09, GetFirstBlipInfoId(8), Citizen.ResultAsVector()))
                            local from = GetEntityCoords(PlayerPedId())
                            local to = vector3(x, y, z)
                            call_data.price = CalculateTaxiPrice(call_data.job, from, to)
                        end
                    end
                    TriggerServerEvent('qb-npcservices:server:sendService', call_data)
                end
            end)
        end
    end)
end)

RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if MenuItemId ~= nil then
        exports['qb-radialmenu']:RemoveOption(MenuItemId)
        MenuItemId = nil
    end
    MenuItemId = exports['qb-radialmenu']:AddOption(
        {
            id = 'callemsservices',
            title = Lang:t('menu.title'),
            icon = Config.RadialMenuIcone,
            type = 'client',
            event = 'qb-npcservices:client:menu',
            shouldClose = true
        },
    MenuItemId)
end)

CreateThread(function()
    while true do
        Wait(1)
        if company.isEscorted then
            DisableAllControlActions(0)
            EnableControlAction(0, 1, true)
			EnableControlAction(0, 2, true)
            EnableControlAction(0, 245, true)
            EnableControlAction(0, 38, true)
            EnableControlAction(0, 322, true)
            EnableControlAction(0, 249, true)
            EnableControlAction(0, 46, true)
        end
        if company.isHandcuffed then
            DisableControlAction(0, 24, true) -- Attack
			DisableControlAction(0, 257, true) -- Attack 2
			DisableControlAction(0, 25, true) -- Aim
			DisableControlAction(0, 263, true) -- Melee Attack 1
			DisableControlAction(0, 45, true) -- Reload
			DisableControlAction(0, 22, true) -- Jump
			DisableControlAction(0, 44, true) -- Cover
			DisableControlAction(0, 37, true) -- Select Weapon
			DisableControlAction(0, 23, true) -- Also 'enter'?
			DisableControlAction(0, 288, true) -- Disable phone
			DisableControlAction(0, 289, true) -- Inventory
			DisableControlAction(0, 170, true) -- Animations
			DisableControlAction(0, 167, true) -- Job
			DisableControlAction(0, 26, true) -- Disable looking behind
			DisableControlAction(0, 73, true) -- Disable clearing animation
			DisableControlAction(2, 199, true) -- Disable pause screen
			DisableControlAction(0, 59, true) -- Disable steering in vehicle
			DisableControlAction(0, 71, true) -- Disable driving forward in vehicle
			DisableControlAction(0, 72, true) -- Disable reversing in vehicle
			DisableControlAction(2, 36, true) -- Disable going stealth
			DisableControlAction(0, 264, true) -- Disable melee
			DisableControlAction(0, 257, true) -- Disable melee
			DisableControlAction(0, 140, true) -- Disable melee
			DisableControlAction(0, 141, true) -- Disable melee
			DisableControlAction(0, 142, true) -- Disable melee
			DisableControlAction(0, 143, true) -- Disable melee
			DisableControlAction(0, 75, true)  -- Disable exit vehicle
			DisableControlAction(27, 75, true) -- Disable exit vehicle
            EnableControlAction(0, 249, true) -- Added for talking while cuffed
            EnableControlAction(0, 46, true)  -- Added for talking while cuffed
        end
        if not company.isHandcuffed and not company.isEscorted then
            Wait(2000)
        end
    end
end)

-- npc is driving (Taxi/Police/Ambulance/Mechanic/TowTruck)
CreateThread(function()
    while true do
        if LocalPlayer.state.isLoggedIn then
            if company.driveToPlayer then
                local coords = nil
                if company.job == 'mechanic' then
                    coords = GetEntityCoords(company.damage_vehicle)
                else
                    coords = GetEntityCoords(PlayerPedId())
                end
                local vdistance = GetDistance(coords, GetEntityCoords(company.vehicle))
                local ddistance = GetDistance(coords, GetEntityCoords(company.driver))
                if company.job == 'mechanic' or company.job == 'taxi' or company.job == 'limousine' then
                    if QBCore.Functions.GetPlayerData().metadata['isdead'] or QBCore.Functions.GetPlayerData().metadata['inlaststand'] then
                        company.driveToPlayer = false
                        company.isDriving = false
                        ClearPedTasks(company.driver)
                        RemoveBlip(company.blip)
                        LeaveTarget()
                    end
                end
                if vdistance < company.spotRadius then
		    company.driverIsInVehicle = false
                    if company.job == "mechanic" then
                        local engine = GetWorldPositionOfEntityBone(company.damage_vehicle, GetEntityBoneIndexByName(company.damage_vehicle, "engine"))
                        TaskGoToCoordAnyMeans(company.driver, engine, 1.0, 0, 0, company.walkStyle, 0xbf800000)
                        ddistance = GetDistance(vector3(engine.x, engine.y, engine.z), GetEntityCoords(company.driver))
                    else
                        if company.job == "police" or company.job == "ambulance" then
                            if company.job == "police" then FreezeEntityPosition(PlayerPedId(), true) end
                            TaskGoToCoordAnyMeans(company.driver, coords, 2.0, 0, 0, company.walkStyle, 0xbf800000)
                        else
                            TaskGoToCoordAnyMeans(company.driver, coords, 1.0, 0, 0, company.walkStyle, 0xbf800000)
                        end
                    end
                    if company.job == 'police' or company.job == 'ambulance' then SetVehicleSiren(company.vehicle, false) end
                    if ddistance < Config.InteractDiustance then
                        ClearPedTasks(company.driver)
                        RemoveBlip(company.blip)
                        company.driveToPlayer = false
                        company.isDriving = false
                        if company.job == 'police' then
                            SetFakeWantedLevel(0)
                            if not company.isEscorted then WalkToVehicle() end
                        elseif company.job == 'ambulance' then
                            if QBCore.Functions.GetPlayerData().metadata['isdead'] or QBCore.Functions.GetPlayerData().metadata['inlaststand'] then
                                if not company.isEscorted then WalkToVehicle() end
                            else
                                HelpOnLocation()
                            end
                        elseif company.job == 'mechanic' then
                            if GetVehicleEngineHealth(company.damage_vehicle) < Config.MinDamageForFlatbed then
                                GoToMechanicShop()
                            else
                                HelpOnLocation()
                            end
                        elseif company.job == 'taxi' then
                            company.isWaiting = true
                        elseif company.job == 'limousine' then
                            company.isWaiting = true
                        end
                    end
                end
            elseif company.isReadyToGo then
                if not IsPedInVehicle(PlayerPedId(), company.vehicle, false) then TempDisableControl() end
                SetVehicleDoorsLocked(company.vehicle, 0)
                if IsPedInVehicle(company.driver, company.vehicle, false) and IsPedInVehicle(PlayerPedId(), company.vehicle, false) then
                    company.isReadyToGo = false
                    if company.job == 'taxi' or company.job == 'limousine' then GoToLocation() end
                    if company.job == 'mechanic' then
                        DriveToLocation(GetEntityCoords(company.vehicle), company.home)
                        company.driveToMechanic = true
                    end
                end
            elseif company.isEscorted then
                local distance = GetDistance(GetEntityCoords(company.driver), GetEntityCoords(company.vehicle))
                if company.job == "police" then
                    loadAnimDict("mp_arresting")
                    TaskPlayAnim(PlayerPedId(), "mp_arresting", "idle", 8.0, -8, -1, 16, 0, 0, 0, 0)
                end
                if distance <= 5 then
                    company.isEscorted = false
                    company.coords = company.home
                    DoScreenFadeOut(500)
                    Wait(700)
                    DetachEntity(PlayerPedId(), true, false)
                    TaskWarpPedIntoVehicle(PlayerPedId(), company.vehicle, company.passengerSeat)
                    TaskWarpPedIntoVehicle(company.driver, company.vehicle, -1)
                    Wait(1000)
                    DoScreenFadeIn(500)
                    company.driveToCompany = true
                    DriveToLocation(GetEntityCoords(company.vehicle), company.home)
                end
            elseif company.driveToCompany then
                local distance = GetDistance(GetEntityCoords(company.vehicle), company.home)
                lastCoords = GetEntityCoords(company.vehicle)
                if Config.ForceFirstperson then SetFollowVehicleCamViewMode(4) end
                if distance <= 5 then
                    company.driveToCompany = false
                    company.isDriving = false
                    BringVehicleToHalt(company.vehicle, 5.0, 1000, true)
                    Wait(1100)
                    ClearPedTasks(company.driver)
                    SetVehicleSiren(company.vehicle, false)
                    DoScreenFadeOut(500)
                    Wait(600)
                    if Config.ForceFirstperson then SetFollowVehicleCamViewMode(followPedCamMode) end
                    SetEntityCoords(PlayerPedId(), vector3(company.checkin.x, company.checkin.y, company.checkin.z), false, false, false, true)
                    if company.job == "police" then
                        if Config.UseAutoJail then
                            isInJail = true
                            jailTime = math.random(Config.MinJailTime, Config.MaxJailTime)
                        end
                        FreezeEntityPosition(PlayerPedId(), false)
                        company.isHandcuffed = false
                    end

                    if company.job == "ambulance" then FreezeEntityPosition(PlayerPedId(), false) end
                    Wait(1000)
                    DoScreenFadeIn(500)
                    Wait(500)
                    LeaveTarget()                    
                end
            elseif company.driveToMechanic then
                local distance = GetDistance(GetEntityCoords(company.vehicle), company.home)
                lastCoords = GetEntityCoords(company.vehicle)
                if Config.ForceFirstperson then SetFollowVehicleCamViewMode(4) end
                if distance <= 5 then
                    company.driveToMechanic = false
                    company.isDriving = false
                    BringVehicleToHalt(company.vehicle, 5.0, 1000, true)
                    ClearPedTasks(company.driver)
                    TaskLeaveVehicle(PlayerPedId(), company.vehicle, 1)
                    TriggerServerEvent('qb-npcservices:server:pay', company.job, company.price)
                    if Config.ForceFirstperson then SetFollowVehicleCamViewMode(followPedCamMode) end
                    Wait(3000)
                    DetachEntity(company.damage_vehicle, false, false)
                    Wait(100)
                    SetEntityCoords(company.damage_vehicle, vector3(company.vehicleDrop.x, company.vehicleDrop.y, company.vehicleDrop.z), false, false, false, true)
                    Wait(2000)
                    LeaveTarget()
                end
            elseif company.driveToLocation then
                local distance = GetDistance(GetEntityCoords(company.vehicle), company.coords)
                if Config.ForceFirstperson then SetFollowVehicleCamViewMode(4) end
                lastCoords = GetEntityCoords(company.vehicle)
                if distance <= 5 or not DoesBlipExist(GetFirstBlipInfoId(8)) then
                    company.driveToLocation = false
                    company.isDriving = false
                    BringVehicleToHalt(company.vehicle, 5.0, 1000, true)
                    Wait(1100)
                    ClearPedTasks(company.driver)
                    TriggerServerEvent('qb-npcservices:server:pay', company.job, company.price)
                    PlayAmbientSpeech1(company.driver, "TAXID_CLOSE_AS_POSS", "SPEECH_PARAMS_FORCE_NORMAL")
                    Wait(100)
                    if Config.ForceFirstperson then SetFollowVehicleCamViewMode(followPedCamMode) end
                    TriggerServerEvent('qb-npcservices:server:leavevehicle', company.vehicle)
                    Wait(3100)
                    LeaveTarget()
                end
            elseif company.driveAway then
                lastCoords = GetEntityCoords(company.vehicle)
                local distance = GetDistance(GetEntityCoords(company.vehicle), GetEntityCoords(PlayerPedId()))
                if distance > 100 then
                    company.driveAway = false
                    Reset()
                end
            end
        end
        Wait(100)
    end
end)

CreateThread(function()
    while true do
        if LocalPlayer.state.isLoggedIn then
            if Config.UseUnlimitHealth then
                if company.job ~= nil then
                    if company.vehicle ~= nil then
                        SetVehicleBodyHealth(company.vehicle, 1000.0)
                        SetVehicleEngineHealth(company.vehicle, 1000.0)
                        SetVehicleFuelLevel(company.vehicle, 100.0)
                        DecorSetFloat(company.vehicle, "_FUEL_LEVEL", GetVehicleFuelLevel(company.vehicle))
                    end
                    if company.driver ~= nil then SetEntityHealth(company.driver, 200) end
                    if company.codriver ~= nil then SetEntityHealth(company.codriver, 200) end
                end
            end
        end
        Wait(500)
    end
end)

CreateThread(function()
	while true do
        if LocalPlayer.state.isLoggedIn then
            if Config.AutoCallAmbulance and not company.sendNotify and not company.isCalled then
                if QBCore.Functions.GetPlayerData().metadata['inlaststand'] then
                    if not company.sendNotify then
                        local call_data = {}
                        call_data.job = 'ambulance'
                        call_data.callerId = GetPlayerServerId(PlayerId())
                        call_data.targetId = GetPlayerServerId(PlayerId())
                        call_data.price = Config.Service['ambulance'].price
                        Wait(Config.AutoCallTimer)
                        CallAnimation(call_data.job)
                        TriggerServerEvent('qb-npcservices:server:sendService', call_data)
                    end 
                end
            end
            CheckIfCompanyPedIsDeath()
        end
        Wait(1000)
    end
end)

CreateThread(function()
    while true do
        if LocalPlayer.state.isLoggedIn then
            if company.job ~= nil then
                if company.driveToCompany or company.driveToLocation or company.driveToMechanic or company.driveToPlayer or company.driveAway then
                    if company.driverIsInVehicle then VehicleStuckCheck() end
                end
            end
        end
        Wait(1000)
    end
end)

CreateThread(function()
	while true do
        local sleep = 1000
        if LocalPlayer.state.isLoggedIn then
            if isInJail and Config.UseAutoJail then
                jailTime = jailTime - 1
                if jailTime <= 0 then
                    isInJail = false
                    SetEntityCoords(PlayerPedId(), vector3(Config.Service['police'].checkout.x, Config.Service['police'].checkout.y, Config.Service['police'].checkout.z), false, false, false, true)
                    SetEntityHeading(PlayerPedId(), Config.Service['police'].checkout.w)
                    Reset()
                end
            elseif company.isWaiting then
                waitTime = waitTime - 1
                if waitTime <= 0 then LeaveTarget() end

            elseif company.cooldown then
                cooldownTime = cooldownTime - 1
                if cooldownTime <= 0 then company.cooldown = false end
            end
        end
        Wait(sleep)
    end
end)

CreateThread(function()
	while true do
        local sleep = 1000
        if LocalPlayer.state.isLoggedIn then
            if isInJail and Config.UseAutoJail then
                sleep = 5
                if jailTime > 0 then
                    GetEntityHealth(PlayerPedId(), 200.0)
                    DrawTxt(0.93, 1.44, 1.0, 1.0, 0.6, Lang:t('notify.jail_free_time', {freetime = math.ceil(jailTime)}), 255, 255, 255, 255)
                end
            elseif company.isWaiting then
                sleep = 5
                if waitTime > 0 then
                    DrawTxt(0.83, 1.44, 1.0, 1.0, 0.6, Lang:t('notify.press_e_to_enter',{waitTime = math.ceil(waitTime), job = company.job}), 255, 255, 255, 255)
                    if IsControlJustPressed(0, 38) then -- press E to get in the taxi
                        if not IsPedInVehicle(PlayerPedId(), company.vehicle, false) then
                            company.isWaiting = false
                            company.isReadyToGo = true
                            SetVehicleDoorsLocked(company.vehicle, 0)
                            TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(company.vehicle), 0)
                            TriggerServerEvent('qb-npcservices:server:getin', company.job, company.vehicle)
                            TaskEnterVehicle(company.driver, company.vehicle, -1, -1, 1.0, 1, 0)
                        end
                    end
                elseif company.cooldown then
                    sleep = 5
                    if waitTime > 0 then
                        DrawTxt(0.93, 1.44, 1.0, 1.0, 0.6, Lang:t('notify.cooldown',{cooldownTime = math.ceil(cooldownTime), job = company.job}), 255, 255, 255, 255)
                    end
                end
            end
        end
        Wait(sleep)
    end
end)

-- Debug Stuff
CreateThread(function()
    local count = 0
    while true do
        count = count + 1
        if LocalPlayer.state.isLoggedIn then
            if Config.Debug and company.job ~= nil then
                if company.driveToCompany or company.driveToLocation or company.driveToMechanic or company.driveToPlayer or company.driveAway or company.isEscorted or isInJail or not isInJail then
                    -- print("-----------"..count.."-----------")
                    -- print("Driver "..company.job.." Debugger")
                    -- print("Driver in Vehicle: "..tostring(company.driverIsInVehicle))
                    -- print("Driver Is Driving: "..tostring(company.isDriving))
                    -- print("Driver is Escorted: "..tostring(company.isEscorted))
                    -- print("Drive to Company: "..tostring(company.driveToCompany))
                    -- print("Drive to Location: "..tostring(company.driveToLocation))
                    -- print("Drive to Mechanic: "..tostring(company.driveToMechanic))
                    -- print("Drive to Player: "..tostring(company.driveToPlayer))
                    -- print("Drive Away: "..tostring(company.driveAway))
                    -- print("Player is in Jail: "..tostring(isInJail))
                else
                    count = 0
                end
            end
        end
        Wait(2000)
    end
end)

RegisterNetEvent('qb-npcservices:client:getinvehicle', function(vehicle)
    for i = 1, 7 do
        if IsVehicleSeatFree(vehicle, i) then
            TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), 0)
            SetVehicleDoorsLocked(vehicle, 0)
            TaskEnterVehicle(PlayerPedId(), vehicle, -1, i, 1.0, 1, 0)
        end
    end
end)

RegisterNetEvent('qb-npcservices:client:leavevehicle', function()
    local vehicle = GetVehiclePedIsIn(PlayerPedId())
    if IsPedInAnyVehicle(PlayerPedId(), false) then
        SetVehicleDoorsLocked(vehicle, 0)
        TaskLeaveVehicle(PlayerPedId(), vehicle, 1)
    end
end)

-- for taxi and limousine
if Config.UseTarget then
    RegisterNetEvent('qb-npcservices:client:taxi', function(vehicle)
        local vehicle, distance = QBCore.Functions.GetClosestVehicle(GetEntityCoords(PlayerPedId()))
        TriggerServerEvent('qb-npcservices:server:getin', 'taxi', vehicle)
    end)
    RegisterNetEvent('qb-npcservices:client:limousine', function(vehicle)
        local vehicle, distance = QBCore.Functions.GetClosestVehicle(GetEntityCoords(PlayerPedId()))
        TriggerServerEvent('qb-npcservices:server:getin', 'limousine', vehicle)
    end)
    for _, model in pairs(Config.Vehicles['taxi']) do
        exports['qb-target']:AddTargetModel(model, {
            options = {
                {
                    type = "client",
                    event = "qb-npcservices:client:taxi",
                    icon = "fas fa-car",
                    label = Lang:t('target.get_in'),
                },
            },
            distance = 2.5
        })
    end
    for _, model in pairs(Config.Vehicles['limousine']) do
        exports['qb-target']:AddTargetModel(model, {
            options = {
                {
                    type = "client",
                    event = "qb-npcservices:client:limousine",
                    icon = "fas fa-car",
                    label = Lang:t('target.get_in'),
                },
            },
            distance = 2.5
        })
    end
end