-- Rewards Module
-- Made by: skudd3r for ComfyPlay
-- This module sets the rewards based on the killscore(score module)

local Event = require 'utils.event'
local Token = require 'utils.token'
local Task = require 'utils.task'
local Score = require 'utils.gui.score'
local floor = math.floor
local sqrt = math.sqrt
local insert = table.insert

local rewards_loot = {
    [1] = { { name = 'submachine-gun', count = 1, text = ' Submachine Gun' }, { name = 'firearm-magazine', count = 50, text = ' SMG Rounds' } },
    [2] = { { name = 'heavy-armor', count = 1, text = ' Heavy Armor' }, { name = 'defender-capsule', count = 20, text = ' Defender-Bots' } },
    [3] = { { name = 'firearm-magazine', count = 50, text = ' SMG Rounds' }, { name = 'grenade', count = 10, text = ' Grenades' } },
    [4] = { { name = 'land-mine', count = 20, text = ' Landmines' }, { name = 'firearm-magazine', count = 100, text = ' SMG Rounds' } },
    [5] = { { name = 'slowdown-capsule', count = 20, text = ' Slowdown Capsules' }, { name = 'poison-capsule', count = 20, text = ' Poison Capsules' } },
    [6] = { { name = 'land-mine', count = 30, text = ' Landmines' }, { name = 'raw-fish', count = 30, text = ' Fish Food' } },
    [7] = { { name = 'piercing-rounds-magazine', count = 50, text = ' SMG AP Rounds' }, { name = 'distractor-capsule', count = 20, text = ' Distractor Capsules' } },
    [8] = { { name = 'combat-shotgun', count = 1, text = ' Combat Shotgun' }, { name = 'piercing-shotgun-shell', count = 50, text = ' AP Shotgun Shells' } },
    [9] = { { name = 'poison-capsule', count = 40, text = ' Poison Capsule' }, { name = 'piercing-rounds-magazine', count = 100, text = ' SMG AP Rounds' } },
    [10] = { { name = 'computer', count = 1, text = ' Teleporter Computer' }, { name = 'modular-armor', count = 1, text = ' Modular Armor' } },
    [11] = {
        { name = 'solar-panel-equipment',  count = 2, text = ' Portable Solar Panel' },
        { name = 'battery-equipment',      count = 1, text = ' MK1 Battery' },
        { name = 'night-vision-equipment', count = 1, text = ' Night Vision Goggles' }
    },
    [12] = { { name = 'cluster-grenade', count = 20, text = ' Cluster Grenades' }, { name = 'piercing-shotgun-shell', count = 100, text = ' AP Shotgun Shells' } },
    [13] = { { name = 'flamethrower', count = 1, text = ' Flamethrower' }, { name = 'flamethrower-ammo', count = 50, text = ' Flamethrower Rounds' } },
    [14] = { { name = 'slowdown-capsule', count = 30, text = ' Slowdown Capsule' }, { name = 'piercing-rounds-magazine', count = 200, text = ' SMG Rounds' } },
    [15] = { { name = 'battery-equipment', count = 2, text = ' MK1 Battery' }, { name = 'solar-panel-equipment', count = 4, text = ' Portable Solar Panel' } },
    [16] = {
        { name = 'energy-shield-equipment', count = 1,  text = ' Energy Shield MK1' },
        { name = 'cluster-grenade',         count = 20, text = ' Cluster Grenades' },
        { name = 'flamethrower-ammo',       count = 50, text = ' Flamethrower Rounds' }
    },
    [17] = {
        { name = 'energy-shield-equipment',  count = 1,   text = ' Energy Shield MK1' },
        { name = 'land-mine',                count = 50,  text = ' Landmines' },
        { name = 'piercing-rounds-magazine', count = 200, text = ' SMG Rounds' }
    },
    [18] = { { name = 'exoskeleton-equipment', count = 1, text = ' Exoskelet' }, { name = 'raw-fish', count = 50, text = ' Fish Food' } },
    [19] = {
        { name = 'battery-mk2-equipment', count = 1,  text = ' Armor Battery Mk2' },
        { name = 'rocket-launcher',       count = 1,  text = ' Rocket Launcher' },
        { name = 'rocket',                count = 10, text = ' Rockets' }
    },
    [20] = { { name = 'power-armor', count = 1, text = ' Power Armor MK1' }, { name = 'computer', count = 1, text = ' Teleporter Computer' } },
    [21] = { { name = 'personal-roboport-equipment', count = 1, text = ' Armor Roboport MK1' }, { name = 'construction-robot', count = 10, text = ' Construction-Bots' } },
    [22] = { { name = 'personal-laser-defense-equipment', count = 1, text = ' Personal Laser Defense' }, { name = 'flamethrower-ammo', count = 100, text = ' Flamethrower Rounds' } },
    [23] = { { name = 'rocket', count = 40, text = ' Rockets' }, { name = 'cluster-grenade', count = 20, text = ' Cluster-Grenades' } },
    [24] = { { name = 'explosive-rocket', count = 40, text = ' Explosive Rockets' }, { name = 'piercing-rounds-magazine', count = 200, text = ' SMG Rounds' } },
    [25] = { { name = 'land-mine', count = 50, text = ' Landmines' }, { name = 'solar-panel-equipment', count = 2, text = ' Portable Solar Panel' } },
    [26] = { { name = 'uranium-rounds-magazine', count = 100, text = ' Uranium Rounds' } },
    [27] = { { name = 'energy-shield-equipment', count = 2, text = ' Energy Shield MK1' }, { name = 'poison-capsule', count = 50, text = ' Poison-Capsule' } },
    [28] = { { name = 'exoskeleton-equipment', count = 1, text = ' Exoskelet' }, { name = 'battery-mk2-equipment', count = 1, text = ' Armor Battery Mk2' } },
    [29] = { { name = 'distractor-capsule', count = 40, text = ' Distractor Bots' }, { name = 'personal-laser-defense-equipment', count = 2, text = ' Personal Laser Defense' } },
    [30] = { { name = 'fusion-reactor-equipment', count = 1, text = ' Fusion Reactor' }, { name = 'computer', count = 1, text = ' Teleporter Computer' } },
    [31] = { { name = 'uranium-rounds-magazine', count = 200, text = ' Uranium Rounds' }, { name = 'destroyer-capsule', count = 40, text = ' Destroyer Capsules' } },
    [32] = { { name = 'destroyer-capsule', count = 50, text = ' Destroyer Bots' } },
    [33] = { { name = 'power-armor-mk2', count = 1, text = ' Power Armor MK2' } },
    [34] = { { name = 'exoskeleton-equipment', count = 1, text = ' Exoskeleton' }, { name = 'uranium-rounds-magazine', count = 200, text = ' Uranium Rounds' } },
    [35] = { { name = 'energy-shield-mk2-equipment', count = 1, text = ' Energy Shield MK2' } },
    [36] = { { name = 'personal-roboport-mk2-equipment', count = 1, text = ' Personal Roboport MK2' } },
    [37] = { { name = 'personal-laser-defense-equipment', count = 2, text = ' Personal Laser Defense' } },
    [38] = { { name = 'fusion-reactor-equipment', count = 2, text = ' Fusion Reactor' }, { name = 'uranium-rounds-magazine', count = 400, text = ' Uranium Rounds' } },
    [39] = { { name = 'atomic-bomb', count = 10, text = ' Atomic Rockets' } },
    [40] = { { name = 'computer', count = 2, text = ' Teleporter Computer' }, { name = 'uranium-rounds-magazine', count = 500, text = ' Uranium Rounds' } }
}

