-- this script adds a group button to create groups for your players --

local Tabs = require 'comfy_panel.main'
local Global = require 'utils.global'
local SpamProtection = require 'utils.spam_protection'
local Token = require 'utils.token'

local module_name = 'Groups'

local this = {
    player_group = {},
    join_spam_protection = {},
    tag_groups = {},
    alphanumeric = true
}

Global.register(
    this,
    function(t)
        this = t
    end
)

local Public = {}

local function build_group_gui(data)
    local player = data.player
    local frame = data.frame
    local group_name_width = 150
    local description_width = 240
    local members_width = 90
    local member_columns = 3
    local actions_width = 80
    local total_height = frame.style.minimal_height - 60

    frame.clear()

    local t = frame.add({type = 'table', column_count = 5})
    local headings = {
        {'Title', group_name_width},
        {'Description', description_width},
        {'Members', members_width * member_columns},
        {'', actions_width}
    }
    for _, h in pairs(headings) do
        local l = t.add({type = 'label', caption = h[1]})
        l.style.font_color = {r = 0.98, g = 0.66, b = 0.22}
        l.style.font = 'default-listbox'
        l.style.top_padding = 6
        l.style.minimal_height = 40
        l.style.minimal_width = h[2]
        l.style.maximal_width = h[2]
    end

    local scroll_pane =
        frame.add(
        {
            type = 'scroll-pane',
            name = 'scroll_pane',
            direction = 'vertical',
            horizontal_scroll_policy = 'never',
            vertical_scroll_policy = 'auto'
        }
    )
    scroll_pane.style.maximal_height = total_height - 50
    scroll_pane.style.minimal_height = total_height - 50

    t = scroll_pane.add({type = 'table', name = 'groups_table', column_count = 4})
    for _, h in pairs(headings) do
        local l = t.add({type = 'label', caption = ''})
        l.style.minimal_width = h[2]
        l.style.maximal_width = h[2]
    end

    for _, group in pairs(this.tag_groups) do
        if (group.name and group.founder and group.description) then
            local l = t.add({type = 'label', caption = group.name})
            l.style.font = 'default-bold'
            l.style.top_padding = 16
            l.style.bottom_padding = 16
            l.style.minimal_width = group_name_width
            l.style.maximal_width = group_name_width
            local color
            if game.players[group.founder] and game.players[group.founder].color then
                color = game.players[group.founder].color
            else
                color = {r = 0.90, g = 0.90, b = 0.90}
            end
            color = {r = color.r * 0.6 + 0.4, g = color.g * 0.6 + 0.4, b = color.b * 0.6 + 0.4, a = 1}
            l.style.font_color = color
            l.style.single_line = false
            l = t.add({type = 'label', caption = group.description})
            l.style.top_padding = 16
            l.style.bottom_padding = 16
            l.style.minimal_width = description_width
            l.style.maximal_width = description_width
            l.style.font_color = {r = 0.90, g = 0.90, b = 0.90}
            l.style.single_line = false

            local tt = t.add({type = 'table', column_count = member_columns})
            for _, p in pairs(game.connected_players) do
                if group.name == this.player_group[p.name] then
                    l = tt.add({type = 'label', caption = p.name})
                    color = {
                        r = p.color.r * 0.6 + 0.4,
                        g = p.color.g * 0.6 + 0.4,
                        b = p.color.b * 0.6 + 0.4,
                        a = 1
                    }
                    l.style.font_color = color
                    --l.style.minimal_width = members_width
                    l.style.maximal_width = members_width * 2
                end
            end

            tt = t.add({type = 'table', name = group.name, column_count = 1})
            if group.name ~= this.player_group[player.name] then
                local b = tt.add({type = 'button', caption = 'Join'})
                b.style.font = 'default-bold'
                b.style.minimal_width = actions_width
                b.style.maximal_width = actions_width
            else
                local b = tt.add({type = 'button', caption = 'Leave'})
                b.style.font = 'default-bold'
                b.style.minimal_width = actions_width
                b.style.maximal_width = actions_width
            end
            if player.admin == true or group.founder == player.name then
                local b = tt.add({type = 'button', caption = 'Delete'})
                b.style.font = 'default-bold'
                b.style.minimal_width = actions_width
                b.style.maximal_width = actions_width
            else
                l = tt.add({type = 'label', caption = ''})
                l.style.minimal_width = actions_width
                l.style.maximal_width = actions_width
            end
        end
    end

    local frame2 = frame.add({type = 'frame', name = 'frame2'})
    t = frame2.add({type = 'table', name = 'group_table', column_count = 3})
    local textfield = t.add({type = 'textfield', name = 'new_group_name', text = 'Name'})
    textfield.style.minimal_width = 200
    textfield = t.add({type = 'textfield', name = 'new_group_description', text = 'Description'})
    textfield.style.minimal_width = 400
    local b = t.add({type = 'button', name = 'create_new_group', caption = 'Create'})
    b.style.minimal_width = 150
    b.style.font = 'default-bold'
end

local build_group_gui_token = Token.register(build_group_gui)

