local function arrayToSet(array)
    local set = {}
    for _, value in ipairs(array) do set[value] = true end
    return set
end

local function log(text) print(text) end

local evDoubleChance = 10
local evChances = {
    ["goop"] = 30,
    ["fire"] = 10,
    ["spikeball"] = 20,
    ["laser"] = 10,
    ["bigpapa"] = 10,
    ["speed"] = 20
}
local weightSum = 0
for k, v in pairs(evChances) do weightSum = weightSum + v end

local evAllowedEnemies = arrayToSet{
    MT_BLUECRAWLA, MT_REDCRAWLA, MT_GFZFISH, MT_GOLDBUZZ, MT_REDBUZZ,
    MT_JETTBOMBER, MT_JETTGUNNER, MT_CRAWLACOMMANDER, MT_DETON, MT_SKIM,
    MT_TURRET, MT_POPUPTURRET, MT_SPINCUSHION, MT_CRUSHSTACEAN, MT_BANPYURA,
    MT_JETJAW, MT_SNAILER, MT_VULTURE, MT_POINTY, MT_ROBOHOOD, MT_FACESTABBER,
    MT_EGGGUARD, MT_GSNAPPER, MT_MINUS, MT_SPRINGSHELL, MT_YELLOWSHELL,
    MT_UNIDUS, MT_CANARIVORE, MT_PYREFLY, MT_PTERABYTE, MT_DRAGONBOMBER,
	MT_BUMBLEBORE, MT_PENGUINATOR
}

local evTypes = {
    ["goop"] = {
        color = SKINCOLOR_NEON,
        mobjSpawn = function(mo)
            mo.ev_goop_timer = leveltime
            mo.ev_goop_mobjs = {}
        end,
        mobjThink = function(mo)
			if mo.type ~= MT_DETON then
				P_Move(mo, 5)
			end
			
            if leveltime - mo.ev_goop_timer > TICRATE * 1 then
                local goopmo = P_SpawnMobjFromMobj(mo, 0, 0, FRACUNIT * 10,
                                                   MT_GOOP)
                table.insert(mo.ev_goop_mobjs, 0, goopmo)

                if #mo.ev_goop_mobjs > 3 then
                    local goopToRemove = table.remove(mo.ev_goop_mobjs)
                    if goopToRemove.valid then
                        P_RemoveMobj(goopToRemove)
                    end
                end

                mo.ev_goop_timer = leveltime
            end
        end,
        mobjRemoved = function(mo) end
    },
    ["fire"] = {
        color = SKINCOLOR_FLAME,
        mobjSpawn = function(mo)
            mo.ev_fire_mobj = P_SpawnMobjFromMobj(mo, 0, 0, FRACUNIT * 64,
                                                  MT_FJSPINAXISA)
        end,
        mobjThink = function(mo)
            P_TeleportMove(mo.ev_fire_mobj, mo.x, mo.y, mo.z + FRACUNIT * 64)
        end,
        mobjRemoved = function(mo) P_RemoveMobj(mo.ev_fire_mobj) end
    },
    ["spikeball"] = {
        color = SKINCOLOR_CARBON,
        mobjSpawn = function(mo)
            mo.ev_spikeball_mobj = P_SpawnMobjFromMobj(mo, 0, 0, FRACUNIT * 64,
                                                       MT_SPIKEBALL)
        end,
        mobjThink = function(mo)
            P_TeleportMove(mo.ev_spikeball_mobj, mo.x, mo.y,
                           mo.z + FRACUNIT * 64)
        end,
        mobjRemoved = function(mo) P_RemoveMobj(mo.ev_spikeball_mobj) end
    },
    ["laser"] = {
        color = SKINCOLOR_RED,
        mobjSpawn = function(mo) mo.ev_laser_timer = leveltime end,
        mobjThink = function(mo)
            if leveltime - mo.ev_laser_timer > TICRATE * 2 and mo.target then
                local missile = P_SpawnXYZMissile(mo, mo.target, MT_ROCKET,
                                                  mo.x, mo.y,
                                                  mo.z + FRACUNIT * 64)
                mo.ev_laser_timer = leveltime
            end
        end,
        mobjRemoved = function(mo) end
    },
    ["bigpapa"] = {
        color = SKINCOLOR_GOLD,
        mobjSpawn = function(mo) mo.scale = FRACUNIT * 2 end,
        mobjThink = function(mo) 
		end,
        mobjRemoved = function(mo)			
            local child = P_SpawnMobjFromMobj(mo, FRACUNIT * 64, 0,
                                              FRACUNIT * 64, mo.type)
            child.ev_type = "papachild"
            child = P_SpawnMobjFromMobj(mo, -FRACUNIT * 64, 0, FRACUNIT * 64,
                                        mo.type)
            child.ev_type = "papachild"
            child = P_SpawnMobjFromMobj(mo, 0, FRACUNIT * 64, FRACUNIT * 64,
                                        mo.type)
            child.ev_type = "papachild"
            child = P_SpawnMobjFromMobj(mo, 0, -FRACUNIT * 64, FRACUNIT * 64,
                                        mo.type)
            child.ev_type = "papachild"
        end
    },
    ["papachild"] = {
        color = SKINCOLOR_GOLD,
        mobjSpawn = function(mo) mo.scale = FRACUNIT / 2 end,
        mobjThink = function(mo) end,
        mobjRemoved = function(mo) end
    },
    ["speed"] = {
        color = SKINCOLOR_BLUE,
        mobjSpawn = function(mo) end,
        mobjThink = function(mo)
			if mo.type ~= MT_DETON then
				P_Move(mo, 12)
			end
		end,
        mobjRemoved = function(mo) end
    }
}

addHook("MobjSpawn", function(mo)
    if not evAllowedEnemies[mo.type] then return end

    -- Weighted random selection
    local randomWeight = P_RandomRange(1, weightSum)
    for k, v in pairs(evChances) do
        randomWeight = randomWeight - v
        if randomWeight <= 0 then
            mo.ev_type = k
            mo.ev_executed_spawn = false -- Delay spawn logic a little
            break
        end
    end

    -- Chance for combined ev_type
    if P_RandomRange(1, evDoubleChance) == 1 then
        local copyOfChances = {}
        for k, v in pairs(evChances) do
            if k ~= mo.ev_type then copyOfChances[k] = v end
        end

        randomWeight = P_RandomRange(1, weightSum)
        for k, v in pairs(copyOfChances) do
            randomWeight = randomWeight - v
            if randomWeight <= 0 then
                mo.ev_type2 = k
                break
            end
        end
    end
end)

addHook("MobjThinker", function(mo)
    if not evAllowedEnemies[mo.type] or not mo.valid then return end

    if mo.ev_type then
        if not mo.ev_executed_spawn then
            mo.color = evTypes[mo.ev_type].color
            evTypes[mo.ev_type].mobjSpawn(mo)

            if mo.ev_type2 then
                -- Two variations = white
                mo.color = SKINCOLOR_BONE
                evTypes[mo.ev_type2].mobjSpawn(mo)
            end

            mo.ev_executed_spawn = true
        end
        evTypes[mo.ev_type].mobjThink(mo)
        if mo.ev_type2 then evTypes[mo.ev_type2].mobjThink(mo) end
    end
end)

addHook("MobjRemoved", function(mo)
    if not evAllowedEnemies[mo.type] then return end

    if mo.ev_type then
        evTypes[mo.ev_type].mobjRemoved(mo)
        if mo.ev_type2 then evTypes[mo.ev_type2].mobjRemoved(mo) end
    end
end)