local function create_reward_button(player)
    if not player.gui.top.rewards then
        local b = player.gui.top.add({ type = 'sprite-button', name = 'rewards', sprite = 'item/submachine-gun' })
        b.style.minimal_height = 38
        b.style.minimal_width = 38
        b.style.top_padding = 2
        b.style.left_padding = 4
        b.style.right_padding = 4
        b.style.bottom_padding = 2
    end
end

local function show_rewards(player)
    local get_score = Score.get_table().score_table
    if player.gui.left['rewards_panel'] then
        player.gui.left['rewards_panel'].destroy()
    end
    local frame = player.gui.left.add { type = 'frame', name = 'rewards_panel', direction = 'vertical' }

    local current_level = storage.rewards[player.name].level
    local next_level = current_level + 1
    local kill_score = get_score[player.force.name].players[player.name].killscore

    local next_level_score = ((3.5 + next_level) ^ 2.7 / 10) * 100
    local min_score = ((3.5 + current_level) ^ 2.7 / 10) * 100

    local t = frame.add { type = 'table', column_count = 2 }

    local l = t.add { type = 'label', caption = 'Combat Level: ' }
    l.style.font = 'default-bold'
    l.style.font_color = { r = 244, g = 212, b = 66 }
    l.style.minimal_width = 100

    local str = '0'
    if storage.rewards[player.name].level then
        str = tostring(current_level)
    end
    l = t.add { type = 'label', caption = str }
    l.style.font = 'default-bold'
    l.style.font_color = { r = 0.9, g = 0.9, b = 0.9 }
    l.style.minimal_width = 123

    t = frame.add { type = 'table', column_count = 1 }

    l = t.add { type = 'label', caption = 'Progress to Next Level: ' }
    l.style.font = 'default-bold'
    l.style.font_color = { r = 244, g = 212, b = 66 }
    l.style.minimal_width = 123

    t = frame.add { type = 'table', column_count = 1 }
    local value
    if kill_score then
        value = ((kill_score - min_score) / (floor(next_level_score) - min_score))
    end
    l = t.add { type = 'progressbar', value = value }
    l.style.font = 'default-bold'
    l.style.font_color = { r = 0.9, g = 0.9, b = 0.9 }
    l.style.minimal_width = 123
    l.style.height = 20

    t = frame.add { type = 'table', column_count = 1 }

    l = t.add { type = 'label', caption = 'Next Reward: ' }
    l.style.font = 'default-bold'
    l.style.font_color = { r = 244, g = 212, b = 66 }
    l.style.minimal_width = 123

    t = frame.add { type = 'table', column_count = 1 }

    for _, v in pairs(rewards_loot[next_level]) do
        str = '0'
        if storage.rewards[player.name].level then
            str = tostring(v.count .. ' ' .. v.text)
        end
        l = t.add { type = 'label', caption = str }
        l.style.font = 'default-bold'
        l.style.font_color = { r = 0.9, g = 0.9, b = 0.9 }
        l.style.minimal_width = 123
    end
