<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="using:System.Collections.Generic"
        xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
        xmlns:views="using:DodoSSH.Client.App.Views"
        x:Class="DodoSSH.Client.App.Views.MainWindow"
        x:DataType="vm:MainWindowViewModel"
        Title="DodoSSH"
        Width="1180"
        Height="760"
        MinWidth="880"
        MinHeight="560"
        Background="{StaticResource Canvas}"
        SystemDecorations="BorderOnly"
        Focusable="True">

  <!--
    The shell window: a titlebar it draws itself, a nav rail, a tab strip, one surface at a time, and a
    status bar.

    Windows is asked for a resize border and nothing else, so TitleBar does the dragging, the maximising and
    the closing. That is a real cost, and the reason it is paid is that a stock grey system bar above a
    near-black application is the one part of the window that would look borrowed.

    SystemDecorations="BorderOnly" rather than extending the client area under the system chrome, which is
    what this tried first and is measurably wrong on Avalonia 12: ExtendClientAreaChromeHints is gone from
    that version, so extending leaves the caption buttons and the window title drawn on top of this
    window's own — two sets of minimise/maximise/close, and "DodoSSH" printed twice, overlapping. BorderOnly
    removes the caption and keeps the resize border and the drop shadow, which is the half of the system
    chrome worth having.

    TWO SURFACES, ONE RECTANGLE.

    The tab strip is above everything the nav rail leads to, so a terminal opened from any screen stays
    visible and reachable from every other one. What that costs is that the terminal and the pages now share
    the area beneath the strip, and exactly one of them may occupy it. That is the whole of ShellSurface: an
    enum rather than two flags, so there is no way to write the state where both are showing.

    THE OCCLUSION RULE, which every arrangement in this file obeys.

    NativeWebView hosts a real Win32 child window through NativeControlHost, and a child window composites
    above everything the parent paints — so no sibling in this visual tree can cover it, whatever the
    z-order says. Layering a screen over it leaves that screen sliced at the WebView's left edge with its
    buttons unreachable, which this window has shipped once already.

    So anything that would occupy the terminal's rectangle collapses the terminal instead, and
    IsTerminalShowing is the one place that decision is made: a locked vault, the page area, or the
    quick-connect palette. Collapsing is safe, and cheaply so — NativeControlHost creates the native
    control when the control is attached to the visual tree, not when it is laid out or shown, so WebView2
    still starts, still loads the page and still lets the renderer attach its socket while it is false. It
    only swaps ShowInBounds for HideWithSize, and flipping it back re-pushes the bounds.

    Note where IsShowingPages is bound: on the one Panel that holds every screen, not on each screen. That
    is what makes the rule hard to break rather than merely documented — a sixth screen added inside that
    Panel cannot forget to collapse, because it is not the thing doing the collapsing. Its own IsVisible
    only chooses between the pages.

    Two nearby alternatives are wrong. Removing the control from the tree instead — conditional content, a
    template swap — detaches it, and detaching destroys the native control and the whole WebView2 process
    tree, so every unlock would pay a cold start. Hoisting the binding to an ancestor looks tidier and is
    unverified: NativeControlHost does watch ancestors, but NativeWebView's own bounds-and-scaling re-push
    fires only for its own IsVisible.
  -->

  <Grid RowDefinitions="Auto,*,Auto">

    <views:TitleBar Grid.Row="0" />

    <Panel Grid.Row="1">

      <!-- The unlocked application. -->
      <Grid ColumnDefinitions="Auto,*" IsVisible="{Binding IsUnlocked}">

        <views:NavRail Grid.Column="0" />

        <!--
          The rail is full height and the strip is not, so the strip spans exactly the area it navigates.
          The other arrangement — strip above rail — would put a row of tabs over a column of destinations
          they have nothing to do with.
        -->
        <Grid Grid.Column="1" RowDefinitions="Auto,*">

          <views:TerminalTabs Grid.Row="0" />

          <Panel Grid.Row="1">

            <!-- ============ THE PAGES ============ -->
            <Panel IsVisible="{Binding IsShowingPages}">

              <!--
                Bound directly rather than wrapped, unlike the two below it: this screen's data context is
                the shell's, so IsHostsScreen resolves. It hands the vault to the sidebar from inside its
                own markup.
              -->
              <views:HostsScreen x:Name="HostsPane" IsVisible="{Binding IsHostsScreen}" />

              <!-- ============ FILES ============ -->
              <!--
                Wrapped rather than bound directly, for the same reason the vault screen is: this element's
                visibility is the shell's business and its data context is the transfers view model, and
                putting both on one element resolves IsVisible against that view model, where
                IsTransfersScreen does not exist.
              -->
              <Panel IsVisible="{Binding IsTransfersScreen}">
                <views:TransfersScreen DataContext="{Binding Transfers}" />
              </Panel>

              <!-- ============ KEYCHAIN ============ -->
              <!--
                Wrapped rather than bound directly, for the reason the vault column always was: this
                element's visibility is the shell's business and its data context is the vault, and put both
                on one element and IsVisible resolves against the vault as well, where IsVaultScreen does
                not exist.
              -->
              <Panel IsVisible="{Binding IsVaultScreen}">
                <views:VaultScreen x:Name="VaultPane" DataContext="{Binding Vault}" />
              </Panel>

              <!-- ============ HOST KEYS ============ -->
              <!--
                Wrapped, like the two above and for the same reason: its data context is the screen's own
                view model, where IsKnownHostsScreen does not exist.
              -->
              <Panel IsVisible="{Binding IsKnownHostsScreen}">
                <views:KnownHostsScreen x:Name="PinsPane" DataContext="{Binding KnownHostsScreen}" />
              </Panel>

              <!-- ============ SNIPPETS ============ -->
              <!-- Wrapped, like the others whose data context is their own view model. -->
              <Panel IsVisible="{Binding IsSnippetsScreen}">
                <views:SnippetsScreen x:Name="SnippetsPane" DataContext="{Binding SnippetsScreen}" />
              </Panel>

              <!-- ============ LOGS ============ -->
              <!-- Wrapped, like the others whose data context is their own view model. -->
              <Panel IsVisible="{Binding IsLogsScreen}">
                <views:LogsScreen x:Name="LogsPane" DataContext="{Binding LogsScreen}" />
              </Panel>

              <!-- ============ TEAM ============ -->
              <!--
                Wrapped, for the reason the vault and transfers screens are: the visibility is the shell's
                business and the data context is the teams view model, and both on one element would resolve
                IsTeamScreen against a type that does not have it.
              -->
              <Panel IsVisible="{Binding IsTeamScreen}">
                <views:TeamsScreen DataContext="{Binding Teams}" />
              </Panel>

              <!-- ============ PREFERENCES ============ -->
              <views:PreferencesScreen IsVisible="{Binding IsPreferencesScreen}" />

              <!-- ============ IMPORT ============ -->
              <!--
                Reached from preferences rather than from the rail; see ShellScreen.Import. Wrapped, like
                the others whose data context is their own view model.
              -->
              <Panel IsVisible="{Binding IsImportScreen}">
                <views:ImportScreen x:Name="ImportPane" DataContext="{Binding ImportScreen}" />
              </Panel>

            </Panel>

            <!--
              The other thing that can be in the terminal's rectangle: a tab whose session does not exist
              yet, or never will. Exclusive with the WebView below by construction — a selected tab either
              has a session or it does not — which is what makes drawing it here safe under the occlusion
              rule, the same way the page area is. See ConnectingCard.axaml.
            -->
            <views:ConnectingCard x:Name="ConnectingPane"
                                  IsVisible="{Binding IsConnectingShowing, FallbackValue=False}" />

            <!--
              One WebView hosting every terminal. Not one per tab: each WebView2 is a separate browser
              process tree, so twenty tabs would cost twenty of them.

              A sibling of the page area rather than a child of any screen, which is the structural half of
              the tab rework: the terminal belongs to the window now, not to the hosts screen.

              FallbackValue, because a compiled binding with no DataContext yields UnsetValue, IsVisible
              then falls back to its default of true, and the occlusion comes back silently. Not reachable
              at runtime — the DataContext is set before the window is shown — but it is what the previewer
              does.
            -->
            <NativeWebView x:Name="Terminal"
                           IsVisible="{Binding IsTerminalShowing, FallbackValue=False}" />

          </Panel>
        </Grid>
      </Grid>

      <!--
        Setup and unlock. Later in the Panel, so it is above the application content in Avalonia's z-order —
        which covers Avalonia-drawn content and nothing else. The terminal is collapsed rather than covered;
        see the occlusion rule above.
      -->
      <Border Background="{StaticResource Canvas}" IsVisible="{Binding !IsUnlocked}">
        <Panel>

          <Border Classes="card" IsVisible="{Binding IsStarting}">
            <StackPanel Spacing="10">
              <TextBlock Classes="heading" Text="DodoSSH" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <Border Classes="card" IsVisible="{Binding IsNeedingServer}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Connect to your server" />
              <TextBlock Classes="hint"
                         Text="One address is all this needs. The identity provider, the client id and the scopes all come from the server itself." />
              <!--
                Named because signing out lands here with the keyboard needing somewhere to go: the vault's
                controls have just been collapsed, and Focus() on a collapsed control is a no-op that is
                not replayed. See MainWindow.axaml.cs.
              -->
              <TextBox x:Name="ServerUrlBox" Text="{Binding ServerUrl}"
                       PlaceholderText="https://dodossh.example" />
              <Button Classes="accent" Content="SIGN IN WITH YOUR BROWSER" Command="{Binding SignInCommand}"
                      IsEnabled="{Binding !IsBusy}" HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <Border Classes="card" IsVisible="{Binding IsNeedingEnrollment}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Choose a keychain passphrase" />
              <TextBlock Classes="hint"
                         Text="This passphrase never leaves this machine, and the server cannot reset it. It is the only thing standing between a stolen copy of the database and every credential in your keychain." />
              <TextBox Text="{Binding Passphrase}" PlaceholderText="passphrase" PasswordChar="•" />
              <TextBox Text="{Binding ConfirmPassphrase}" PlaceholderText="again" PasswordChar="•" />
              <Button Classes="accent" Content="CREATE MY KEYCHAIN" Command="{Binding EnrollCommand}"
                      IsEnabled="{Binding !IsBusy}" HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <!--
            Shown once and impossible to skip. This is the only moment the code exists, and losing it
            together with the passphrase means the keychain is unrecoverable — there is no server-side reset
            by design.
          -->
          <Border Classes="card" IsVisible="{Binding IsShowingRecoveryCode}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Write this recovery code down" />
              <TextBlock Classes="hint"
                         Text="It is shown once and is not stored anywhere. Without it, forgetting your passphrase means losing the keychain: nobody — including whoever runs the server — can recover it for you." />
              <Border Background="{StaticResource Raised}" BorderBrush="{StaticResource Border}"
                      BorderThickness="1" CornerRadius="6" Padding="14">
                <SelectableTextBlock Classes="mono" Text="{Binding RecoveryCode}"
                                     FontSize="16" Foreground="{StaticResource Accent}"
                                     TextWrapping="Wrap" />
              </Border>
              <CheckBox IsChecked="{Binding RecoveryCodeWrittenDown}"
                        Content="I have written it down somewhere safe" />
              <Button Classes="accent" Content="CONTINUE" Command="{Binding ConfirmRecoveryCodeCommand}"
                      HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <!--
            The unlock card, and the sign-out confirmation that replaces it. Both live in their own files:
            nothing inside this window can be laid out by a test — WebView2's adapter refuses the headless
            session's thread — so markup that stays here is markup nobody can measure. See UnlockCard.axaml.
          -->
          <Border Classes="card" IsVisible="{Binding IsAskingForThePassphrase}">
            <views:UnlockCard x:Name="UnlockPane" />
          </Border>

          <!--
            The confirmation, in place of the unlock card rather than under it: the card is already close
            to the height the window guarantees at its minimum, and a screen a user cannot read all of is
            worse than one that shows one question at a time.
          -->
          <Border Classes="card" IsVisible="{Binding IsConfirmingSignOut}">
            <views:SignOutCard />
          </Border>

        </Panel>
      </Border>

      <!--
        Last, so it is over everything Avalonia draws. It is over the terminal too, because opening it
        collapses the terminal — see IsTerminalShowing.
      -->
      <views:QuickConnect x:Name="Palette" IsVisible="{Binding IsSearching}" />

    </Panel>

    <views:StatusBar Grid.Row="2" />

  </Grid>

</Window>
