--luacheck: ignore
local Event = require 'utils.event'
local AI = require 'utils.functions.AI'
local math_random = math.random
local math_floor = math.floor
storage.biter_command = {}
storage.biter_command.active_unit_groups = {}
storage.biter_command.enabled = true
storage.biter_command.whitelist = {}
storage.biter_command.admin_mode = true   --if only admins can see and use the panel

storage.biter_command.teleporting = false --if teleporting is allowed for non-admins
storage.biter_command.buildings = true    ---if player can trigger building nests and worms

local worm_raffle = {
    'small-worm-turret',
    'small-worm-turret',
    'medium-worm-turret',
    'small-worm-turret',
    'medium-worm-turret',
    'medium-worm-turret',
    'big-worm-turret',
    'medium-worm-turret',
    'big-worm-turret',
    'big-worm-turret',
    'behemoth-worm-turret',
    'big-worm-turret',
    'behemoth-worm-turret',
    'behemoth-worm-turret',
    'behemoth-worm-turret',
    'big-worm-turret',
    'behemoth-worm-turret'
}





local function get_evo(force)
    local evo = math_floor(game.forces['enemy'].evolution_factor * 20)
    local nests = math_random(1 + evo, 2 + evo * 2)
end

local function place_nest_near_unit_group(group)
    if not storage.biter_command.buildings then
        return false
    end
    if not group.members then
        return false
    end
    if #group.members < 5 then
        return false
    end
    local units = group.members
    table.shuffle_table(units)
    for i = 1, 5, 1 do
        if not units[i].valid then
            return false
        end
    end
    local name = 'biter-spawner'
    if math_random(1, 3) == 1 then
        name = 'spitter-spawner'
    end
    local position = group.surface.find_non_colliding_position(name, group.position, 16, 1)
    if not position then
        return false
    end
    group.surface.create_entity({ name = name, position = position, force = group.force })
    group.surface.create_entity({ name = 'blood-explosion-huge', position = position })
    for i = 1, 5, 1 do
        units[i].surface.create_entity({ name = 'blood-explosion-huge', position = units[i].position })
        units[i].destroy()
    end
    return true
end

local function build_worm(group)
    if not storage.biter_command.buildings then
        return false
    end
    if not group.members then
        return false
    end
    if #group.members < 5 then
        return false
    end
    local units = group.members
    table.shuffle_table(units)
    for i = 1, 5, 1 do
        if not units[i].valid then
            return false
        end
    end
    local position = group.surface.find_non_colliding_position('assembling-machine-1', group.position, 8, 1)
    local worm = worm_raffle[math_random(1 + math_floor(group.force.evolution_factor * 8), math_floor(1 + group.force.evolution_factor * 16))]
    if not position then
        return false
    end
    group.surface.create_entity({ name = worm, position = position, force = group.force })
    group.surface.create_entity({ name = 'blood-explosion-huge', position = position })
    for i = 1, 5, 1 do
        units[i].surface.create_entity({ name = 'blood-explosion-huge', position = units[i].position })
        units[i].destroy()
    end
    return true
end