end

local function rewards_gui(event)
    if not event then
        return
    end
    if not event.element then
        return
    end
    if not event.element.valid then
        return
    end

    local player = game.players[event.element.player_index]
    local name = event.element.name

    if name == 'rewards' then
        if player.gui.left['rewards_panel'] then
            player.gui.left['rewards_panel'].destroy()
        else
            show_rewards(player)
        end
        return
    end
end

-- Callback to trigger the player level
local callback =
    Token.register(
        function (data)
            if #data.pos_list < 1 then
                return
            end
            for i = 1, #data.pos_list, 1 do
                if data.pos_list[i].distance >= data.run then
                    data.surface.create_entity({ name = 'water-splash', position = data.pos_list[i].position })
                end
            end
        end
    )

local function reward_messages(data)
    local player = data.player
    local item_rewards = data.rewards
    -- Check that the table isn't empty
    if #item_rewards < 1 then
        return
    end
    local print_text = ''

    player.surface.create_entity({ name = 'flying-text', position = { player.position.x, player.position.y }, text = 'Reached Combat Level: ' .. data.next_level, color = { r = 0.2, g = 1.0, b = 0.1 } })
    -- Loop through all of the rewards for this level and print out flying text
    for i = 1, #item_rewards, 1 do
        player.surface.create_entity({ name = 'flying-text', position = { player.position.x, player.position.y + (i * 0.5) }, text = item_rewards[i].text, color = { r = 1.0, g = 1.0, b = 1.0 } })
        if i > 1 then
            print_text = item_rewards[i].text .. ' ' .. print_text
        else
            print_text = item_rewards[i].text
        end
    end
    player.print('[INFO] Kill Score Level ' .. data.next_level .. ' Achieved! Rewards: ' .. print_text, { r = 1.0, g = 0.84, b = 0.36 })
