local QBCore = exports["qb-core"]:GetCoreObject()

local Action = {
    name = "",
    duration = 0,
    label = "",
    useWhileDead = false,
    canCancel = true,
	disarm = true,
    controlDisables = {
        disableMovement = false,
        disableCarMovement = false,
        disableMouse = false,
        disableCombat = false,
    },
    animation = {
        animDict = nil,
        anim = nil,
        flags = 0,
        task = nil,
    },
    prop = {
        model = nil,
        bone = nil,
        coords = { x = 0.0, y = 0.0, z = 0.0 },
        rotation = { x = 0.0, y = 0.0, z = 0.0 },
    },
    propTwo = {
        model = nil,
        bone = nil,
        coords = { x = 0.0, y = 0.0, z = 0.0 },
        rotation = { x = 0.0, y = 0.0, z = 0.0 },
    },
}

local isDoingAction = false
local wasCancelled = false
local isAnim = false
local isProp = false
local isPropTwo = false
local prop_net = nil
local propTwo_net = nil
local runProgThread = false

RegisterNetEvent('progressbar:client:ToggleBusyness', function(bool)
    isDoingAction = bool
end)

function ActionCleanup()
    local ped = PlayerPedId()

    if Action.animation then
        if Action.animation.task or (Action.animation.animDict and Action.animation.anim) then
            ClearPedSecondaryTask(ped)
            StopAnimTask(ped, Action.animDict, Action.anim, 1.0)
        end
    end
    if prop_net then
        DetachEntity(NetToObj(prop_net), 1, 1)
        DeleteEntity(NetToObj(prop_net))
        prop_net = nil
    end

    if propTwo_net then
        DetachEntity(NetToObj(propTwo_net), 1, 1)
        DeleteEntity(NetToObj(propTwo_net))
        propTwo_net = nil
    end

    runProgThread = false
end

local function Cancel()
    TriggerEvent('progressbar:setstatus', false)
    isDoingAction = false
    wasCancelled = true

    LocalPlayer.state:set("inv_busy", false, true) -- Not Busy
    ActionCleanup()

    SendNUIMessage({
        action = "cancel"
    })
end

local function Finish()
    TriggerEvent('progressbar:setstatus', false)
    isDoingAction = false
    ActionCleanup()
    LocalPlayer.state:set("inv_busy", false, true) -- Not Busy
end

local function DisableActions()
    if Action.controlDisables.disableMouse then
        DisableControlAction(0, 1, true) -- LookLeftRight
        DisableControlAction(0, 2, true) -- LookUpDown
        DisableControlAction(0, 106, true) -- VehicleMouseControlOverride
    end

    if Action.controlDisables.disableMovement then
        DisableControlAction(0, 30, true) -- disable left/right
        DisableControlAction(0, 31, true) -- disable forward/back
        DisableControlAction(0, 36, true) -- INPUT_DUCK
        DisableControlAction(0, 21, true) -- disable sprint
    end

    if Action.controlDisables.disableCarMovement then
        DisableControlAction(0, 63, true) -- veh turn left
        DisableControlAction(0, 64, true) -- veh turn right
        DisableControlAction(0, 71, true) -- veh forward
        DisableControlAction(0, 72, true) -- veh backwards
        DisableControlAction(0, 75, true) -- disable exit vehicle
    end

    if Action.controlDisables.disableCombat then
        DisablePlayerFiring(PlayerId(), true) -- Disable weapon firing
        DisableControlAction(0, 24, true) -- disable attack
        DisableControlAction(0, 25, true) -- disable aim
        DisableControlAction(1, 37, true) -- disable weapon select
        DisableControlAction(0, 47, true) -- disable weapon
        DisableControlAction(0, 58, true) -- disable weapon
        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, 263, true) -- disable melee
        DisableControlAction(0, 264, true) -- disable melee
        DisableControlAction(0, 257, true) -- disable melee
    end
end

local function loadAnimDict(dict)
    if not HasAnimDictLoaded(dict) then
        while not HasAnimDictLoaded(dict) do
            RequestAnimDict(dict)
            Wait(25)
        end
    end
end

local function loadModel(model)
    if not HasModelLoaded(model) then
        while not HasModelLoaded(model) do
            RequestModel(model)
            Wait(25)
        end
    end
end

