<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
             xmlns:views="using:DodoSSH.Client.App.Views"
             x:Class="DodoSSH.Client.App.Views.HostSidebar"
             x:DataType="vm:VaultViewModel">

  <!--
    The host list, and the editor for whichever host is open.

    This was the top half of VaultColumn. The design gives hosts their own column beside the terminal and
    puts everything else in the vault screen, which is the split this control and VaultScreen are — and it
    is a better split than the one it replaces: the hosts list is the thing you look at while you work, and
    the keys and passwords behind it are the thing you go and manage.

    Its data context is the VaultViewModel, so every binding here is a property of the vault. The shell
    hands it over; see MainWindow.

    The editor stays in this column rather than moving into the terminal's half of the window, and that is
    an occlusion constraint rather than a preference: the terminal's WebView is a native child window that
    composites above anything Avalonia draws in the same rectangle, so a form laid over there would render
    underneath it with its buttons unclickable. It is also why this control is measurable at all — no part
    of it is the WebView, so the layout harness can lay it out headlessly.
  -->

  <Grid RowDefinitions="Auto,Auto,*,Auto,Auto" Background="{StaticResource Sidebar}">

    <!--
      The filter. It narrows this list and nothing else — the connect path, the selection and the pinned
      host key list all read the unfiltered collection — so a filter left in the box can hide a host but
      can never break one.
    -->
    <Border Grid.Row="0" Padding="10,8" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,0,0,1">
      <TextBox x:Name="HostFilter" Text="{Binding HostFilter}" PlaceholderText="filter hosts" FontSize="11"
               FontFamily="{StaticResource MonoFont}" MinHeight="26" Padding="8,3" />
    </Border>

    <!--
      One heading, which names the vault while there is one and says ALL VAULTS once a team's is readable
      too — a heading that went on naming the personal vault over a list containing a team's hosts would be
      a quiet lie, so the rows carry the vault name instead. The chevron folds the list away; the count is
      the collection's own, so it follows the filter without a second number to keep in step.
    -->
    <Button Grid.Row="1" Classes="flat grouphead" Command="{Binding ToggleHostsCommand}"
            HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
      <Grid ColumnDefinitions="Auto,Auto,*,Auto">
        <TextBlock Grid.Column="0" Text="▾" Foreground="{StaticResource TextFaint}" FontSize="8"
                   VerticalAlignment="Center" Margin="0,0,6,0"
                   IsVisible="{Binding AreHostsExpanded}" />
        <TextBlock Grid.Column="0" Text="▸" Foreground="{StaticResource TextFaint}" FontSize="8"
                   VerticalAlignment="Center" Margin="0,0,6,0"
                   IsVisible="{Binding !AreHostsExpanded}" />
        <TextBlock Grid.Column="1" Classes="label" Text="{Binding HostsHeading}"
                   Foreground="{StaticResource TextDim}" VerticalAlignment="Center" />
        <TextBlock Grid.Column="3" Classes="mono" Text="{Binding VisibleHosts.Count}" FontSize="10"
                   Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
      </Grid>
    </Button>

    <!--
      Named because it is where keyboard focus lands when the user leaves the terminal.

      Focusable, which a ListBox is not by default — Avalonia leaves focus to the items and an empty list has
      none. Without it the release-the-keyboard path is a measured no-op: it takes Win32 focus off the
      terminal's child window and then calls Focus() on something that refuses it, leaving the window with
      nothing focused and the keystrokes going nowhere.
    -->
    <ListBox Grid.Row="2" x:Name="HostList" Focusable="True"
             DragDrop.AllowDrop="True"
             IsVisible="{Binding AreHostsExpanded}"
             ItemsSource="{Binding SidebarRows}"
             SelectedItem="{Binding SelectedSidebarRow}">

      <!--
        The three things you can do to a host, on the host itself.

        On the list rather than in the item template, and that is what makes it one menu rather than one per
        row: a ContextMenu inside a DataTemplate would have the row for its data context, and the commands
        here are the vault's — the same three the buttons at the foot of this column run. The code-behind
        selects whatever was right-clicked before the menu opens, so the selection-based commands act on the
        row under the pointer, and cancels the menu outright over a group heading, which has no host to act
        on. See HostSidebar.axaml.cs.

        The same commands as the buttons, deliberately: a second path to deleting a host would be a second
        place for the confirmation to be forgotten.
      -->
      <ListBox.ContextMenu>
        <ContextMenu>
          <MenuItem Header="Connect" Command="{Binding ConnectCommand}" />
          <MenuItem Header="Edit…" Command="{Binding EditSelectedHostCommand}" />
          <Separator />
          <MenuItem Header="Delete…" Command="{Binding DeleteHostCommand}" />
        </ContextMenu>
      </ListBox.ContextMenu>

      <!--
        Two kinds of row in one list, chosen by type. It has to be one ListBox: it owns the selection and it
        is where keyboard focus lands when the terminal gives it back, neither of which survives a list per
        group. A vault with no groups produces no heading rows at all, so this is the list it always was.

        The heading is a row rather than a container, which means the control will happily select it. That is
        turned back into the previous host selection in the view model — see SelectedSidebarRow — because
        CONNECT, EDIT and DELETE all act on a host and a highlighted heading is not one.
      -->
      <ListBox.DataTemplates>

        <DataTemplate DataType="vm:SidebarGroupHeader">
          <Button Classes="flat grouphead" Command="{Binding $parent[ListBox].((vm:VaultViewModel)DataContext).ToggleGroupCommand}"
                  CommandParameter="{Binding}"
                  HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
            <Grid ColumnDefinitions="Auto,*,Auto">
              <TextBlock Grid.Column="0" Text="{Binding Chevron}" Foreground="{StaticResource TextFaint}"
                         FontSize="8" VerticalAlignment="Center" Margin="0,0,6,0" />
              <TextBlock Grid.Column="1" Classes="label" Text="{Binding Label}"
                         Foreground="{StaticResource TextDim}" VerticalAlignment="Center"
                         TextTrimming="CharacterEllipsis" />
              <TextBlock Grid.Column="2" Classes="mono" Text="{Binding Count}" FontSize="10"
                         Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
            </Grid>
          </Button>
        </DataTemplate>

        <DataTemplate DataType="vm:HostRowViewModel">
          <Grid ColumnDefinitions="Auto,Auto,*" Margin="0,5,10,5">

            <!-- The accent strip a selected row carries; see the style in App.axaml. -->
            <Border Grid.Column="0" Classes="rowmark" />

            <!--
              Connected, and nothing more. Green means a terminal is open on this host right now; grey means
              there is not one. It is deliberately not reachability — nothing here pings anything, and a dot
              that meant "up" would be a claim this application never checks.
            -->
            <Ellipse Grid.Column="1" Classes="dot" Classes.live="{Binding IsConnected}"
                     Margin="8,5,8,0" VerticalAlignment="Top" />

            <StackPanel Grid.Column="2" Spacing="1">
              <StackPanel Orientation="Horizontal" Spacing="6">
                <TextBlock Classes="mono" Text="{Binding Label}" Foreground="{StaticResource Text}"
                           FontSize="11.5" FontWeight="Medium"
                           TextTrimming="CharacterEllipsis" />
                <Border Classes="chip warn" Padding="4,0"
                        IsVisible="{Binding Badge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
                  <TextBlock Text="{Binding Badge}" FontSize="8.5" />
                </Border>
              </StackPanel>
              <StackPanel Orientation="Horizontal" Spacing="6">
                <TextBlock Classes="mono" Text="{Binding Address}" FontSize="9.5"
                           Foreground="{StaticResource TextFaint}" TextTrimming="CharacterEllipsis" />
                <!--
                  Which of the three ways this host authenticates. In the list because only one of them wants
                  the password box filled in, and an empty box on a key-authenticated host is otherwise
                  indistinguishable from one somebody forgot to fill in.
                -->
                <TextBlock Classes="mono" Text="{Binding Authentication}" FontSize="9.5"
                           Foreground="{StaticResource TextFaint}" />
                <!--
                  Which vault this host is in, and only when there is more than one to be in. It decides
                  who else can see the host and where an edit goes back to, so on a list that spans
                  several vaults it is not decoration.
                -->
                <TextBlock Classes="mono" Text="{Binding VaultBadge}" FontSize="9.5"
                           Foreground="{StaticResource TextFaint}"
                           IsVisible="{Binding HasVaultBadge}" />
              </StackPanel>
            </StackPanel>
          </Grid>
        </DataTemplate>

      </ListBox.DataTemplates>
    </ListBox>

    <!-- The editor doubles as the "add" form; there is no separate dialog. -->
    <Border Grid.Row="3" Padding="10" Background="{StaticResource Chrome}"
            BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0"
            IsVisible="{Binding IsEditing}">
      <StackPanel Spacing="6">
        <TextBox Text="{Binding EditorLabel}" PlaceholderText="name" />
        <TextBox Text="{Binding EditorHostname}" PlaceholderText="hostname or address" />
        <Grid ColumnDefinitions="*,8,*">
          <NumericUpDown Grid.Column="0" Value="{Binding EditorPort}" Minimum="1" Maximum="65535"
                         FormatString="0" ShowButtonSpinner="False" />
          <TextBox Grid.Column="2" Text="{Binding EditorUsername}" PlaceholderText="username" />
        </Grid>
        <TextBox Text="{Binding EditorNotes}" PlaceholderText="notes" AcceptsReturn="True"
                 Height="48" TextWrapping="Wrap" />
        <!--
          How this host authenticates: a typed password, one of the vault's keys, or one of its credentials.
          Part of the host rather than of the connection, so it follows the host to every machine; a host
          bound to something since deleted keeps a placeholder entry here, so that editing the port cannot
          quietly turn it back into a typed-password host.

          One control for all three, which is what makes "a key or a credential, never both" impossible to
          express rather than merely invalid. The qualifier beside each label is not decoration: a key called
          "deploy" and the deploy account's password are the ordinary case, and bare labels would offer two
          identical-looking entries that authenticate completely differently.
        -->
        <ComboBox ItemsSource="{Binding EditorAuthenticationChoices}"
                  SelectedItem="{Binding EditorSelectedAuthentication}"
                  HorizontalAlignment="Stretch">
          <ComboBox.ItemTemplate>
            <DataTemplate x:DataType="vm:AuthenticationChoice">
              <StackPanel Orientation="Horizontal" Spacing="6">
                <TextBlock Text="{Binding Label}" />
                <TextBlock Text="{Binding Qualifier}" Classes="hint" FontSize="10"
                           VerticalAlignment="Center"
                           IsVisible="{Binding Qualifier, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
              </StackPanel>
            </DataTemplate>
          </ComboBox.ItemTemplate>
        </ComboBox>
        <!--
          Which group this host is filed under. Inside the encrypted payload like everything else here, so
          the server learns nothing about how the estate is organised — and a group the vault no longer has
          keeps a placeholder entry, so that editing the port cannot quietly unfile the host.
        -->
        <ComboBox ItemsSource="{Binding EditorGroupChoices}"
                  SelectedItem="{Binding EditorSelectedGroup}"
                  HorizontalAlignment="Stretch">
          <ComboBox.ItemTemplate>
            <DataTemplate x:DataType="vm:GroupChoice">
              <TextBlock Text="{Binding Label}" />
            </DataTemplate>
          </ComboBox.ItemTemplate>
        </ComboBox>
        <CheckBox IsChecked="{Binding EditorRelayEnabled}"
                  Content="Connect through the server relay" />
        <!--
          Stated at the moment the decision is made, which is the only place it means anything. With
          relay off the server stores no address at all; with it on the server must be able to resolve
          the target, or it becomes an authenticated open proxy into the operator's network.
        -->
        <TextBlock Classes="hint" FontSize="10"
                   Text="The relay stores this host's address on the server in plain text. Everything else stays encrypted." />
        <StackPanel Orientation="Horizontal" Spacing="6">
          <Button Classes="accent" Content="SAVE" Command="{Binding SaveHostCommand}" />
          <Button Classes="ghost" Content="CANCEL" Command="{Binding CancelEditCommand}" />
        </StackPanel>
        <!--
          Withdrawing host key trust lives here, in the host's own settings, because a changed host key
          is refused outright with no way to continue past it — so a legitimately rebuilt server needs
          somewhere deliberate to be re-approved from, and that somewhere must not be the warning
          itself. It takes effect when clicked rather than on Save, and the status line says so; it is
          not a field of the host.
        -->
        <Button Classes="danger" Content="FORGET HOST KEY" HorizontalAlignment="Left"
                Command="{Binding ForgetHostKeyCommand}"
                IsVisible="{Binding CanForgetHostKey}"
                ToolTip.Tip="Removes the pinned key for this host's address, so the next connection asks you to check its fingerprint again." />
      </StackPanel>
    </Border>

    <Border Grid.Row="4" Padding="10,8" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0"
            IsVisible="{Binding ShowsHostActions}">
      <StackPanel Orientation="Horizontal" Spacing="6">
        <Button Classes="ghost" Content="+ NEW HOST" Command="{Binding NewHostCommand}" />
        <Button Classes="ghost" Content="EDIT" Command="{Binding EditSelectedHostCommand}" />
        <Button Classes="ghost" Content="DELETE" Command="{Binding DeleteHostCommand}" />
      </StackPanel>
    </Border>

    <!--
      The question DELETE asks, in the place the buttons were rather than under them. This strip is at the
      bottom edge of a column whose middle is a list that has already taken every spare pixel, so a second
      block below the first would push its own buttons off the window — the same reasoning that swaps the
      unlock card for the sign-out card rather than stacking them. Swapping also means DELETE cannot be
      pressed again while its own question is up; see VaultViewModel.ShowsHostActions.
    -->
    <Border Grid.Row="4" Padding="10,8" Background="{StaticResource DangerWash}"
            BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0"
            IsVisible="{Binding IsConfirmingHostDeletion}">
      <views:ConfirmDeleteCard />
    </Border>

  </Grid>

</UserControl>