end

local function kill_rewards(event)
    local get_score = Score.get_table().score_table
    if not event.cause then
        return
    end
    local player = event.cause.player
    local pinsert = player.insert
    local score = get_score[player.force.name]
    local kill_score = score.players[player.name].killscore

    -- If kill score isn't found don't run the other stuff
    if not kill_score then
        return
    end
    local surface = player.surface
    local center_position = surface.get_tile(player.position).position
    local current_level = storage.rewards[player.name].level
    local next_level_score = ((3.5 + current_level + 1) ^ 2.7 / 10) * 100
    if kill_score >= next_level_score then
        local next_level = current_level + 1
        storage.rewards[player.name].level = next_level
        -- Get item rewards for this level
        local leveled_list = {}
        for _, v in pairs(rewards_loot[next_level]) do
            insert(leveled_list, { text = '+' .. v.count .. v.text })
        end
        reward_messages({ player = player, rewards = leveled_list, next_level = next_level })
        -- Insert Item rewards into players inventory
        for k, item in pairs(rewards_loot[next_level]) do
            local inserted_count = pinsert { name = item.name, count = item.count }
            -- Check if player inventory is full, store remaining rewards in table
            if (item.count - inserted_count) > 0 then
                surface.spill_item_stack(center_position, { name = item.name, count = (item.count - inserted_count) }, true)
                player.print('[WARNING] Inventory Full, Rewards Dropped', { r = 1.0, g = 0.0, b = 0.0 })
            end
        end
        -- Creates the level up effect in a radius
        for i = 1, 5, 1 do
            local pos_list = {}
            local area = { left_top = { x = (center_position.x - i), y = (center_position.y - i) }, right_bottom = { x = (center_position.x + i + 1), y = (center_position.y + i + 1) } }
            for _, t in pairs(surface.find_tiles_filtered { area = area }) do
                local distance = floor(sqrt((center_position.x - t.position.x) ^ 2 + (center_position.y - t.position.y) ^ 2))
                if (distance <= i) then
                    insert(pos_list, { position = { t.position.x + 1, t.position.y + 1 }, distance = distance })
                end
            end
            -- Sets each new timer for each tile expansions loop
            Task.set_timeout_in_ticks(10 + i * 10, callback, { pos_list = pos_list, surface = surface, run = i })
        end
    end

    -- Refresh GUI
    if event.cause then
        if event.cause.player then
            if event.cause.player.gui.left['rewards_panel'] then
                show_rewards(event.cause.player)
            end
        end
    end
end

local function check_data(event)
    local player = game.players[event.player_index]
    if not storage.rewards then
        storage.rewards = {}
    end
    if not storage.rewards[player.name] then
        storage.rewards[player.name] = { level = 0 }
    end
    if not storage.inventory_queue then
        storage.inventory_queue = {}
    end
    if not storage.inventory_queue[player.name] then
        storage.inventory_queue[player.name] = { items = {} }
    end

    create_reward_button(player)
end

Event.add(defines.events.on_entity_died, kill_rewards)
Event.add(defines.events.on_player_joined_game, check_data)
Event.add(defines.events.on_gui_click, rewards_gui)