local function ActionStart()
    runProgThread = true
    LocalPlayer.state:set("inv_busy", true, true) -- Busy

    CreateThread(function()
        while runProgThread do
            if isDoingAction then
                if not isAnim then
                    if Action.animation then
                        if Action.animation.task then
                            TaskStartScenarioInPlace(PlayerPedId(), Action.animation.task, 0, true)
                        elseif Action.animation.animDict and Action.animation.anim then
                            if Action.animation.flags == nil then
                                Action.animation.flags = 1
                            end

                            local player = PlayerPedId()
                            if (DoesEntityExist(player) and not IsEntityDead(player)) then
                                loadAnimDict( Action.animation.animDict)
                                TaskPlayAnim(player, Action.animation.animDict, Action.animation.anim, 3.0, 3.0, -1, Action.animation.flags, 0, 0, 0, 0 )
                            end
                        end
                    end

                    isAnim = true
                end
                if not isProp and Action.prop and Action.prop.model then
                    loadModel(joaat(Action.prop.model))

                    local pCoords = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, 0.0, 0.0)
                    local modelSpawn = CreateObject(joaat(Action.prop.model), pCoords.x, pCoords.y, pCoords.z, true, true, true)

                    local netid = ObjToNet(modelSpawn)
                    SetNetworkIdExistsOnAllMachines(netid, true)
                    NetworkSetNetworkIdDynamic(netid, true)
                    SetNetworkIdCanMigrate(netid, false)
                    if Action.prop.bone == nil then
                        Action.prop.bone = 60309
                    end

                    if Action.prop.coords == nil then
                        Action.prop.coords = { x = 0.0, y = 0.0, z = 0.0 }
                    end

                    if Action.prop.rotation == nil then
                        Action.prop.rotation = { x = 0.0, y = 0.0, z = 0.0 }
                    end

                    AttachEntityToEntity(modelSpawn, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), Action.prop.bone), Action.prop.coords.x, Action.prop.coords.y, Action.prop.coords.z, Action.prop.rotation.x, Action.prop.rotation.y, Action.prop.rotation.z, 1, 1, 0, 1, 0, 1)
                    prop_net = netid

                    isProp = true

                    if not isPropTwo and Action.propTwo and Action.propTwo.model then
                        loadModel(joaat(Action.propTwo.model))

                        local pCoords2 = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, 0.0, 0.0)
                        local modelSpawn2 = CreateObject(joaat(Action.propTwo.model), pCoords2.x, pCoords2.y, pCoords2.z, true, true, true)

                        local netid2 = ObjToNet(modelSpawn2)
                        SetNetworkIdExistsOnAllMachines(netid2, true)
                        NetworkSetNetworkIdDynamic(netid2, true)
                        SetNetworkIdCanMigrate(netid2, false)
                        if Action.propTwo.bone == nil then
                            Action.propTwo.bone = 60309
                        end

                        if Action.propTwo.coords == nil then
                            Action.propTwo.coords = { x = 0.0, y = 0.0, z = 0.0 }
                        end

                        if Action.propTwo.rotation == nil then
                            Action.propTwo.rotation = { x = 0.0, y = 0.0, z = 0.0 }
                        end

                        AttachEntityToEntity(modelSpawn2, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), Action.propTwo.bone), Action.propTwo.coords.x, Action.propTwo.coords.y, Action.propTwo.coords.z, Action.propTwo.rotation.x, Action.propTwo.rotation.y, Action.propTwo.rotation.z, 1, 1, 0, 1, 0, 1)
                        propTwo_net = netid2

                        isPropTwo = true
                    end
                end

                DisableActions(PlayerPedId())
            end
            Wait(0)
        end
    end)
end

local function Process(action, start, tick, finish)
	ActionStart()
    Action = action
	if Action.icon then
		if QBCore.Shared.Items[tostring(Action.icon)] then
			local img = "nui://ps-inventory/html/" -- default qb-core inventory link
			if not string.find(QBCore.Shared.Items[tostring(Action.icon)].image, "http") then -- 👀 Slipped in support for custom html links too
				if not string.find(QBCore.Shared.Items[tostring(Action.icon)].image, "images/") then --search for if the icon images have /images in the listing
					img = img.."images/"
				end
				Action.icon = img..QBCore.Shared.Items[tostring(Action.icon)].image
			end
		end
	end
    if not IsEntityDead(PlayerPedId()) or Action.useWhileDead then
        if not isDoingAction then
            isDoingAction = true
            wasCancelled = false
            isAnim = false
            isProp = false
            TriggerEvent('progressbar:setstatus', true)
            SendNUIMessage({
                action = "progress",
                duration = Action.duration,
                label = Action.label,
                icon = Action.icon
            })
            CreateThread(function ()
                if start then
                    start()
                end
                while isDoingAction do
                    Wait(1)
                    if tick then
                        tick()
                    end
                    if IsControlJustPressed(0, 177) and Action.canCancel then -- IsControlJustPressed(0, 177) BACKSPACE
                        TriggerEvent("progressbar:client:cancel")
                    end

                    if IsEntityDead(PlayerPedId()) and not Action.useWhileDead then
                        TriggerEvent("progressbar:client:cancel")
                    end
                end
                if finish then
                    finish(wasCancelled)
                end
            end)
        else
            TriggerEvent("QBCore:Notify", "Du er allerede igang med noget!", "error")
        end
    else
        TriggerEvent("QBCore:Notify", "Det kan du ikke!", "error")
    end
end


RegisterNetEvent("progressbar:client:progress", function(action, finish)
	Process(action, nil, nil, finish)
end)

RegisterNetEvent("progressbar:client:ProgressWithStartEvent", function(action, start, finish)
	Process(action, start, nil, finish)
end)

RegisterNetEvent("progressbar:client:ProgressWithTickEvent", function(action, tick, finish)
	Process(action, nil, tick, finish)
end)

RegisterNetEvent("progressbar:client:ProgressWithStartAndTick", function(action, start, tick, finish)
	Process(action, start, tick, finish)
end)

RegisterNetEvent("progressbar:client:cancel", function()
	Cancel()
end)

RegisterNUICallback('FinishAction', function(_, cb)
	Finish()
    cb("ok")
end)


local function Progress(action, finish)
	Process(action, nil, nil, finish)
end exports("Progress", Progress)

local function ProgressWithStartEvent(action, start, finish)
	Process(action, start, nil, finish)
end exports("ProgressWithStartEvent", ProgressWithStartEvent)

local function ProgressWithTickEvent(action, tick, finish)
	Process(action, nil, tick, finish)
end exports("ProgressWithTickEvent", ProgressWithTickEvent)

local function ProgressWithStartAndTick(action, start, tick, finish)
	Process(action, start, tick, finish)
end exports("ProgressWithStartAndTick", ProgressWithStartAndTick)