local PID = SquadMenu.GetPlayerId
local L = SquadMenu.GetLanguageText
local ScaleSize = StyledTheme.ScaleSize

function SquadMenu:CloseFrame()
    if IsValid( self.frame ) then
        self.frame:Close()
    end
end

function SquadMenu:OpenFrame()
    if IsValid( self.frame ) then
        self:CloseFrame()
        return
    end

    local frame = vgui.Create( "Styled_TabbedFrame" )
    frame:SetTitle( L"title" )
    frame:Center()
    frame:MakePopup()

    frame.OnClose = function()
        self.frame = nil
    end

    local h = ScaleSize( 550 )
    frame:SetTall( h )
    frame:SetMinHeight( h )

    self.frame = frame

    local panels = {}
    frame._panels = panels

    -- Squad state
    local separation = ScaleSize( 4 )

    panels.squadState = vgui.Create( "DPanel", frame )
    panels.squadState:SetTall( ScaleSize( 40 ) )
    panels.squadState:Dock( BOTTOM )
    panels.squadState:DockMargin( separation, separation, 0, 0 )
    panels.squadState:DockPadding( separation, separation, separation, separation )

    -- Tabs
    panels.squadList = frame:AddTab( "styledstrike/icons/bullet_list.png", L"tab.squad_list" )
    panels.squadProperties = frame:AddTab( "styledstrike/icons/flag_two_tone.png", L"tab.squad_properties", "DPanel" )
    panels.squadMembers = frame:AddTab( "styledstrike/icons/users.png", L"tab.squad_members", "DPanel" )
    panels.joinRequests = frame:AddTab( "styledstrike/icons/user_add.png", L"tab.join_requests", "DPanel" )
    panels.settings = frame:AddTab( "styledstrike/icons/cog.png", L"tab.settings" )

    self:RequestSquadListUpdate( true )
    self:UpdateSquadStatePanel()
    self:UpdateRequestsPanel()
    self:UpdateSquadMembersPanel()
    self:UpdateSquadPropertiesPanel()

    local squad = self.mySquad

    if squad then
        if #squad.members < 2 then
            frame:SetActiveTabByIndex( 4 ) -- Join requests
        else
            frame:SetActiveTabByIndex( 3 ) -- Squad members
        end
    end

    -- Settings
    StyledTheme.CreateFormHeader( panels.settings, L"tab.settings", 0 )

    StyledTheme.CreateFormSlider( panels.settings, L"settings.name_draw_distance", self.Config.nameDistance, 500, 50000, 0, function( value )
        self.Config.nameDistance = self.ValidateNumber( value, 2000, 500, 50000 )
        self.Config:Save()
    end )

    StyledTheme.CreateFormSlider( panels.settings, L"settings.halo_draw_distance", self.Config.haloDistance, 500, 50000, 0, function( value )
        self.Config.haloDistance = self.ValidateNumber( value, 8000, 500, 50000 )
        self.Config:Save()
    end )

    local binderPing = StyledTheme.CreateFormBinder( panels.settings, L"settings.ping_key", self.Config.pingKey )

    binderPing.OnChange = function( _, key )
        self.Config.pingKey = key
        self.Config:Save()
    end

    StyledTheme.CreateFormToggle( panels.settings, L"settings.show_members", self.Config.showMembers, function( checked )
        self.Config.showMembers = checked
        self.Config:Save()
    end )

    StyledTheme.CreateFormToggle( panels.settings, L"settings.show_rings", self.Config.showRings, function( checked )
        self.Config.showRings = checked
        self.Config:Save()
    end )

    StyledTheme.CreateFormToggle( panels.settings, L"settings.show_halos", self.Config.showHalos, function( checked )
        self.Config.showHalos = checked
        self.Config:Save()
    end )

    StyledTheme.CreateFormToggle( panels.settings, L"settings.enable_sounds", self.Config.enableSounds, function( checked )
        self.Config.enableSounds = checked
        self.Config:Save()
    end )
end

function SquadMenu:GetPanel( id )
    if IsValid( self.frame ) then
        return self.frame._panels[id]
    end
end