local function flying_text(message, action, position, player)
    local texts = {
        { 'roger',    'acknowledged',     'aye aye',         'confirmed',                'will do' },
        { 'negative', 'no',               'not really',      'we are not your critters', 'go away' },
        { 'fooood',   'nom nom',          'we hunger',       'killllll' },
        { 'WTF',      'we wanted ACTION', 'why you hate us', 'we were good soldiers',    'go to hell' }
    }
    colors = { { r = 0, g = 220, b = 0 }, { r = 220, g = 0, b = 0 }, { r = 0, g = 100, b = 220 }, { r = 200, g = 200, b = 0 }, { r = 255, g = 255, b = 255 } }
    if message then
        player.create_local_flying_text { text = message, position = position, color = colors[5] }
    else
        player.create_local_flying_text { text = texts[action][math_random(1, #texts[action])], position = position, color = colors[action] }
    end
end

-----------commands-----------


-- local function attackmaincommand(target)
--   local wave_defense_table = WD.get_table()
--   if not wave_defense_table.target then return end
--   if not wave_defense_table.target.valid then return end
--   local command = {
-- 		type = defines.command.attack,
-- 		target = target,
-- 		distraction = defines.distraction.by_enemy,
-- 	}
--   return command
-- end



local function get_coords(group, source_player)
    local position
    if source_player.gui.screen['biter_panel'] then
        local x = tonumber(source_player.gui.screen['biter_panel']['coords']['coord_x'].text)
        local y = tonumber(source_player.gui.screen['biter_panel']['coords']['coord_y'].text)
        if x == nil or x == 'nil' then
            x = group.position.x
            source_player.gui.screen['biter_panel']['coords']['coord_x'].text = tostring(group.position.x)
        end
        if y == nil or y == 'nil' then
            y = group.position.y
            source_player.gui.screen['biter_panel']['coords']['coord_y'].text = tostring(group.position.y)
        end
        position = { x = x, y = y }
    end
    return position
end

-------button functions-------------

local function pan(group, source_player)
    source_player.open_map(group.position, 0.5)
end

local function teleport(group, source_player)
    if source_player.admin or storage.biter_command.teleporting then
        source_player.teleport(group.position, group.surface)
    else
        flying_text('Teleporting is disabled', nil, source_player.position, source_player)
    end
end

local function disband(group, source_player)
    flying_text(nil, 4, group.position, source_player)
    group.destroy()
end

local function movetome(group, source_player)
    group.set_command(AI.command_move_to(source_player.position, defines.distraction.none))
    flying_text(nil, 1, group.position, source_player)
end

local function movetoposition(group, source_player)
    local position = get_coords(group, source_player)
    if position then
        group.set_command(AI.command_move_to(position, defines.distraction.none))
        flying_text(nil, 1, group.position, source_player)
    else
        flying_text(nil, 2, group.position, source_player)
    end
end

local function patroltome(group, source_player)
    group.set_command(AI.command_move_to(source_player.position, defines.distraction.by_enemy))
    flying_text(nil, 1, group.position, source_player)
end

local function patroltoposition(group, source_player)
    local position = get_coords(group, source_player)
    if position then
        group.set_command(AI.command_move_to(position, defines.distraction.by_enemy))
        flying_text(nil, 1, group.position, source_player)
    else
        flying_text(nil, 2, group.position, source_player)
    end
end

local function settle(group, source_player)
    local success = place_nest_near_unit_group(group)
    if success then
        flying_text(nil, 1, group.position, source_player)
    else
        flying_text(nil, 2, group.position, source_player)
        source_player.print('Settling new nest failed. Check if group has enough members(5+) and there is empty space (or nests are disabled).')
    end
end

local function siege(group, source_player)
    local success = build_worm(group)
    if success then
        flying_text(nil, 1, group.position, source_player)
    else
        flying_text(nil, 2, group.position, source_player)
        source_player.print('Making worm failed. Check if group has enough members(5+) and there is empty space (or worms are disabled).')
    end
end

local function report(group, source_player)
    local status = group.state
    local states = { 'gathering', 'moving', 'attacking distraction', 'attacking target', 'finished', 'pathfinding', 'wander in group' }
    flying_text(states[status + 1], nil, group.position, source_player)
end

local function attackenemiesaround(group, source_player)
    flying_text(nil, 3, group.position, source_player)
    group.set_command(AI.command_attack_area(group.position, 25))
end

local function attackobstaclesaround(group, source_player)
    local commands = AI.command_attack_obstacles(group.surface, group.position)
    if #commands > 1 then
        group.set_command(
            {
                type = defines.command.compound,
                structure_type = defines.compound_command.return_last,
                commands = commands
            }
        )
        flying_text(nil, 3, group.position, source_player)
    else
        source_player.print('No obstacles found around unit group.')
        flying_text(nil, 2, group.position, source_player)
    end
end

local function attackenemiesaroundme(group, source_player)
    group.set_command(AI.command_attack_area(source_player.position, 25))
    flying_text(nil, 3, group.position, source_player)
end

local function attackobstaclesaroundme(group, source_player)
    local commands = AI.command_attack_obstacles(source_player.surface, source_player.position)
    if #commands > 1 then
        group.set_command(
            {
                type = defines.command.compound,
                structure_type = defines.compound_command.return_last,
                commands = commands
            }
        )
        flying_text(nil, 3, group.position, source_player)
    else
        source_player.print('No obstacles found around player.')
        flying_text(nil, 2, group.position, source_player)
    end
end

local function addunitsaroundme(group, source_player)
    local units = source_player.surface.find_entities_filtered { position = source_player.position, radius = 50, type = 'unit', force = group.force }
    for i = 1, #units, 1 do
        group.add_member(units[i])
    end
end

local function addunits(group, source_player)
    local units = source_player.surface.find_entities_filtered { position = group.position, radius = 50, type = 'unit', force = group.force }
    for i = 1, #units, 1 do
        group.add_member(units[i])
    end
end

local function forcemove(group, source_player)
    group.start_moving()
    flying_text(nil, 1, group.position, source_player)
end

local function creategroup(source_player)
    source_player.surface.create_unit_group { position = source_player.position, force = source_player.force }
    flying_text('Unit group created', nil, source_player.position, source_player)
end
----------------------direction panel-----------------
local function set_directions(changedx, changedy, source_player)
    if source_player.gui.screen['biter_panel'] then
        local x = tonumber(source_player.gui.screen['biter_panel']['coords']['coord_x'].text)
        local y = tonumber(source_player.gui.screen['biter_panel']['coords']['coord_y'].text)
        if x == nil or x == 'nil' then
            x = 0
        end
        if y == nil or y == 'nil' then
            y = 0
        end
        x = x + changedx
        y = y + changedy
        source_player.gui.screen['biter_panel']['coords']['coord_x'].text = tostring(x)
        source_player.gui.screen['biter_panel']['coords']['coord_y'].text = tostring(y)
    end
end

local function nw(source_player)
    set_directions(-25, -25, source_player)
end

local function n(source_player)
    set_directions(0, -25, source_player)
end

local function ne(source_player)
    set_directions(25, -25, source_player)
end

local function w(source_player)
    set_directions(-25, 0, source_player)
end

local function e(source_player)
    set_directions(25, 0, source_player)
end

local function sw(source_player)
    set_directions(-25, 25, source_player)
end

local function s(source_player)
    set_directions(0, 25, source_player)
end

local function se(source_player)
    set_directions(25, 25, source_player)
end

local function center(group, source_player)
    if source_player.gui.screen['biter_panel'] then
        source_player.gui.screen['biter_panel']['coords']['coord_x'].text = tostring(group.position.x)
        source_player.gui.screen['biter_panel']['coords']['coord_y'].text = tostring(group.position.y)
    end
end

----------------------------gui-----------------------

local function top_button(player)
    if player.gui.top['biter_commands'] then
        if storage.biter_command.enabled or storage.biter_command.whitelist[player.name] == true then
            player.gui.top['biter_commands'].visible = true
            return
        else
            --player.gui.top["biter_commands"].destroy()
            player.gui.top['biter_commands'].visible = false
            return
        end
    end
    if player.admin or not storage.biter_command.admin_mode then
        if storage.biter_command.enabled or storage.biter_command.whitelist[player.name] == true then
            local button = player.gui.top.add({ type = 'sprite-button', name = 'biter_commands', sprite = 'entity/medium-spitter' })
            button.style.minimal_height = 38
            button.style.minimal_width = 38
            button.style.padding = -2
        end
    end
end

local function show_info(player)
    if player.gui.screen['biter_comm_info'] then
        player.gui.screen['biter_comm_info'].destroy()
        return
    end
    local frame = player.gui.screen.add { type = 'frame', name = 'biter_comm_info', caption = 'Biter Commander needs halp', direction = 'vertical' }
    frame.location = { x = 350, y = 45 }
    frame.style.minimal_height = 300
    frame.style.maximal_height = 300
    frame.style.minimal_width = 330
    frame.style.maximal_width = 630
    frame.add({ type = 'label', caption = 'Create new group first, then add biters to it.' })
    frame.add({ type = 'label', caption = 'You can use directionpad to navigate them, or do it in person.' })
    frame.add({ type = 'label', caption = "If you input invalid coordinates, they get rewritten to current group's position." })
    frame.add({ type = 'label', caption = 'You can operate only biters and create groups of your own force.' })
    frame.add({ type = 'label', caption = "If group is stuck at gathering state, use 'force move' button." })
    frame.add({ type = 'label', caption = 'Empty groups get autodeleted by game after a while.' })
    frame.add({ type = 'button', name = 'close_info', caption = 'Close' })
end

local function build_groups(player)
    local groups = {}
    for _, g in pairs(storage.biter_command.active_unit_groups) do
        if g.group.valid then
            if player.admin and storage.biter_command.admin_mode then
                table.insert(groups, tostring(g.id))
            else
                if player.force == g.group.force then
                    table.insert(groups, tostring(g.id))
                end
            end
        else
            g = nil
        end
    end
    table.insert(groups, 'Select Group')
    return groups
end

local function biter_panel(player)
    if player.gui.screen['biter_panel'] then
        player.gui.screen['biter_panel'].destroy()
        return
    end

    local frame = player.gui.screen.add { type = 'frame', caption = 'Biter Commander', name = 'biter_panel', direction = 'vertical' }
    frame.location = { x = 5, y = 45 }
    frame.style.minimal_height = 680
    frame.style.maximal_height = 680
    frame.style.minimal_width = 330
    frame.style.maximal_width = 330

    local groups = build_groups(player)
    local selected_index = #groups
    if storage.panel_group_index then
        if storage.panel_group_index[player.name] then
            if groups[storage.panel_group_index[player.name]] then
                selected_index = storage.paneld_group_index[player.name]
            end
        end
    end
    local t0 = frame.add({ type = 'table', name = 'top', column_count = 3 })
    local drop_down = t0.add({ type = 'drop-down', name = 'group_select', items = groups, selected_index = selected_index })
    drop_down.style.minimal_width = 150
    drop_down.style.right_padding = 12
    drop_down.style.left_padding = 12
    t0.add({ type = 'sprite-button', name = 'info', sprite = 'virtual-signal/signal-info' })
    t0.add({ type = 'sprite-button', name = 'close_biters', sprite = 'virtual-signal/signal-X' })

    local l1 = frame.add({ type = 'label', caption = 'Camera' })
    local t1 = frame.add({ type = 'table', name = 'camera', column_count = 2 })
    local l2 = frame.add({ type = 'label', caption = 'Movement' })
    local t2 = frame.add({ type = 'table', name = 'movement', column_count = 2 })
    local l3 = frame.add({ type = 'label', caption = 'Build' })
    local t3 = frame.add({ type = 'table', name = 'build', column_count = 2 })
    local l4 = frame.add({ type = 'label', caption = 'Attack' })
    local t4 = frame.add({ type = 'table', name = 'attack', column_count = 2 })
    local l5 = frame.add({ type = 'label', caption = 'Group Management' })
    local t5 = frame.add({ type = 'table', name = 'management', column_count = 2 })
    local line = frame.add { type = 'line' }
    line.style.top_margin = 8
    line.style.bottom_margin = 8
    local t6 = frame.add({ type = 'table', name = 'directions', column_count = 3 })
    local buttons = {
        t1.add({ type = 'button', caption = 'Pan to group', name = 'pan', tooltip = 'Moves camera to group position.' }),
        t1.add({ type = 'button', caption = 'TP to group', name = 'teleport', tooltip = 'Teleports to group.' }),
        t2.add({ type = 'button', caption = 'Move to me', name = 'movetome', tooltip = 'Gives group order to move to your position.' }),
        t2.add({ type = 'button', caption = 'Move to position', name = 'movetoposition', tooltip = 'Sends group to position with coordinates entered below.' }),
        t2.add({ type = 'button', caption = 'Patrol to me ', name = 'patroltome', tooltip = 'Gives group order to move to your position and engage any enemy during movement.' }),
        t2.add(
            {
                type = 'button',
                caption = 'Patrol to position',
                name = 'patroltoposition',
                tooltip = 'Sends group to position with coordinates entered below and engage any enemy during movement.'
            }
        ),
        t3.add({ type = 'button', caption = 'Settle nest', name = 'settle', tooltip = 'Group creates base. Costs 5 units.' }),
        t3.add({ type = 'button', caption = 'Build worm', name = 'siege', tooltip = 'Group builds worm turret. Costs 5 units.' }),
        t4.add({ type = 'button', caption = 'Attack area', name = 'attackenemiesaround', tooltip = 'Group attacks enemy things around self.' }),
        t4.add({ type = 'button', caption = 'Attack obstacles', name = 'attackobstaclesaround', tooltip = 'Group attacks obstacles around self.' }),
        t4.add({ type = 'button', caption = 'Attack my area', name = 'attackenemiesaroundme', tooltip = 'Group attacks enemy things around your position.' }),
        t4.add({ type = 'button', caption = 'Attack my obstacles', name = 'attackobstaclesaroundme', tooltip = 'Group attacks obstacles around your position.' }),
        t5.add({ type = 'button', caption = 'Report', name = 'report', tooltip = 'Reports group status.' }),
        t5.add({ type = 'button', caption = 'Force Move', name = 'forcemove', tooltip = 'Makes group to start moving even if gathering is not done (unstuck).' }),
        t5.add({ type = 'button', caption = 'Add units around me', name = 'addunitsaroundme', tooltip = 'Adds units around you to selected unit group.' }),
        t5.add({ type = 'button', caption = 'Add units', name = 'addunits', tooltip = 'Adds units around group to it.' }),
        t5.add({ type = 'button', caption = 'Create group', name = 'creategroup', tooltip = 'Creates new group on player position' }),
        t5.add({ type = 'button', caption = 'Disband group', name = 'disband', tooltip = 'Disbands group.' })
    }
    local buttons2 = {
        t6.add({ type = 'button', caption = '25 NW', name = 'nw', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 N', name = 'n', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 NE', name = 'ne', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 W', name = 'w', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = 'Center', name = 'center', tooltip = 'Centers remote position to group' }),
        t6.add({ type = 'button', caption = '25 E', name = 'e', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 SW', name = 'sw', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 S', name = 's', tooltip = 'Changes remote position' }),
        t6.add({ type = 'button', caption = '25 SE', name = 'se', tooltip = 'Changes remote position' })
    }
    for _, button in pairs(buttons) do
        button.style.font = 'default-bold'
        button.style.font_color = { r = 0.99, g = 0.99, b = 0.99 }
        button.style.minimal_width = 150
    end
    for _, button in pairs(buttons2) do
        button.style.font = 'default-bold'
        button.style.font_color = { r = 0.99, g = 0.99, b = 0.99 }
        button.style.minimal_width = 70
    end
    local t7 = frame.add({ type = 'table', name = 'coords', column_count = 2 })
    t7.add({ type = 'label', caption = 'X: ' })
    t7.add({ type = 'textfield', name = 'coord_x' })
    t7.add({ type = 'label', caption = 'Y: ' })
    t7.add({ type = 'textfield', name = 'coord_y' })
end

local comm_functions = {
    ['pan'] = pan,
    ['teleport'] = teleport,
    ['disband'] = disband,
    ['movetome'] = movetome,
    ['movetoposition'] = movetoposition,
    ['patroltome'] = patroltome,
    ['patroltoposition'] = patroltoposition,
    ['settle'] = settle,
    ['siege'] = siege,
    ['report'] = report,
    ['attackenemiesaround'] = attackenemiesaround,
    ['attackobstaclesaround'] = attackobstaclesaround,
    ['attackenemiesaroundme'] = attackenemiesaroundme,
    ['attackobstaclesaroundme'] = attackobstaclesaroundme,
    ['addunits'] = addunits,
    ['addunitsaroundme'] = addunitsaroundme,
    ['forcemove'] = forcemove,
    ['center'] = center
}

local comm_global_functions = {
    ['creategroup'] = creategroup,
    ['nw'] = nw,
    ['n'] = n,
    ['ne'] = ne,
    ['w'] = w,
    ['e'] = e,
    ['sw'] = sw,
    ['s'] = s,
    ['se'] = se
}

local function refresh_groups(player)
    local groups = build_groups(player)
    player.gui.screen['biter_panel']['top']['group_select'].items = groups
end

local function on_gui_click(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]
    if event.element.name == 'biter_commands' then --top button press
        if storage.biter_command.enabled or storage.biter_command.whitelist[player.name] == true then
            biter_panel(player)
            return
        else
            top_button(player)
            player.print('Biter commander module is disabled.')
            return
        end
    else
        if storage.biter_command.enabled or storage.biter_command.whitelist[player.name] == true then
            top_button(player)
        end
    end
    if event.element.type ~= 'button' and event.element.type ~= 'sprite-button' then
        return
    end
    --if event.frame.name ~= "biter_panel" then return end
    local name = event.element.name
    if name == 'close_biters' then
        biter_panel(player)
        return
    end
    if name == 'info' then
        show_info(player)
        return
    end
    if name == 'close_info' then
        show_info(player)
        return
    end
    if comm_functions[name] then
        local target_group_id = event.element.parent.parent['top']['group_select'].items[event.element.parent.parent['top']['group_select'].selected_index]
        if not target_group_id then
            return
        end
        if target_group_id == 'Select Group' then
            player.print('No target group selected.', { r = 0.88, g = 0.88, b = 0.88 })
            return
        end
        -- local index = index(tonumber(target_group_id))
        -- if not index then
        --   player.print("Selected group is no longer valid.", {r=0.88, g=0.88, b=0.88})
        --   return
        -- end
        local group = storage.biter_command.active_unit_groups[tonumber(target_group_id)]
        if group and group.group.valid then
            comm_functions[name](group.group, player)
        else
            refresh_groups(player)
        end
        return
    end

    if comm_global_functions[name] then
        comm_global_functions[name](player)
        return
    end
end

local function refresh_panel()
    for _, player in pairs(game.connected_players) do
        if player.gui.screen['biter_panel'] then
            refresh_groups(player)
        end
    end
end

local function on_player_joined_game(event)
    top_button(game.players[event.player_index])
end

local function on_unit_group_created(event)
    if event and event.group and event.group.valid then
        storage.biter_command.active_unit_groups[event.groupunique_id] = { id = event.groupunique_id, group = event.group }
        refresh_panel()
    end
end

local function on_unit_removed_from_group(event)
    if event and event.group and event.group.valid then
        if #event.group.members == 1 then
            storage.biter_command.active_unit_groups[event.groupunique_id] = nil
            refresh_panel()
        end
    end
end

Event.add(defines.events.on_unit_removed_from_group, on_unit_removed_from_group)
Event.add(defines.events.on_unit_group_created, on_unit_group_created)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_gui_click, on_gui_click)