if (chunkProcessorG) then
    return chunkProcessorG
end
local chunkProcessor = {}

-- imports

local chunkUtils = require("ChunkUtils")
local mathUtils = require("MathUtils")
local constants = require("Constants")

-- constants

local CHUNK_SIZE = constants.CHUNK_SIZE

local MAX_TICKS_BEFORE_SORT_CHUNKS = constants.MAX_TICKS_BEFORE_SORT_CHUNKS

-- imported functions

local mapScanEnemyChunk = chunkUtils.mapScanEnemyChunk
local mapScanPlayerChunk = chunkUtils.mapScanPlayerChunk
local mapScanResourceChunk = chunkUtils.mapScanResourceChunk

local createChunk = chunkUtils.createChunk
local initialScan = chunkUtils.initialScan
local chunkPassScan = chunkUtils.chunkPassScan

local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed

local tSort = table.sort

local abs = math.abs
local next = next
local table_size = table_size

local tRemove = table.remove

-- module code

local origin = {x=0,y=0}

local function sorter(a, b)
    local aDistance = euclideanDistanceNamed(a, origin)
    local bDistance = euclideanDistanceNamed(b, origin)

    if (aDistance == bDistance) then
        if (a.x == b.x) then
            return (abs(a.y) < abs(b.y))
        else
            return (abs(a.x) < abs(b.x))
        end
    end

    return (aDistance < bDistance)
end

function chunkProcessor.processPendingChunks(map, tick, flush)
    local processQueue = map.processQueue
    local pendingChunks = map.pendingChunks

    local area = map.universe.area

    local topOffset = area[1]
    local bottomOffset = area[2]

    local event = next(pendingChunks, map.chunkProcessorIterator)
    local endCount = 2
    if flush then
        endCount = table_size(pendingChunks)
    end
    for _=1,endCount do
        if not event then
            map.chunkProcessorIterator = nil
            if (table_size(pendingChunks) == 0) then
                -- this is needed as the next command remembers the max length a table has been
                map.pendingChunks = {}
            end
            break
        else
            local topLeft = event.area.left_top
            local x = topLeft.x
            local y = topLeft.y

            topOffset[1] = x
            topOffset[2] = y
            bottomOffset[1] = x + CHUNK_SIZE
            bottomOffset[2] = y + CHUNK_SIZE

            if map[x] and map[x][y] then
                local chunk = map[x][y]
                mapScanPlayerChunk(chunk, map)
                mapScanEnemyChunk(chunk, map)
                mapScanResourceChunk(chunk, map)
            else
                if map[x] == nil then
                    map[x] = {}
                end

                local chunk = createChunk(x, y)

                chunk = initialScan(chunk, map, tick)

                if (chunk ~= -1) then
                    map[x][y] = chunk
                    processQueue[#processQueue+1] = chunk
                end
            end
            local newEvent,_ = next(pendingChunks, event)
            pendingChunks[event] = nil
            event = newEvent
        end

    end
    map.chunkProcessorIterator = event

    if (#processQueue > map.nextChunkSort) or
        (((tick - map.nextChunkSortTick) > MAX_TICKS_BEFORE_SORT_CHUNKS) and
                ((map.nextChunkSort - 150) < #processQueue))
    then
        map.nextChunkSort = #processQueue + 150
        map.nextChunkSortTick = tick
        tSort(processQueue, sorter)
    end
end

function chunkProcessor.processScanChunks(map)
    local area = map.universe.area

    local topOffset = area[1]
    local bottomOffset = area[2]

    local removals = map.chunkRemovals

    local chunkCount = 0

    local chunkToPassScan = map.chunkToPassScan

    for chunk,_ in pairs(chunkToPassScan) do
        local x = chunk.x
        local y = chunk.y

        topOffset[1] = x
        topOffset[2] = y
        bottomOffset[1] = x + CHUNK_SIZE
        bottomOffset[2] = y + CHUNK_SIZE

        chunk = chunkPassScan(chunk, map)

        if (chunk == -1) then
            map[x][y] = nil

            chunkCount = chunkCount + 1
            removals[chunkCount] = chunk
        end

        chunkToPassScan[chunk] = nil
    end

    if (chunkCount > 0) then
        local processQueue = map.processQueue
        for i=#processQueue,1,-1 do
            for ri=chunkCount,1,-1 do
                if (removals[ri] == processQueue[i]) then
                    tRemove(processQueue, i)
                    -- tRemove(removals, ri)
                    break
                end
            end
        end
    end
end

chunkProcessorG = chunkProcessor
return chunkProcessor