function SquadMenu:UpdateSquadStatePanel()
    local statePanel = self:GetPanel( "squadState" )
    if not statePanel then return end

    statePanel:Clear()

    local squad = self.mySquad
    local squadColor = squad and squad.color or Color( 0, 0, 0 )

    statePanel.Paint = function( _, w, h )
        surface.SetDrawColor( 20, 20, 20 )
        surface.DrawRect( 0, 0, w, h )

        surface.SetDrawColor( squadColor:Unpack() )
        surface.DrawOutlinedRect( 0, 0, w, h, 1 )
    end

    local imageIcon = vgui.Create( "DImage", statePanel )
    imageIcon:Dock( LEFT )
    imageIcon:SetWide( ScaleSize( 32 ) )
    imageIcon:SetImage( squad and squad.icon or "vgui/avatar_default" )

    local labelName = vgui.Create( "DLabel", statePanel )
    labelName:Dock( FILL )
    labelName:DockMargin( ScaleSize( 8 ), 0, 0, 0 )
    labelName:SetText( squad and squad.name or L"not_in_a_squad" )

    StyledTheme.Apply( labelName )

    if not squad then return end

    local buttonLeave = vgui.Create( "DButton", statePanel )
    buttonLeave:SetText( L"leave_squad" )
    buttonLeave:SetWide( ScaleSize( 180 ) )
    buttonLeave:Dock( RIGHT )

    StyledTheme.Apply( buttonLeave )

    buttonLeave.DoClick = function()
        SquadMenu.LeaveMySquad( buttonLeave )
    end
end

function SquadMenu:RequestSquadListUpdate( immediate )
    timer.Remove( "SquadMenu.RequestListUpdate" )

    local listPanel = self:GetPanel( "squadList" )
    if not listPanel then return end

    listPanel:Clear()

    StyledTheme.CreateFormHeader( listPanel, L"fetching_data", 0 )

    if not immediate then
        -- Don't spam when this function gets called in quick succession
        timer.Create( "SquadMenu.RequestListUpdate", 1, 1, function()
            SquadMenu:RequestSquadListUpdate( true )
        end )

        return
    end

    self.StartCommand( self.SQUAD_LIST )
    net.SendToServer()
end

function SquadMenu:UpdateSquadList( squads )
    local listPanel = self:GetPanel( "squadList" )
    if not listPanel then return end

    listPanel:Clear()

    if #squads == 0 then
        StyledTheme.CreateFormHeader( listPanel, L"no_available_squads", 0 )
        return
    end

    StyledTheme.CreateFormHeader( listPanel, L"tab.squad_list", 0 )

    local separation = ScaleSize( 6 )

    for _, squad in ipairs( squads ) do
        local line = vgui.Create( "Squad_ListRow", listPanel )
        line:SetSquad( squad )
        line:Dock( TOP )
        line:DockMargin( 0, 0, 0, separation )
    end
end