local function refresh_gui()
    for _, p in pairs(game.connected_players) do
        local frame = Tabs.comfy_panel_get_active_frame(p)
        if frame then
            if frame.name == module_name then
                local new_group_name = frame.frame2.group_table.new_group_name.text
                local new_group_description = frame.frame2.group_table.new_group_description.text

                local data = {player = p, frame = frame}
                build_group_gui(data)

                frame = Tabs.comfy_panel_get_active_frame(p)
                frame.frame2.group_table.new_group_name.text = new_group_name
                frame.frame2.group_table.new_group_description.text = new_group_description
            end
        end
    end
end

local function on_player_joined_game(event)
    local player = game.players[event.player_index]

    if not this.player_group[player.name] then
        this.player_group[player.name] = '[Group]'
    end

    if not this.join_spam_protection[player.name] then
        this.join_spam_protection[player.name] = game.tick
    end
end

local function alphanumeric(str)
    return (string.match(str, '[^%w%s%p]') ~= nil)
end

local function on_gui_click(event)
    local element = event.element
    if not element or not element.valid then
        return
    end
    local player = game.get_player(event.player_index)

    local name = event.element.name

    if name == 'tab_Groups' then
        local is_spamming = SpamProtection.is_spamming(player, nil, 'Groups tab_Groups')
        if is_spamming then
            return
        end
    end

    local frame = Tabs.comfy_panel_get_active_frame(player)
    if not frame then
        return
    end
    if frame.name ~= module_name then
        return
    end

    if name == 'create_new_group' then
        local is_spamming = SpamProtection.is_spamming(player, nil, 'Group Click')

        if is_spamming then
            return
        end

        local new_group_name = frame.frame2.group_table.new_group_name.text
        local new_group_description = frame.frame2.group_table.new_group_description.text
        if new_group_name ~= '' and new_group_name ~= 'Name' and new_group_description ~= 'Description' then
            if this.alphanumeric then
                if alphanumeric(new_group_name) then
                    player.print('Group name is not valid.', {r = 0.90, g = 0.0, b = 0.0})
                    return
                end

                if alphanumeric(new_group_description) then
                    player.print('Group description is not valid.', {r = 0.90, g = 0.0, b = 0.0})
                    return
                end
            end

            if string.len(new_group_name) > 64 then
                player.print('Group name is too long. 64 characters maximum.', {r = 0.90, g = 0.0, b = 0.0})
                return
            end

            if string.len(new_group_description) > 128 then
                player.print('Description is too long. 128 characters maximum.', {r = 0.90, g = 0.0, b = 0.0})
                return
            end

            this.tag_groups[new_group_name] = {
                name = new_group_name,
                description = new_group_description,
                founder = player.name
            }
            local color = {
                r = player.color.r * 0.7 + 0.3,
                g = player.color.g * 0.7 + 0.3,
                b = player.color.b * 0.7 + 0.3,
                a = 1
            }
            game.print(player.name .. ' has founded a new group!', color)
            game.print('>> ' .. new_group_name, {r = 0.98, g = 0.66, b = 0.22})
            game.print(new_group_description, {r = 0.85, g = 0.85, b = 0.85})

            frame.frame2.group_table.new_group_name.text = 'Name'
            frame.frame2.group_table.new_group_description.text = 'Description'
            refresh_gui()
            return
        end
    end

    local p = element.parent
    if p then
        p = p.parent
    end
    if p then
        if p.name == 'groups_table' then
            local is_spamming = SpamProtection.is_spamming(player, nil, 'Group Click')

            if is_spamming then
                return
            end

            if element.type == 'button' and element.caption == 'Join' then
                this.player_group[player.name] = element.parent.name
                local str = '[' .. element.parent.name
                str = str .. ']'
                player.tag = str
                if game.tick - this.join_spam_protection[player.name] > 600 then
                    local color = {
                        r = player.color.r * 0.7 + 0.3,
                        g = player.color.g * 0.7 + 0.3,
                        b = player.color.b * 0.7 + 0.3,
                        a = 1
                    }
                    game.print(player.name .. ' has joined group "' .. element.parent.name .. '"', color)
                    this.join_spam_protection[player.name] = game.tick
                end
                refresh_gui()
                return
            end

            if element.type == 'button' and element.caption == 'Delete' then
                for _, players in pairs(game.players) do
                    if this.player_group[players.name] then
                        if this.player_group[players.name] == element.parent.name then
                            this.player_group[players.name] = '[Group]'
                            players.tag = ''
                        end
                    end
                end
                game.print(player.name .. ' deleted group "' .. element.parent.name .. '"')
                this.tag_groups[element.parent.name] = nil
                refresh_gui()
                return
            end

            if element.type == 'button' and element.caption == 'Leave' then
                this.player_group[player.name] = '[Group]'
                player.tag = ''
                refresh_gui()
                return
            end
        end
    end
end

function Public.alphanumeric_only(value)
    if value then
        this.alphanumeric = value
    else
        this.alphanumeric = false
    end
end

function Public.reset_groups()
    local players = game.connected_players
    for i = 1, #players do
        local player = players[i]
        this.player_group[player.name] = '[Group]'
        this.join_spam_protection[player.name] = game.tick
    end
    this.tag_groups = {}
end

Tabs.add_tab_to_gui({name = module_name, id = build_group_gui_token, admin = false})

local event = require 'utils.event'
event.add(defines.events.on_gui_click, on_gui_click)
event.add(defines.events.on_player_joined_game, on_player_joined_game)

return Public
