Editor/Dock/SessionRosterCard.cs
using System;
using System.Collections.Generic;
using Editor;
using Sandbox;

namespace TeamCreate;

public sealed class SessionRosterCard : DockCard
{
    private sealed class PeerRow : Widget
    {
        private readonly Packet peer;

        private readonly string key;

        private readonly Button focus;

        public PeerRow(Widget parent, string key, Packet peer, string role)
            : base(parent)
        {
            this.peer = peer;
            this.key = key;
            FixedHeight = 40f;
            Layout = Layout.Row();
            Layout.Spacing = 4f;
            Layout.Add(new PeerBody(this, peer, role), 1);
            focus = Layout.Add(new Button(this)
            {
                Icon = "center_focus_strong"
            });
            focus.FixedWidth = 26f;
            focus.Clicked = () =>
            {
                DockCard.Run(() =>
                {
                    CollaborationSession.Current.FocusCamera(key);
                });
            };
        }

        public void SyncFocus(string localScene)
        {
            bool flag = !string.IsNullOrEmpty(localScene) && string.Equals(peer.Scene, localScene, StringComparison.Ordinal);
            string text;
            if (string.IsNullOrWhiteSpace(peer.Scene))
            {
                text = "no saved scene";
            }
            else
            {
                text = ((peer.Scene.Length <= 96) ? peer.Scene : peer.Scene.Substring(0, 96));
            }
            string text2 = (flag ? "Move my camera to theirs." : ("Move my camera to theirs — unavailable: that collaborator is in " + text + ". Open the scene first."));
            if (focus.Enabled != flag || !string.Equals(focus.ToolTip, text2, StringComparison.Ordinal))
            {
                focus.Enabled = flag;
                focus.ToolTip = text2;
            }
        }
    }

    private sealed class PeerBody : Widget
    {
        private readonly Packet peer;

        private readonly string role;

        public PeerBody(Widget parent, Packet peer, string role)
            : base(parent)
        {
            this.peer = peer;
            this.role = role;
            ToolTip = BuildToolTip();
            OnPaintOverride = PaintRow;
        }

        private string BuildToolTip()
        {
            string text = (string.IsNullOrWhiteSpace(peer.Name) ? "(unknown name)" : peer.Name);
            string text2 = text + "\nSteamId: " + (string.IsNullOrWhiteSpace(peer.SteamId) ? "not reported" : peer.SteamId);
            text2 = text2 + "\nRole: " + ((role == "—") ? "not reported by this build" : role);
            return text2 + "\nActive scene: " + (peer.Scene ?? "No saved scene");
        }

        private bool PaintRow()
        {
            Rect rect = LocalRect;
            if (Paint.HasMouseOver)
            {
                Paint.SetBrush(Theme.Text.WithAlpha(0.05f));
                Paint.DrawRect(in rect, 4f);
            }
            Rect rect2 = new Rect(rect.Left + 4f, rect.Top + (rect.Height - 32f) * 0.5f, 32f, 32f);
            DockTheme.DrawAvatar(rect2, peer.SteamId, peer.Name, inSession: true, online: true, notFriend: false);
            float width = ((role == "—") ? 18f : (Paint.MeasureText(role).x + 14f));
            float x = rect.Left + 32f + 14f;
            float width2 = MathF.Max(0f, rect.Right - x - width - 16f);
            float height = (rect.Height - 6f) * 0.5f;
            string text = (string.IsNullOrWhiteSpace(peer.Name) ? "(unknown name)" : peer.Name);
            Paint.SetPen(in Theme.Text, 0f, PenStyle.Solid);
            Paint.DrawText(new Rect(in x, rect.Top + 3f, in width2, in height), Paint.GetElidedText(text, width2), TextFlag.LeftCenter | TextFlag.SingleLine);
            string text2 = peer.Scene ?? "No saved scene";
            Paint.SetPen(Theme.Text.WithAlpha(0.6f), 0f, PenStyle.Solid);
            Paint.DrawText(new Rect(in x, rect.Top + 3f + height, in width2, in height), Paint.GetElidedText(text2, width2), TextFlag.LeftCenter | TextFlag.SingleLine);
            Rect rect3 = new Rect(rect.Right - width - 6f, rect.Top + (rect.Height - height - 4f) * 0.5f, in width, height + 4f);
            Paint.SetBrushAndPen(Theme.Text.WithAlpha(0.1f), Color.Transparent, 0f);
            Paint.DrawRect(in rect3, 4f);
            Paint.SetPen((role == "—") ? DockTheme.MutedColor : Theme.Text.WithAlpha(0.85f), 0f, PenStyle.Solid);
            Paint.DrawText(in rect3, role, TextFlag.Center | TextFlag.SingleLine);
            return false;
        }
    }

    private const string FocusIcon = "center_focus_strong";

    private const int ScenePathLimit = 96;

    private readonly Widget rows;

    private readonly Label notice;

    private readonly List<PeerRow> peerRows = new List<PeerRow>();

    private int lastFingerprint = int.MinValue;

    protected override bool FieldsAssigned => rows != null && notice != null && peerRows != null;

    public SessionRosterCard(Widget parent)
        : base(parent, "This session")
    {
        notice = Layout.Add(new Label("", this)
        {
            WordWrap = true,
            Color = DockTheme.WarningColor
        });
        notice.Visible = false;
        rows = Layout.Add(new Widget(this));
        rows.Layout = Layout.Column();
        rows.Layout.Spacing = 4f;
        Refresh();
    }

    public void Refresh()
    {
        CollaborationSession current = CollaborationSession.Current;
        int num = MembershipFingerprint(current);
        if (num != lastFingerprint)
        {
            Rebuild(current);
        }
        string text = current.FocusNotice ?? string.Empty;
        DockCard.SetText(notice, text);
        bool flag = text.Length > 0;
        if (notice.Visible != flag)
        {
            notice.Visible = flag;
        }
        if (peerRows.Count == 0)
        {
            return;
        }
        string localScene = SceneAdapter.PathOf(SceneEditorSession.Active);
        foreach (PeerRow peerRow in peerRows)
        {
            peerRow.SyncFocus(localScene);
        }
    }

    private static int MembershipFingerprint(CollaborationSession session)
    {
        int num = session.Roster.Count;
        foreach (KeyValuePair<string, Packet> item in session.Roster)
        {
            num = num * 31 + item.Key.GetHashCode();
        }
        return num;
    }

    private void Rebuild(CollaborationSession session)
    {
        lastFingerprint = MembershipFingerprint(session);
        peerRows.Clear();
        rows.Layout.Clear(deleteWidgets: true);
        DockCard.SetText(Title, "This session (" + (session.Roster.Count + 1) + ")");
        if (session.Roster.Count == 0)
        {
            rows.Layout.Add(new Label("No one else is in this session yet.", this)
            {
                Color = DockTheme.MutedColor
            });
            return;
        }
        foreach (KeyValuePair<string, Packet> item in session.Roster)
        {
            PeerRow peerRow = new PeerRow(rows, item.Key, item.Value, RoleOf(session, item.Value));
            peerRows.Add(peerRow);
            rows.Layout.Add(peerRow);
        }
    }

    private static string RoleOf(CollaborationSession session, Packet peer)
    {
        if (session.IsHost)
        {
            return "Guest";
        }
        return (session.Roster.Count == 1) ? "Host" : "—";
    }
}