function SquadMenu:UpdateRequestsPanel()
    local requestsPanel = self:GetPanel( "joinRequests" )
    if not requestsPanel then return end

    requestsPanel:Clear()

    local padding = StyledTheme.dimensions.scrollPadding
    requestsPanel:DockPadding( padding, 0, padding, padding )
    requestsPanel:SetPaintBackground( true )
    requestsPanel:SetBackgroundColor( StyledTheme.colors.scrollBackground )

    self.frame:SetTabNotificationCountByIndex( 4, 0 ) -- Join requests tab

    local squad = self.mySquad

    if not squad then
        StyledTheme.CreateFormHeader( requestsPanel, L"not_in_a_squad", 0 )
        return
    end

    if squad.leaderId ~= PID( LocalPlayer() ) then
        StyledTheme.CreateFormHeader( requestsPanel, L"not_squad_leader", 0 )
        return
    end

    local memberLimit = self.GetMemberLimit() - #squad.members

    if memberLimit < 1 then
        StyledTheme.CreateFormHeader( requestsPanel, L"member_limit_reached", 0 )
        return
    end

    local requestsHeaderLabel = StyledTheme.CreateFormHeader( requestsPanel, L"requests_list", 0 ):GetChildren()[1]

    local function UpdateMemberCount( current )
        requestsHeaderLabel:SetText( L( "slots" ) .. ": " .. current .. "/" .. self.GetMemberLimit() )
    end

    UpdateMemberCount( #squad.members )

    if squad.isPublic then
        StyledTheme.CreateFormHeader( requestsPanel, L"no_requests_needed", 0 )
        return
    end

    if #squad.requests == 0 then
        StyledTheme.CreateFormHeader( requestsPanel, L"no_requests_yet", 0 )
        return
    end

    self.frame:SetTabNotificationCountByIndex( 4, #squad.requests ) -- Join requests tab

    local scrollRequests = vgui.Create( "DScrollPanel", requestsPanel )
    scrollRequests:Dock( FILL )
    scrollRequests:SetPaintBackground( false )

    local buttonAccept
    local acceptedPlayers = {}

    local function OnClickAccept()
        local ids = table.GetKeys( acceptedPlayers )

        self.StartCommand( self.ACCEPT_REQUESTS )
        self.WriteTable( ids )
        net.SendToServer()
    end

    local function UpdateAcceptedCount( count )
        UpdateMemberCount( #squad.members + count )

        if buttonAccept then
            buttonAccept:Remove()
            buttonAccept = nil
        end

        if count == 0 then return end

        buttonAccept = vgui.Create( "DButton", requestsPanel )
        buttonAccept:SetText( L"accept" )
        buttonAccept:Dock( BOTTOM )
        buttonAccept.DoClick = OnClickAccept
        buttonAccept._themeHighlight = true

        StyledTheme.Apply( buttonAccept )
    end

    UpdateAcceptedCount( 0 )

    local function OnClickRow( row )
        local id = row._id
        local count = #table.GetKeys( acceptedPlayers )

        if acceptedPlayers[id] then
            acceptedPlayers[id] = nil
            count = count - 1
        else
            if count < memberLimit then
                acceptedPlayers[id] = true
                count = count + 1
            else
                Derma_Message( L"cannot_accept_more", L"title", L"ok" )
            end
        end

        row.isChecked = acceptedPlayers[id] ~= nil
        UpdateAcceptedCount( count )
    end

    local rowHeight = ScaleSize( 48 )
    local rowPadding = ScaleSize( 6 )
    local nameColor = Color( 255, 255, 255 )

    local function OnPaintRow( row, w, h )
        row._OriginalPaint( row, w, h )
        draw.SimpleText( row._name, "StyledTheme_Small", rowHeight + rowPadding, h * 0.5, nameColor, 0, 1 )
    end

    local byId = SquadMenu.AllPlayersById()
    local dimensions = StyledTheme.dimensions

    for _, member in ipairs( squad.requests ) do
        local row = vgui.Create( "DButton", scrollRequests )
        row:SetText( "" )
        row:Dock( TOP )
        row:DockMargin( dimensions.formPadding, 0, dimensions.formPadding, dimensions.formSeparator )
        row:DockPadding( rowPadding, rowPadding, rowPadding, rowPadding )

        StyledTheme.Apply( row )

        row._id = member.id
        row._name = member.name
        row.isToggle = true
        row.isChecked = false
        row.DoClick = OnClickRow
        row:SetTall( rowHeight )

        row._OriginalPaint = row.Paint
        row.Paint = OnPaintRow

        local avatar = vgui.Create( "AvatarImage", row )
        avatar:Dock( LEFT )
        avatar:SetWide( rowHeight - rowPadding * 2 )

        if byId[member.id] then
            avatar:SetPlayer( byId[member.id], 64 )
        end
    end
end

function SquadMenu:UpdateSquadMembersPanel()
    local membersPanel = self:GetPanel( "squadMembers" )
    if not membersPanel then return end

    membersPanel:Clear()

    local padding = StyledTheme.dimensions.scrollPadding
    membersPanel:DockPadding( padding, 0, padding, padding )
    membersPanel:SetPaintBackground( true )
    membersPanel:SetBackgroundColor( StyledTheme.colors.scrollBackground )

    local squad = self.mySquad

    if not squad then
        StyledTheme.CreateFormHeader( membersPanel, L"not_in_a_squad", 0 )
        return
    end

    local memberCount = #squad.members

    StyledTheme.CreateFormHeader( membersPanel, L( "slots" ) .. ": " .. memberCount .. "/" .. self.GetMemberLimit(), 0 )

    if memberCount < 2 then
        StyledTheme.CreateFormHeader( membersPanel, L"no_members", 0 )
        return
    end

    local localId = PID( LocalPlayer() )
    local isLocalPlayerLeader = squad.leaderId == localId

    local membersScroll = vgui.Create( "DScrollPanel", membersPanel )
    membersScroll:Dock( FILL )
    membersScroll:DockMargin( 0, padding, 0, padding )

    local OnClickKick = function( s )
        s:SetEnabled( false )
        s:SetText( "..." )

        self.StartCommand( self.KICK )
        net.WriteString( s._id )
        net.SendToServer()
    end

    local rowHeight = ScaleSize( 48 )
    local rowPadding = ScaleSize( 6 )

    local colors = StyledTheme.colors
    local DrawRect = StyledTheme.DrawRect

    local function OnPaintRow( row, w, h )
        DrawRect( 0, 0, w, h, colors.buttonBorder )
        DrawRect( 1, 1, w - 2, h - 2, colors.panelBackground )

        draw.SimpleText( row._name, "StyledTheme_Small", rowHeight + rowPadding, h * 0.5, colors.labelText, 0, 1 )
    end

    local byId = SquadMenu.AllPlayersById()
    local dimensions = StyledTheme.dimensions

    for _, member in ipairs( squad.members ) do
        local row = vgui.Create( "Panel", membersScroll )
        row:Dock( TOP )
        row:DockMargin( dimensions.formPadding, 0, dimensions.formPadding, dimensions.formSeparator )
        row:DockPadding( rowPadding, rowPadding, rowPadding, rowPadding )

        row._name = member.name
        row.Paint = OnPaintRow
        row:SetTall( rowHeight )

        local avatar = vgui.Create( "AvatarImage", row )
        avatar:Dock( LEFT )
        avatar:SetWide( rowHeight - rowPadding * 2 )

        if byId[member.id] then
            avatar:SetPlayer( byId[member.id], 64 )
        end

        if isLocalPlayerLeader and member.id ~= localId then
            local kick = vgui.Create( "DButton", row )
            kick:SetText( L"kick" )
            kick:SetWide( ScaleSize( 100 ) )
            kick:Dock( RIGHT )

            kick._id = member.id
            kick.DoClick = OnClickKick

            StyledTheme.Apply( kick )
        end
    end
end

function SquadMenu:UpdateSquadPropertiesPanel()
    local propertiesPanel = self:GetPanel( "squadProperties" )
    if not propertiesPanel then return end

    propertiesPanel:Clear()

    local padding = StyledTheme.dimensions.scrollPadding
    propertiesPanel:DockPadding( padding, 0, padding, padding )
    propertiesPanel:SetPaintBackground( true )
    propertiesPanel:SetBackgroundColor( StyledTheme.colors.scrollBackground )

    local squad = self.mySquad

    if squad and squad.leaderId ~= PID( LocalPlayer() ) then
        StyledTheme.CreateFormHeader( propertiesPanel, L"leave_first_create", 0 )
        return
    end

    local isNew = squad == nil
    local oldName = squad and squad.name or nil
    local oldColor = squad and squad.color or nil

    if not oldColor then
        local c = HSVToColor( math.random( 0, 360 ), 1, 1 )
        oldColor = Color( c.r, c.g, c.b ) -- Reconstruct color instance to avoid a bug
    end

    squad = squad or {
        enableRings = true
    }

    StyledTheme.CreateFormHeader( propertiesPanel, L( isNew and "create_squad" or "edit_squad" ), 0 )

    local data = {
        name = squad.name or string.format( L"default_squad_name", LocalPlayer():Nick() ),
        icon = squad.icon or "icon16/flag_blue.png",

        enableRings = squad.enableRings == true,
        friendlyFire = squad.friendlyFire == true,
        isPublic = squad.isPublic == true,

        r = oldColor.r,
        g = oldColor.g,
        b = oldColor.b
    }

    local buttonCreate = vgui.Create( "DButton", propertiesPanel )
    buttonCreate:SetTall( 36 )
    buttonCreate:SetText( L( isNew and "create_squad" or "edit_squad" ) )
    buttonCreate:Dock( BOTTOM )
    buttonCreate:DockMargin( 0, ScaleSize( 8 ), 0, 0 )

    StyledTheme.Apply( buttonCreate )

    buttonCreate.DoClick = function( s )
        s:SetEnabled( false )
        s:SetText( "..." )

        self.StartCommand( self.SETUP_SQUAD )
        self.WriteTable( data )
        net.SendToServer()
    end

    local leftPanel = vgui.Create( "DPanel", propertiesPanel )
    leftPanel:Dock( FILL )

    StyledTheme.Apply( leftPanel )
    StyledTheme.CreateFormHeader( leftPanel, L"squad_name", 0, 0 )

    local separator = ScaleSize( 6 )
    local rowHeight = StyledTheme.dimensions.buttonHeight

    local entryName = vgui.Create( "DTextEntry", leftPanel )
    entryName:SetTall( rowHeight )
    entryName:Dock( TOP )
    entryName:DockMargin( separator, separator, separator, separator )
    entryName:SetMaximumCharCount( self.MAX_NAME_LENGTH )
    entryName:SetValue( data.name )

    entryName.OnChange = function()
        local value = entryName:GetValue()
        data.name = value:Trim() == "" and oldName or value
    end

    StyledTheme.Apply( entryName )
    StyledTheme.CreateFormHeader( leftPanel, L"tab.squad_properties", 0, 0 )

    local buttonIcon = vgui.Create( "DButton", leftPanel )
    buttonIcon:SetTall( rowHeight )
    buttonIcon:SetIcon( data.icon )
    buttonIcon:SetText( L"choose_icon" )
    buttonIcon:Dock( TOP )
    buttonIcon:DockMargin( separator, separator, separator, 0 )

    StyledTheme.Apply( buttonIcon )

    buttonIcon.DoClick = function()
        local iconBrowser = vgui.Create( "DIconBrowser" )
        iconBrowser:SetSize( 300, 200 )

        local m = DermaMenu()
        m:AddPanel( iconBrowser )
        m:SetPaintBackground( false )
        m:Open( gui.MouseX() + 8, gui.MouseY() + 10 )

        iconBrowser.OnChange = function( s )
            local iconPath = s:GetSelectedIcon()

            buttonIcon:SetIcon( iconPath )
            data.icon = iconPath

            CloseDermaMenus()
        end
    end

    StyledTheme.CreateFormToggle( leftPanel, L"squad_is_public", data.isPublic, function( checked )
        data.isPublic = checked
    end ):DockMargin( separator, separator, separator, 0 )

    local ffButton = StyledTheme.CreateFormToggle( leftPanel, L"squad_friendly_fire", data.friendlyFire, function( checked )
        data.friendlyFire = checked
    end )

    ffButton:DockMargin( separator, separator, separator, 0 )

    if SquadMenu.GetForceFriendlyFire() then
        ffButton:SetEnabled( false )
        ffButton:SetIcon( "icon16/accept.png" )
        ffButton:SetText( L"squad_force_friendly_fire" )
    end

    StyledTheme.CreateFormToggle( leftPanel, L"squad_rings", data.enableRings, function( checked )
        data.enableRings = checked
    end ):DockMargin( separator, separator, separator, 0 )

    local rightPanel = vgui.Create( "DPanel", propertiesPanel )
    rightPanel:SetWide( ScaleSize( 260 ) )
    rightPanel:Dock( RIGHT )
    rightPanel:DockMargin( separator, 0, 0, 0 )

    StyledTheme.Apply( rightPanel )
    StyledTheme.CreateFormHeader( rightPanel, L"squad_color", 0, 0 )

    local colorPicker = vgui.Create( "DColorMixer", rightPanel )
    colorPicker:Dock( FILL )
    colorPicker:SetPalette( true )
    colorPicker:SetAlphaBar( false )
    colorPicker:SetWangs( true )
    colorPicker:SetColor( oldColor )

    colorPicker.ValueChanged = function( _, color )
        data.r = color.r
        data.g = color.g
        data.b = color.b
    end
end
