-- Explorer by glitchdetector, Jun. 2020
local Regions = {}

local UncoverPercentage = 0
local UncoverProgress = 0
local UncoverTarget = 0

local BlipState = {}

exports("progress", function()
    return {UncoverPercentage, UncoverProgress, UncoverTarget}
end)

CreateThread(function()
    -- Individual cell sizes
    local CellWidth = MapWidth / MapCellsWidth
    local CellHeight = MapHeight / MapCellsHeight

    -- if Debug then
        -- hide the entire map, might need script restart to apply
        RegisterCommand("explorer_reset", function()
            print("explorer", "starting reset")
            for xx = 0, MapCellsWidth - 1 do
                for yy = 0, MapCellsHeight - 1 do
                    if Persist then DeleteResourceKvp(("%s_%s_%s"):format(ServerCode, xx, yy)) end
                    Regions[xx][yy][2] = true
                end
                Wait(0)
            end
            print("explorer", "reset complete")
        end)
        -- uncover the entire map, might need script restart to apply
        -- takes a few seconds, game will lag badly or even freeze
        -- RegisterCommand("explorer_fill", function()
        --     print("explorer", "starting fill", "client may lag or freeze")
        --     for xx = 0, MapCellsWidth - 1 do
        --         for yy = 0, MapCellsHeight - 1 do
        --             if Persist then SetResourceKvpInt(("%s_%s_%s"):format(ServerCode, xx, yy), 1) end
        --             Regions[xx][yy][2] = false
        --         end
        --         Wait(0)
        --     end
        --     print("explorer", "fill complete")
        -- end)
    -- end

    -- create the regions
    for xx = 0, MapCellsWidth - 1 do
        for yy = 0, MapCellsHeight - 1 do
            if Persist then
                -- load discovered regions from client storage
                if GetResourceKvpInt(("%s_%s_%s"):format(ServerCode, xx, yy)) == 1 then
                    if not Regions[xx] then Regions[xx] = {} end
                    Regions[xx][yy] = {nil, false}
                    if Debug then
                        -- Add a blip for testing and debugging i guess
                        local blip = AddBlipForArea(MapOffsetX + CellWidth * xx, MapOffsetY + CellHeight * yy, 0.0, CellWidth, CellHeight)
                        SetBlipRotation(blip, 0)
                        SetBlipSprite(blip, 0)
                        SetBlipColour(blip, 0xFF000055)
                        Regions[xx][yy][1] = blip
                    end
                end
            end
            -- create undiscovered region if one was not loaded
            if not Regions[xx] then Regions[xx] = {} end
            if not Regions[xx][yy] then
                Regions[xx][yy] = {nil, true}
            end
        end
    end
    -- blip update logic
    CreateThread(function()
        while true do
            -- Iterate all blip types that are affected
            for _, blipSprite in next, BlipTypes do
                local blip = GetFirstBlipInfoId(blipSprite)
                local c = 1
                while DoesBlipExist(blip) do
                    -- Calculate the cell the blip occupies
                    local pos = GetBlipInfoIdCoord(blip)
                    local cellX = math.floor(((pos.x - MapOffsetX) / CellWidth) + 0.5)
                    local cellY = math.floor(((pos.y - MapOffsetY) / CellHeight) + 0.5)
                    if Regions[cellX] and Regions[cellX][cellY] then
                        local currentBlip = blip
                        if Regions[cellX][cellY][2] then
                            -- Hide the blip if it wasn't already hidden
                            if BlipState[blip] ~= true then
                                BlipState[blip] = true
                                SetBlipAlpha(currentBlip, 0)
                            end
                        else
                            -- Show the blip if it's hidden
                            if BlipState[blip] ~= false then
                                BlipState[blip] = false
                                -- Fade the blip in over the course of 255 frames (lazy)
                                CreateThread(function()
                                    local alpha = GetBlipAlpha(currentBlip)
                                    if alpha < 20 then
                                        if FlashDiscovered then SetBlipFlashes(currentBlip, true) end
                                        while alpha < 255 do
                                            alpha = alpha + 1
                                            SetBlipAlpha(currentBlip, alpha)
                                            Wait(0)
                                        end
                                        if FlashDiscovered then SetBlipFlashes(currentBlip, false) end
                                    end
                                end)
                            end
                        end
                    end
                    blip = GetNextBlipInfoId(blipSprite)
                    -- Yield frequently, the script doesn't need to do all actions at once anyways
                    c = c + 1
                    if c % 10 then
                        Wait(0)
                    end
                end
                Wait(0)
            end

            -- Calculate discovery percentage and update region states
            local total = 0
            local uncovered = 0
            local updated = 0
            for xx = 0, MapCellsWidth - 1 do
                for yy = 0, MapCellsHeight - 1 do
                    -- if Regions[xx][yy][4] ~= Regions[xx][yy][3] then
                        -- Regions[xx][yy][3] = Regions[xx][yy][4]
                        updated = true
                    -- end
                    total = total + 1
                    if not Regions[xx][yy][2] then
                        uncovered = uncovered + 1
                    end
                end
            end
            if updated then
                local percentage = (uncovered / total) * 100
                UncoverPercentage = percentage
                UncoverProgress = uncovered
                UncoverTarget = total
            end
        end
    end)
    -- player update loop
    CreateThread(function()
        -- Uncovers a cell, safely spammed since it doesn't update already uncovered cells
        local function UncoverCell(x, y)
            if Regions[x] and Regions[x][y] then
                local region = Regions[x][y]
                if region[2] then
                    -- RemoveBlip(region[1])
                    if Debug then
                        local blip = AddBlipForArea(MapOffsetX + CellWidth * x, MapOffsetY + CellHeight * y, 0.0, CellWidth, CellHeight)
                        SetBlipRotation(blip, 0)
                        SetBlipSprite(blip, 0)
                        SetBlipColour(blip, 0xFF000055)
                        region[1] = blip
                    end
                    region[2] = false
                    if Persist then SetResourceKvpInt(("%s_%s_%s"):format(ServerCode, x, y), 1) end
                end
            end
        end
        -- current player cell logic
        while true do
            local pos = GetEntityCoords(PlayerPedId())
            local cellX = math.floor(((pos.x - MapOffsetX) / CellWidth) + 0.5)
            local cellY = math.floor(((pos.y - MapOffsetY) / CellHeight) + 0.5)
            -- update cells around the player based on offsets in the config
            for _, offsets in next, CoverArea do
                UncoverCell(cellX + offsets[1], cellY + offsets[2])
            end
            Wait(1000)
        end
    end)
end)