<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.HostsScreen"
             x:DataType="vm:VaultViewModel">

  <!--
    The hosts screen: every group, then every host, as cards — and a drawer for whichever one is selected.

    ── IT WAS A 268-PIXEL LIST BESIDE A MOSTLY EMPTY COLUMN. ────────────────────────────────────────────
    That list was doing two jobs at two-thirds size. It had to be narrow so the editor beneath it could be
    a column, and being narrow is what made forty machines a scroll rather than a glance: a host row was a
    name, an address and an auth word stacked in 268 pixels, and the column beside it repeated all three
    for the one that happened to be selected.

    Cards split the two jobs. The grid is about all of them — how many groups there are, which machines are
    in one, which have a shell open — and it gets the full width to say so. The drawer is about one, and it
    is only there when there is one. See HostDrawer and VaultViewModel.IsDrawerOpen.

    ── WHAT IS DELIBERATELY NOT HERE ────────────────────────────────────────────────────────────────────
    The design this was drawn from puts a view-mode switch, a tag filter, a calendar and a share control in
    the toolbar, and offers Serial beside Terminal. None of the five exists: there is one view, no saved
    filters, nothing dated, no sharing outside a team vault, and no serial transport in the SSH layer. Five
    disabled controls would teach nobody anything. See docs/design-import-gaps.md.

    ── ITS DATA CONTEXT IS THE VAULT, and it used to be the shell. ──────────────────────────────────────
    The shell was needed only so that the old sidebar could be handed the vault on its own element while
    everything around it bound Vault.*. The drawer needs no such thing — it is a child of this control and
    inherits what this control has — so the indirection went with the sidebar, and every binding in this
    file is now a property of the vault.

    That is worth more than the tidiness: a control whose data context is the shell cannot be laid out by
    the layout harness without building an entire MainWindowViewModel, and this is the screen with the most
    worth measuring. The window wraps it in a Panel that carries IsHostsScreen, which is the same pattern
    every other screen there already uses — see MainWindow.axaml's note on why the two cannot go on one
    element.
  -->

  <Grid ColumnDefinitions="*,Auto">

    <Grid Grid.Column="0" RowDefinitions="Auto,Auto,Auto,*">

      <!--
        ============ FIND ============
        One wide box, which is what the width bought. It narrows the grid below 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.

        Ctrl+K is named on it because the palette is the other way to reach a host by typing, and somebody
        who has found this box should know about the one that also connects on Enter.

        The connect bar that used to be this row — password box, REMEMBER, the note, CONNECT — is in the
        drawer now, beside the host it is about. See HostDrawer.
      -->
      <Border Grid.Row="0" Padding="16,12" Background="{StaticResource Panel}"
              BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,0,0,1">
        <TextBox x:Name="HostFilter" Text="{Binding HostFilter}"
                 FontFamily="{StaticResource MonoFont}"
                 PlaceholderText="Find a host by name, address or note…   ·   Ctrl+K searches and connects" />
      </Border>

      <!--
        ============ THE TOOLBAR ============
        Only what makes something new. EDIT and DELETE are not here: they act on one host, and one host is
        what the drawer is about — a pair of buttons over a grid of forty is a pair whose subject the user
        has to work out. The group's own EDIT and DELETE sit beside the group cards for the same reason.
      -->
      <Border Grid.Row="1" Padding="16,8" BorderBrush="{StaticResource BorderSubtle}"
              BorderThickness="0,0,0,1">
        <StackPanel Orientation="Horizontal" Spacing="6">
          <Button Classes="accent" Content="+ NEW HOST" Command="{Binding NewHostCommand}" />
          <Button Classes="ghost" Content="+ NEW GROUP" Command="{Binding NewGroupCommand}"
                  ToolTip.Tip="A heading for this grid, and the port, username and key the hosts under it inherit." />
        </StackPanel>
      </Border>

      <StackPanel Grid.Row="2">

        <!--
          Host key prompts. Unknown and changed look deliberately different: one is a decision, the other is
          a refusal. Presenting a changed key with a "continue" button is how users are taught to click
          through the one warning that matters.
        -->
        <Border Padding="16,10" Background="{StaticResource WarnWash}"
                BorderBrush="{StaticResource WarnSoft}" BorderThickness="0,0,0,1"
                IsVisible="{Binding HasPendingHostKey}">
          <StackPanel Spacing="6">
            <TextBlock Text="This host has not been seen before. Check the fingerprint against what the server's operator published."
                       Foreground="{StaticResource WarnText}" TextWrapping="Wrap" />
            <SelectableTextBlock Classes="mono" Text="{Binding PendingHostKey.Fingerprint}"
                                 Foreground="{StaticResource Warn}" TextWrapping="Wrap" />
            <StackPanel Orientation="Horizontal" Spacing="8">
              <Button Classes="accent" Content="TRUST AND CONNECT"
                      Command="{Binding TrustHostKeyCommand}" />
              <Button Classes="ghost" Content="CANCEL"
                      Command="{Binding RejectHostKeyCommand}" />
            </StackPanel>
          </StackPanel>
        </Border>

        <Border Padding="16,10" Background="{StaticResource DangerWash}"
                BorderBrush="{StaticResource DangerSoft}" BorderThickness="0,0,0,1"
                IsVisible="{Binding HasHostKeyMismatch}">
          <StackPanel Spacing="6">
            <TextBlock Text="The host key changed and the connection was refused."
                       Foreground="{StaticResource Danger}" FontWeight="SemiBold" />
            <SelectableTextBlock Text="{Binding HostKeyMismatch}"
                                 Foreground="{StaticResource Danger}" TextWrapping="Wrap" />
            <TextBlock Text="If the server was legitimately rebuilt, edit the host and choose &quot;Forget host key&quot; first. There is deliberately no way to continue from here."
                       Foreground="{StaticResource WarnText}" TextWrapping="Wrap" />
          </StackPanel>
        </Border>

        <!--
          The conflict log. The merge is only allowed to pick a winner because the value it overrode is kept
          and shown; without this panel it would be last-writer-wins with a longer explanation.

          Bounded and scrollable. It sits on an Auto row above a star row, and an ItemsControl with no
          ceiling grows without limit — so a pass that merged twenty items would push the grid off the
          bottom of a screen nobody could scroll.
        -->
        <Border Padding="16,10" Background="{StaticResource Panel}"
                BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1"
                IsVisible="{Binding HasConflicts}">
          <StackPanel Spacing="6">
            <TextBlock Text="Some changes could not be merged automatically."
                       Foreground="{StaticResource Info}" FontWeight="SemiBold" />
            <ScrollViewer MaxHeight="180" HorizontalScrollBarVisibility="Disabled">
              <ItemsControl ItemsSource="{Binding Conflicts}">
                <ItemsControl.ItemTemplate>
                  <DataTemplate x:DataType="vm:ConflictRowViewModel">
                    <Border Margin="0,4" Padding="8" Background="{StaticResource Raised}"
                            CornerRadius="4">
                      <StackPanel Spacing="4">
                        <TextBlock Text="{Binding Summary}" Foreground="{StaticResource Text}"
                                   TextWrapping="Wrap" />
                        <SelectableTextBlock Classes="mono" Text="{Binding Detail}" FontSize="12"
                                             Foreground="{StaticResource TextDim}"
                                             IsVisible="{Binding HasDetail}" />
                      </StackPanel>
                    </Border>
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
            </ScrollViewer>
            <Button Classes="ghost" Content="DISMISS ALL" HorizontalAlignment="Left"
                    Command="{Binding AcknowledgeAllConflictsCommand}" />
          </StackPanel>
        </Border>

      </StackPanel>

      <!-- ============ THE GRID ============ -->
      <ScrollViewer Grid.Row="3" HorizontalScrollBarVisibility="Disabled">
        <StackPanel Margin="16,14" Spacing="16">

          <!-- ============ GROUPS ============ -->
          <!--
            Absent entirely from a vault nobody has filed anything in, which is what makes groups cost
            nothing to ignore — the same rule the host list has always followed for its headings.
          -->
          <StackPanel Spacing="8" IsVisible="{Binding HasGroups}">

            <Grid ColumnDefinitions="Auto,*,Auto">
              <TextBlock Grid.Column="0" Classes="label" Text="GROUPS"
                         Foreground="{StaticResource TextDim}" VerticalAlignment="Center" />

              <!--
                The group's own two actions, beside the group cards rather than in the toolbar, because they
                act on the selected card and this is where the selection is made. Hidden rather than
                disabled while DELETE's question is up, as every other pair in this application is, so it
                cannot be pressed twice — see VaultViewModel.ShowsGroupActions.
              -->
              <StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="6">
                <Button Classes="ghost" Content="SHOW ALL"
                        Command="{Binding ClearGroupFilterCommand}"
                        IsVisible="{Binding IsFilteredByGroup}"
                        ToolTip.Tip="Stops narrowing the grid to one group." />
                <Button Classes="ghost" Content="EDIT" Command="{Binding EditGroupCommand}"
                        IsVisible="{Binding ShowsGroupActions}" />
                <Button Classes="ghost" Content="DELETE" Command="{Binding DeleteGroupCommand}"
                        IsVisible="{Binding ShowsGroupActions}" />
              </StackPanel>
            </Grid>

            <Border Padding="10" Background="{StaticResource DangerWash}" CornerRadius="6"
                    IsVisible="{Binding IsConfirmingGroupDeletion}">
              <views:ConfirmDeleteCard />
            </Border>

            <!--
              A ListBox rather than an ItemsControl of buttons, and that is what marks the chosen group for
              free: selection is a state the control already has, and the tile style draws it the same way
              every other list in this application draws a selected row. Buttons would have needed a
              Classes.active binding per card and a second copy of "which one".

              SelectedItem is GroupFilter and not SelectedGroup. The two are nearly the same thing here —
              GroupFilter assigns SelectedGroup — but only one of them may narrow the grid; see the property
              for why the phone's own use of SelectedGroup must not.
            -->
            <ListBox Classes="tiles" x:Name="GroupGrid"
                     ItemsSource="{Binding Groups}"
                     SelectedItem="{Binding GroupFilter}">
              <ListBox.ItemsPanel>
                <ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
              </ListBox.ItemsPanel>
              <ListBox.ItemTemplate>
                <DataTemplate x:DataType="vm:HostGroupRowViewModel">
                  <Border Classes="tile">
                    <Grid ColumnDefinitions="Auto,*">
                      <Border Grid.Column="0" Classes="tileicon" Background="{StaticResource Accent}">
                        <TextBlock Text="▤" FontSize="14" Foreground="{StaticResource AccentInk}"
                                   HorizontalAlignment="Center" VerticalAlignment="Center" />
                      </Border>
                      <StackPanel Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center"
                                  Spacing="2">
                        <!-- A Grid rather than a horizontal StackPanel; see the note on the host card. -->
                        <Grid ColumnDefinitions="*,Auto">
                          <TextBlock Grid.Column="0" Text="{Binding Label}" FontWeight="Medium"
                                     Foreground="{StaticResource Text}"
                                     TextTrimming="CharacterEllipsis" />
                          <Border Grid.Column="1" Classes="chip warn" Padding="4,0" Margin="6,0,0,0"
                                  IsVisible="{Binding Badge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
                            <TextBlock Text="{Binding Badge}" FontSize="9.5" />
                          </Border>
                        </Grid>
                        <TextBlock Text="{Binding Description}" FontSize="11"
                                   Foreground="{StaticResource TextFaint}" />
                      </StackPanel>
                    </Grid>
                  </Border>
                </DataTemplate>
              </ListBox.ItemTemplate>
            </ListBox>

          </StackPanel>

          <!-- ============ HOSTS ============ -->
          <StackPanel Spacing="8">

            <Grid ColumnDefinitions="Auto,*,Auto">
              <TextBlock Grid.Column="0" Classes="label" Text="HOSTS"
                         Foreground="{StaticResource TextDim}" VerticalAlignment="Center" />
              <!-- The collection's own count, so it follows both filters with no second number to keep in step. -->
              <TextBlock Grid.Column="2" Classes="mono" Text="{Binding VisibleHosts.Count}"
                         FontSize="11" Foreground="{StaticResource TextFaint}"
                         VerticalAlignment="Center" />
            </Grid>

            <!--
              Named because it is where keyboard focus lands when the terminal gives it back, and because
              every gesture on it is wired in the code-behind.

              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.

              It binds SidebarRows rather than VisibleHosts, so the group headings are still in it. Two
              reasons, and neither is decoration: a grid showing every group at once has to say which cards
              belong to which, and a heading is the drop target that takes a host out of one group and into
              another. See VaultViewModel.RebuildSidebarRows for why a heading is a row rather than a
              container, and the code-behind for what a drag does with one.
            -->
            <ListBox Classes="tiles" x:Name="HostGrid" Focusable="True"
                     DragDrop.AllowDrop="True"
                     ItemsSource="{Binding SidebarRows}"
                     SelectedItem="{Binding SelectedSidebarRow}">

              <ListBox.ItemsPanel>
                <ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
              </ListBox.ItemsPanel>

              <!--
                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 card: a ContextMenu inside a DataTemplate would have the row for its data context,
                and the commands here are the vault's. The code-behind selects whatever was right-clicked
                before the menu opens, so the selection-based commands act on the card under the pointer,
                and cancels the menu outright over a group heading, which has no host to act on.
              -->
              <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>

              <ListBox.DataTemplates>

                <!--
                  A heading, and it takes a whole row of the wrap rather than sitting in the flow as another
                  card: it names the run of cards under it, and a heading the width of one card would read
                  as the first of them. The stretch is what MinWidth buys — a WrapPanel gives a child the
                  width it asks for and starts a new line when the line cannot hold it, so a child asking
                  for more than one line holds is alone on its own.
                -->
                <DataTemplate DataType="vm:SidebarGroupHeader">
                  <Button Classes="flat grouphead" MinWidth="640"
                          Command="{Binding $parent[ListBox].((vm:VaultViewModel)DataContext).ToggleGroupCommand}"
                          CommandParameter="{Binding}"
                          HorizontalContentAlignment="Stretch">
                    <Grid ColumnDefinitions="Auto,Auto,*">
                      <TextBlock Grid.Column="0" Text="{Binding Chevron}"
                                 Foreground="{StaticResource TextFaint}" FontSize="9"
                                 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="11"
                                 Foreground="{StaticResource TextFaint}" VerticalAlignment="Center"
                                 Margin="8,0,0,0" HorizontalAlignment="Left" />
                    </Grid>
                  </Button>
                </DataTemplate>

                <DataTemplate DataType="vm:HostRowViewModel">
                  <Border Classes="tile">
                    <StackPanel Spacing="6">
                      <Grid ColumnDefinitions="Auto,*,Auto">

                        <Border Grid.Column="0" Classes="tileicon" Background="{StaticResource Chip}">
                          <TextBlock Classes="mono" Text="&gt;_" FontSize="11" FontWeight="Bold"
                                     Foreground="{StaticResource AccentText}"
                                     HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Border>

                        <StackPanel Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center"
                                    Spacing="2">
                          <!--
                            ◆ A GRID AND NOT A HORIZONTAL StackPanel, on both of these rows, and the two
                            look interchangeable until the text is too long for the card.

                            A horizontal StackPanel measures every child with infinite width, so a
                            TextBlock inside one never learns it is short of room and TextTrimming never
                            fires — the text simply runs on past the card's border and over the card beside
                            it. The card is a fixed 248 and a host name is not, so that is the ordinary
                            case rather than the edge one.

                            The star column is the part that gives way and the Auto column is the part that
                            must not: a badge saying "not synced" and the word that says how a host
                            authenticates are both short and both meaningless trimmed, where a hostname
                            with its tail cut is still the machine you were looking for.
                          -->
                          <Grid ColumnDefinitions="*,Auto">
                            <TextBlock Grid.Column="0" Text="{Binding Label}" FontWeight="Medium"
                                       Foreground="{StaticResource Text}"
                                       TextTrimming="CharacterEllipsis" />
                            <Border Grid.Column="1" Classes="chip warn" Padding="4,0" Margin="6,0,0,0"
                                    IsVisible="{Binding Badge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
                              <TextBlock Text="{Binding Badge}" FontSize="9.5" />
                            </Border>
                          </Grid>
                          <!--
                            The address and how it authenticates, on one line. Which of the three ways is
                            here because only one of them wants a password typed, and an empty password box
                            on a key-authenticated host is otherwise indistinguishable from one somebody
                            forgot to fill in.
                          -->
                          <Grid ColumnDefinitions="*,Auto">
                            <TextBlock Grid.Column="0" Classes="mono" Text="{Binding Address}"
                                       FontSize="10.5" Foreground="{StaticResource TextFaint}"
                                       TextTrimming="CharacterEllipsis" />
                            <TextBlock Grid.Column="1" Classes="mono" Text="{Binding Authentication}"
                                       FontSize="10.5" Margin="6,0,0,0"
                                       Foreground="{StaticResource TextFaint}" />
                          </Grid>
                          <!--
                            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 grid
                            spanning several vaults it is not decoration.
                          -->
                          <TextBlock Classes="mono" Text="{Binding VaultBadge}" FontSize="10.5"
                                     Foreground="{StaticResource TextFaint}"
                                     IsVisible="{Binding HasVaultBadge}" />
                        </StackPanel>

                        <!--
                          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="2" Classes="dot" Classes.live="{Binding IsConnected}"
                                 VerticalAlignment="Top" Margin="6,3,0,0" />
                      </Grid>

                      <!--
                        The tags this host wears. Under the summary rather than beside it, because a host
                        can wear several and the line above is three fixed-width facts where these are not.

                        Names, not ids: a tag the vault cannot resolve is left out rather than drawn. See
                        HostRowViewModel.TagLabels.
                      -->
                      <ItemsControl ItemsSource="{Binding TagLabels}" IsVisible="{Binding HasTags}">
                        <ItemsControl.ItemsPanel>
                          <ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                          <DataTemplate x:DataType="x:String">
                            <Border Classes="chip" Padding="5,0" Margin="0,0,4,2">
                              <TextBlock Text="{Binding}" FontSize="9.5" />
                            </Border>
                          </DataTemplate>
                        </ItemsControl.ItemTemplate>
                      </ItemsControl>
                    </StackPanel>
                  </Border>
                </DataTemplate>

              </ListBox.DataTemplates>
            </ListBox>

            <!--
              Nothing to show, and the two reasons it can happen are different questions. An empty keychain
              is an invitation; a filter that matches nothing is a filter that has gone too far, and saying
              "add a host" to somebody who has thirty would be answering the wrong one.
            -->
            <TextBlock Classes="hint" FontSize="12" TextWrapping="Wrap" MaxWidth="480"
                       HorizontalAlignment="Left" Margin="0,4,0,0"
                       IsVisible="{Binding !HasVisibleHosts}"
                       Text="{Binding NoVisibleHostsMessage}" />

          </StackPanel>

        </StackPanel>
      </ScrollViewer>

    </Grid>

    <!--
      The drawer, and it takes its 304 pixels only while there is something in it — which is what gives the
      grid the full width for most of the time anybody is looking at it.

      Wrapped so that its own IsVisible is the shell's binding and its data context is the vault; see
      MainWindow.axaml's note on why the two cannot be put on one element.
    -->
    <Panel Grid.Column="1" IsVisible="{Binding IsDrawerOpen}">
      <views:HostDrawer x:Name="Drawer" />
    </Panel>

  </Grid>

</UserControl>
