Editor/Dock/ControlsCard.cs
using System;
using Editor;

namespace TeamCreate;

public sealed class ControlsCard : DockCard
{
    private readonly ConnectCard connect;

    private readonly Label message;

    private readonly Button save;

    private readonly Button undo;

    private readonly Button recover;

    private readonly Checkbox autoSave;

    private readonly Checkbox autoRecover;

    private SessionState lastState = (SessionState)(-1);

    private bool lastConnected;

    private bool lastBusy;

    private PlayModeLifecycleState lastLifecycleState = (PlayModeLifecycleState)(-1);

    private string lastTerminal;

    private string lastError;

    private string lastRecovery;

    private string lastPath;

    private string lastSaveNotice;

    private string lastUndoNotice;

    private string lastAction;

    protected override bool FieldsAssigned => connect != null && message != null && save != null && undo != null && recover != null && autoSave != null && autoRecover != null;

    public ControlsCard(Widget parent, ConnectCard connect)
        : base(parent, "Controls")
    {
        ControlsCard controlsCard = this;
        this.connect = connect;
        message = Layout.Add(new Label("", this)
        {
            WordWrap = true,
            Color = DockTheme.BlockedColor
        });
        message.Visible = false;
        save = Layout.Add(new Button("Save synchronized scene", this)
        {
            Clicked = () =>
            {
                DockCard.Run(() =>
                {
                    CollaborationSession.Current.Save();
                });
            }
        });
        undo = Layout.Add(new Button("Undo my last collaboration operation", this)
        {
            Clicked = () =>
            {
                DockCard.Run(() =>
                {
                    CollaborationSession.Current.Undo();
                });
            }
        });
        recover = Layout.Add(new Button("Recover & reconnect", this)
        {
            ToolTip = "Archive local edits, reload the saved scenes from disk, then accept the host's current state.",
            Clicked = () =>
            {
                DockCard.Run(() =>
                {
                    CollaborationSession.Current.Reconnect(connect.Address, connect.PortNumber);
                });
            }
        });
        autoSave = Layout.Add(new Checkbox
        {
            Text = "Auto-save when idle",
            ToolTip = "Save the synchronized scene automatically once it has gone 30 seconds without a local edit, while no collaborator is reported editing and the editor is not in play mode, at most once every 30 seconds. Off by default."
        });
        Checkbox checkbox = autoSave;
        checkbox.Toggled = (Action)Delegate.Combine(checkbox.Toggled, (Action)(() =>
        {
            CollaborationSession.Current.AutoSaveIdle = controlsCard.autoSave.Value;
        }));
        autoRecover = Layout.Add(new Checkbox
        {
            Text = "Automatic recovery",
            ToolTip = "Automatic recovery reloads scenes from disk; unsaved local changes are archived first. Off by default."
        });
        Checkbox checkbox2 = autoRecover;
        checkbox2.Toggled = (Action)Delegate.Combine(checkbox2.Toggled, (Action)(() =>
        {
            CollaborationSession.Current.AutoRecover = controlsCard.autoRecover.Value;
        }));
    }

    /// <summary>True while the card is showing a problem (an error or a recovery notice), so the dock keeps the card on screen after a session ends until it has been read. A line that only reports a save or an undo does not count.</summary>
    public bool HasMessage => message != null && message.Visible && problem;

    private bool problem;

    private static void SetEnabled(Button button, bool enabled)
    {
        if (button.Enabled != enabled)
        {
            button.Enabled = enabled;
        }
    }

    public void Refresh()
    {
        CollaborationSession current = CollaborationSession.Current;
        string terminalTeardownError = current.TerminalTeardownError;
        string error = current.Error;
        string recoveryNotice = current.RecoveryNotice;
        string recoveryPath = current.RecoveryPath;
        string saveNotice = current.SaveNotice;
        string undoNotice = current.UndoNotice;
        string actionNotice = current.ActionNotice;
        SessionState state = current.State;
        bool connected = current.Connected;
        bool busy = current.Busy;
        PlayModeLifecycleState state2 = current.RuntimeLifecycle.State;
        if (autoSave.Value != current.AutoSaveIdle)
        {
            autoSave.Value = current.AutoSaveIdle;
        }
        if (autoRecover.Value != current.AutoRecover)
        {
            autoRecover.Value = current.AutoRecover;
        }
        bool flag = state != lastState || connected != lastConnected || busy != lastBusy || state2 != lastLifecycleState || !string.Equals(terminalTeardownError, lastTerminal, StringComparison.Ordinal) || !string.Equals(error, lastError, StringComparison.Ordinal);
        if (!string.Equals(terminalTeardownError, lastTerminal, StringComparison.Ordinal) || !string.Equals(error, lastError, StringComparison.Ordinal) || !string.Equals(recoveryNotice, lastRecovery, StringComparison.Ordinal) || !string.Equals(recoveryPath, lastPath, StringComparison.Ordinal) || !string.Equals(saveNotice, lastSaveNotice, StringComparison.Ordinal) || !string.Equals(undoNotice, lastUndoNotice, StringComparison.Ordinal) || !string.Equals(actionNotice, lastAction, StringComparison.Ordinal))
        {
            lastTerminal = terminalTeardownError;
            lastError = error;
            lastRecovery = recoveryNotice;
            lastPath = recoveryPath;
            lastSaveNotice = saveNotice;
            lastUndoNotice = undoNotice;
            lastAction = actionNotice;
            DockCard.SetText(message, ControlsMessageRules.Compose(terminalTeardownError, error, recoveryPath, recoveryNotice, actionNotice, undoNotice, saveNotice));
            message.Visible = message.Text.Length > 0;
            // Red is for something that went wrong. A line that only says what was done is shown quietly.
            problem = ControlsMessageRules.IsProblem(terminalTeardownError, error, recoveryNotice);
            message.Color = problem ? DockTheme.BlockedColor : DockTheme.MutedColor;
        }
        if (flag)
        {
            lastState = state;
            lastConnected = connected;
            lastBusy = busy;
            lastLifecycleState = state2;
            bool flag2 = connected;
            bool flag3 = flag2;
            if (flag3)
            {
                bool flag4 = ((state == SessionState.Hosting || state == SessionState.Connected) ? true : false);
                flag3 = flag4;
            }
            bool enabled = flag3;
            SetEnabled(save, enabled);
            SetEnabled(undo, enabled);
            Button button = recover;
            bool flag5 = error != null && terminalTeardownError == null && !current.RuntimeLifecycle.HasTerminalTeardownFailure;
            bool flag6 = flag5;
            if (flag6)
            {
                bool flag4 = ((state2 == PlayModeLifecycleState.RuntimeStarting || state2 == PlayModeLifecycleState.RuntimeBlocked) ? true : false);
                flag6 = !flag4;
            }
            SetEnabled(button, flag6);
        }
    }
}