#include "symtab.h"
 #include "frame.h"
 #include "source.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
 #include <ctype.h>
 
 #include "tui/tui.h"
 #include "tui/tui-source.h"
 #include "gdb_curses.h"
 
-static void show_layout (enum tui_layout_type);
-static enum tui_layout_type next_layout (void);
-static enum tui_layout_type prev_layout (void);
 static void tui_layout_command (const char *, int);
 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
 
-
-/* The pre-defined layouts.  */
-static tui_layout_split *standard_layouts[UNDEFINED_LAYOUT];
+/* The layouts.  */
+static std::vector<std::unique_ptr<tui_layout_split>> layouts;
 
 /* The layout that is currently applied.  */
 static std::unique_ptr<tui_layout_base> applied_layout;
 
-static enum tui_layout_type current_layout = UNDEFINED_LAYOUT;
+/* The "skeleton" version of the layout that is currently applied.  */
+static tui_layout_split *applied_skeleton;
 
-/* Accessor for the current layout.  */
-enum tui_layout_type
-tui_current_layout (void)
-{
-  return current_layout;
-}
+/* The two special "regs" layouts.  Note that these aren't registered
+   as commands and so can never be deleted.  */
+static tui_layout_split *src_regs_layout;
+static tui_layout_split *asm_regs_layout;
 
 /* See tui-layout.h.  */
 
   applied_layout->adjust_size (win->name (), new_height);
 }
 
-/* Show the screen layout defined.  */
-static void
-show_layout (enum tui_layout_type layout)
-{
-  enum tui_layout_type cur_layout = tui_current_layout ();
-
-  if (layout != cur_layout)
-    {
-      tui_make_all_invisible ();
-      applied_layout = standard_layouts[layout]->clone ();
-      tui_apply_current_layout ();
-      current_layout = layout;
-      tui_delete_invisible_windows ();
-    }
-}
-
+/* Set the current layout to LAYOUT.  */
 
-/* Function to set the layout to SRC_COMMAND, DISASSEM_COMMAND,
-   SRC_DISASSEM_COMMAND, SRC_DATA_COMMAND, or DISASSEM_DATA_COMMAND.  */
-void
-tui_set_layout (enum tui_layout_type layout_type)
+static void
+tui_set_layout (tui_layout_split *layout)
 {
-  gdb_assert (layout_type != UNDEFINED_LAYOUT);
-
-  enum tui_layout_type cur_layout = tui_current_layout ();
   struct gdbarch *gdbarch;
   CORE_ADDR addr;
-  struct tui_win_info *win_with_focus = tui_win_with_focus ();
 
   extract_display_start_addr (&gdbarch, &addr);
+  tui_make_all_invisible ();
+  applied_skeleton = layout;
+  applied_layout = layout->clone ();
+  tui_apply_current_layout ();
+  tui_delete_invisible_windows ();
 
-  enum tui_layout_type new_layout = layout_type;
-
-  if (new_layout != cur_layout)
-    {
-      tui_suppress_output suppress;
-
-      show_layout (new_layout);
-
-      /* Now determine where focus should be.  */
-      if (win_with_focus != TUI_CMD_WIN)
-       {
-         switch (new_layout)
-           {
-           case SRC_COMMAND:
-             tui_set_win_focus_to (TUI_SRC_WIN);
-             break;
-           case DISASSEM_COMMAND:
-             /* The previous layout was not showing code.
-                This can happen if there is no source
-                available:
-
-                1. if the source file is in another dir OR
-                2. if target was compiled without -g
-                We still want to show the assembly though!  */
-
-             tui_get_begin_asm_address (&gdbarch, &addr);
-             tui_set_win_focus_to (TUI_DISASM_WIN);
-             break;
-           case SRC_DISASSEM_COMMAND:
-             /* The previous layout was not showing code.
-                This can happen if there is no source
-                available:
-
-                1. if the source file is in another dir OR
-                2. if target was compiled without -g
-                We still want to show the assembly though!  */
-
-             tui_get_begin_asm_address (&gdbarch, &addr);
-             if (win_with_focus == TUI_SRC_WIN)
-               tui_set_win_focus_to (TUI_SRC_WIN);
-             else
-               tui_set_win_focus_to (TUI_DISASM_WIN);
-             break;
-           case SRC_DATA_COMMAND:
-             if (win_with_focus != TUI_DATA_WIN)
-               tui_set_win_focus_to (TUI_SRC_WIN);
-             else
-               tui_set_win_focus_to (TUI_DATA_WIN);
-             break;
-           case DISASSEM_DATA_COMMAND:
-             /* The previous layout was not showing code.
-                This can happen if there is no source
-                available:
-
-                1. if the source file is in another dir OR
-                2. if target was compiled without -g
-                We still want to show the assembly though!  */
-
-             tui_get_begin_asm_address (&gdbarch, &addr);
-             if (win_with_focus != TUI_DATA_WIN)
-               tui_set_win_focus_to (TUI_DISASM_WIN);
-             else
-               tui_set_win_focus_to (TUI_DATA_WIN);
-             break;
-           default:
-             break;
-           }
-       }
-      /*
-       * Now update the window content.
-       */
-      tui_update_source_windows_with_addr (gdbarch, addr);
-      if (new_layout == SRC_DATA_COMMAND
-         || new_layout == DISASSEM_DATA_COMMAND)
-       TUI_DATA_WIN->show_registers (TUI_DATA_WIN->get_current_group ());
-    }
+  if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
+    tui_get_begin_asm_address (&gdbarch, &addr);
+  tui_update_source_windows_with_addr (gdbarch, addr);
 }
 
 /* See tui-layout.h.  */
 {
   gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
 
-  enum tui_layout_type cur_layout = tui_current_layout ();
+  /* If the window already exists, no need to add it.  */
+  if (tui_win_list[type] != nullptr)
+    return;
+
+  /* If the window we are trying to replace doesn't exist, we're
+     done.  */
+  enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
+  if (tui_win_list[other] == nullptr)
+    return;
+
+  const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
+  applied_layout->replace_window (tui_win_list[other]->name (), name);
+  tui_apply_current_layout ();
+  tui_delete_invisible_windows ();
+}
+
+/* Find LAYOUT in the "layouts" global and return its index.  */
 
-  switch (type)
+static size_t
+find_layout (tui_layout_split *layout)
+{
+  for (size_t i = 0; i < layouts.size (); ++i)
     {
-    case SRC_WIN:
-      if (cur_layout != SRC_COMMAND
-         && cur_layout != SRC_DISASSEM_COMMAND
-         && cur_layout != SRC_DATA_COMMAND)
-       {
-         if (cur_layout == DISASSEM_DATA_COMMAND)
-           tui_set_layout (SRC_DATA_COMMAND);
-         else
-           tui_set_layout (SRC_COMMAND);
-       }
-      break;
-    case DISASSEM_WIN:
-      if (cur_layout != DISASSEM_COMMAND
-         && cur_layout != SRC_DISASSEM_COMMAND
-         && cur_layout != DISASSEM_DATA_COMMAND)
-       {
-         if (cur_layout == SRC_DATA_COMMAND)
-           tui_set_layout (DISASSEM_DATA_COMMAND);
-         else
-           tui_set_layout (DISASSEM_COMMAND);
-       }
-      break;
+      if (layout == layouts[i].get ())
+       return i;
     }
+  gdb_assert_not_reached (_("layout not found!?"));
 }
 
-/* Complete possible layout names.  TEXT is the complete text entered so
-   far, WORD is the word currently being completed.  */
+/* Function to set the layout. */
 
 static void
-layout_completer (struct cmd_list_element *ignore,
-                 completion_tracker &tracker,
-                 const char *text, const char *word)
+tui_apply_layout (struct cmd_list_element *command,
+                 const char *args, int from_tty)
 {
-  static const char *layout_names [] =
-    { "src", "asm", "split", "regs", "next", "prev", NULL };
+  tui_layout_split *layout
+    = (tui_layout_split *) get_cmd_context (command);
 
-  complete_on_enum (tracker, layout_names, text, word);
+  /* Make sure the curses mode is enabled.  */
+  tui_enable ();
+  tui_set_layout (layout);
 }
 
-/* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or
-   REGS. */
-static void
-tui_layout_command (const char *layout_name, int from_tty)
-{
-  enum tui_layout_type new_layout = UNDEFINED_LAYOUT;
-  enum tui_layout_type cur_layout = tui_current_layout ();
-
-  if (layout_name == NULL || *layout_name == '\0')
-    error (_("Usage: layout prev | next | LAYOUT-NAME"));
+/* See tui-layout.h.  */
 
-  /* First check for ambiguous input.  */
-  if (strcmp (layout_name, "s") == 0)
-    error (_("Ambiguous command input."));
+void
+tui_next_layout ()
+{
+  size_t index = find_layout (applied_skeleton);
+  ++index;
+  if (index == layouts.size ())
+    index = 0;
+  tui_set_layout (layouts[index].get ());
+}
 
-  if (subset_compare (layout_name, "src"))
-    new_layout = SRC_COMMAND;
-  else if (subset_compare (layout_name, "asm"))
-    new_layout = DISASSEM_COMMAND;
-  else if (subset_compare (layout_name, "split"))
-    new_layout = SRC_DISASSEM_COMMAND;
-  else if (subset_compare (layout_name, "regs"))
-    {
-      if (cur_layout == SRC_COMMAND
-         || cur_layout == SRC_DATA_COMMAND)
-       new_layout = SRC_DATA_COMMAND;
-      else
-       new_layout = DISASSEM_DATA_COMMAND;
-    }
-  else if (subset_compare (layout_name, "next"))
-    new_layout = next_layout ();
-  else if (subset_compare (layout_name, "prev"))
-    new_layout = prev_layout ();
-  else
-    error (_("Unrecognized layout: %s"), layout_name);
+/* Implement the "layout next" command.  */
 
-  /* Make sure the curses mode is enabled.  */
+static void
+tui_next_layout_command (const char *arg, int from_tty)
+{
   tui_enable ();
-  tui_set_layout (new_layout);
+  tui_next_layout ();
 }
 
 /* See tui-layout.h.  */
 
 void
-tui_next_layout ()
+tui_set_initial_layout ()
+{
+  tui_set_layout (layouts[0].get ());
+}
+
+/* Implement the "layout prev" command.  */
+
+static void
+tui_prev_layout_command (const char *arg, int from_tty)
 {
-  tui_layout_command ("next", 0);
+  tui_enable ();
+  size_t index = find_layout (applied_skeleton);
+  if (index == 0)
+    index = layouts.size ();
+  --index;
+  tui_set_layout (layouts[index].get ());
 }
 
+
 /* See tui-layout.h.  */
 
 void
 tui_regs_layout ()
 {
-  tui_layout_command ("regs", 0);
+  /* If there's already a register window, we're done.  */
+  if (TUI_DATA_WIN != nullptr)
+    return;
+
+  tui_set_layout (TUI_DISASM_WIN != nullptr
+                 ? asm_regs_layout
+                 : src_regs_layout);
+}
+
+/* Implement the "layout regs" command.  */
+
+static void
+tui_regs_layout_command (const char *arg, int from_tty)
+{
+  tui_enable ();
+  tui_regs_layout ();
 }
 
 /* See tui-layout.h.  */
 static void
 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
 {
-  enum tui_layout_type cur_layout = tui_current_layout ();
-  struct gdbarch *gdbarch = get_current_arch ();
-  CORE_ADDR addr;
+  struct gdbarch *gdbarch = nullptr;
+  CORE_ADDR addr = 0;
   CORE_ADDR pc;
   struct symtab_and_line cursal = get_current_source_symtab_and_line ();
 
-  switch (cur_layout)
+  if (TUI_SRC_WIN != nullptr)
     {
-    case SRC_COMMAND:
-    case SRC_DATA_COMMAND:
       gdbarch = TUI_SRC_WIN->gdbarch;
       find_line_pc (cursal.symtab,
                    TUI_SRC_WIN->start_line_or_addr.u.line_no,
                    &pc);
       addr = pc;
-      break;
-    case DISASSEM_COMMAND:
-    case SRC_DISASSEM_COMMAND:
-    case DISASSEM_DATA_COMMAND:
+    }
+  else if (TUI_DISASM_WIN != nullptr)
+    {
       gdbarch = TUI_DISASM_WIN->gdbarch;
       addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
-      break;
-    default:
-      addr = 0;
-      break;
     }
 
   *gdbarch_p = gdbarch;
   *addr_p = addr;
 }
 
-
-/* Answer the previous layout to cycle to.  */
-static enum tui_layout_type
-next_layout (void)
-{
-  int new_layout;
-
-  new_layout = tui_current_layout ();
-  if (new_layout == UNDEFINED_LAYOUT)
-    new_layout = SRC_COMMAND;
-  else
-    {
-      new_layout++;
-      if (new_layout == UNDEFINED_LAYOUT)
-       new_layout = SRC_COMMAND;
-    }
-
-  return (enum tui_layout_type) new_layout;
-}
-
-
-/* Answer the next layout to cycle to.  */
-static enum tui_layout_type
-prev_layout (void)
-{
-  int new_layout;
-
-  new_layout = tui_current_layout ();
-  if (new_layout == SRC_COMMAND)
-    new_layout = DISASSEM_DATA_COMMAND;
-  else
-    {
-      new_layout--;
-      if (new_layout == UNDEFINED_LAYOUT)
-       new_layout = DISASSEM_DATA_COMMAND;
-    }
-
-  return (enum tui_layout_type) new_layout;
-}
-
 void
 tui_gen_win_info::resize (int height_, int width_,
                          int origin_x_, int origin_y_)
     }
   else
     {
-      gdb_assert (name == "locator");
+      gdb_assert (name == "status");
       return tui_locator_win_info_ptr ();
     }
 }
 
 /* See tui-layout.h.  */
 
+void
+tui_layout_window::replace_window (const char *name, const char *new_window)
+{
+  if (m_contents == name)
+    {
+      m_contents = new_window;
+      if (m_window != nullptr)
+       {
+         m_window->make_visible (false);
+         m_window = tui_get_window_by_name (m_contents);
+       }
+    }
+}
+
+/* See tui-layout.h.  */
+
 tui_layout_split *
 tui_layout_split::add_split (int weight)
 {
     }
 }
 
+/* See tui-layout.h.  */
+
+void
+tui_layout_split::replace_window (const char *name, const char *new_window)
+{
+  for (auto &item : m_splits)
+    item.layout->replace_window (name, new_window);
+}
+
+/* Destroy the layout associated with SELF.  */
+
 static void
-initialize_layouts ()
+destroy_layout (struct cmd_list_element *self, void *context)
+{
+  tui_layout_split *layout = (tui_layout_split *) context;
+  size_t index = find_layout (layout);
+  layouts.erase (layouts.begin () + index);
+}
+
+/* List holding the sub-commands of "layout".  */
+
+static struct cmd_list_element *layout_list;
+
+/* Add a "layout" command with name NAME that switches to LAYOUT.  */
+
+static void
+add_layout_command (const char *name, tui_layout_split *layout)
 {
-  standard_layouts[SRC_COMMAND] = new tui_layout_split ();
-  standard_layouts[SRC_COMMAND]->add_window ("src", 2);
-  standard_layouts[SRC_COMMAND]->add_window ("locator", 0);
-  standard_layouts[SRC_COMMAND]->add_window ("cmd", 1);
+  struct cmd_list_element *cmd;
 
-  standard_layouts[DISASSEM_COMMAND] = new tui_layout_split ();
-  standard_layouts[DISASSEM_COMMAND]->add_window ("asm", 2);
-  standard_layouts[DISASSEM_COMMAND]->add_window ("locator", 0);
-  standard_layouts[DISASSEM_COMMAND]->add_window ("cmd", 1);
+  gdb::unique_xmalloc_ptr<char> doc (xstrprintf (_("Apply the \"%s\" layout"),
+                                                name));
 
-  standard_layouts[SRC_DATA_COMMAND] = new tui_layout_split ();
-  standard_layouts[SRC_DATA_COMMAND]->add_window ("regs", 1);
-  standard_layouts[SRC_DATA_COMMAND]->add_window ("src", 1);
-  standard_layouts[SRC_DATA_COMMAND]->add_window ("locator", 0);
-  standard_layouts[SRC_DATA_COMMAND]->add_window ("cmd", 1);
+  cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
+  set_cmd_context (cmd, layout);
+  /* There is no API to set this.  */
+  cmd->func = tui_apply_layout;
+  cmd->destroyer = destroy_layout;
+  cmd->doc_allocated = 1;
+  doc.release ();
+  layouts.emplace_back (layout);
+}
 
-  standard_layouts[DISASSEM_DATA_COMMAND] = new tui_layout_split ();
-  standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("regs", 1);
-  standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("asm", 1);
-  standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("locator", 0);
-  standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("cmd", 1);
+/* Initialize the standard layouts.  */
 
-  standard_layouts[SRC_DISASSEM_COMMAND] = new tui_layout_split ();
-  standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("src", 1);
-  standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("asm", 1);
-  standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("locator", 0);
-  standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("cmd", 1);
+static void
+initialize_layouts ()
+{
+  tui_layout_split *layout;
+
+  layout = new tui_layout_split ();
+  layout->add_window ("src", 2);
+  layout->add_window ("status", 0);
+  layout->add_window ("cmd", 1);
+  add_layout_command ("src", layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window ("asm", 2);
+  layout->add_window ("status", 0);
+  layout->add_window ("cmd", 1);
+  add_layout_command ("asm", layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window ("src", 1);
+  layout->add_window ("asm", 1);
+  layout->add_window ("status", 0);
+  layout->add_window ("cmd", 1);
+  add_layout_command ("split", layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window ("regs", 1);
+  layout->add_window ("src", 1);
+  layout->add_window ("status", 0);
+  layout->add_window ("cmd", 1);
+  layouts.emplace_back (layout);
+  src_regs_layout = layout;
+
+  layout = new tui_layout_split ();
+  layout->add_window ("regs", 1);
+  layout->add_window ("asm", 1);
+  layout->add_window ("status", 0);
+  layout->add_window ("cmd", 1);
+  layouts.emplace_back (layout);
+  asm_regs_layout = layout;
 }
 
 \f
 
+/* Base command for "layout".  */
+
+static void
+tui_layout_command (const char *layout_name, int from_tty)
+{
+  help_list (layout_list, "layout ", all_commands, gdb_stdout);
+}
+
 /* Function to initialize gdb commands, for tui window layout
    manipulation.  */
 
 void
 _initialize_tui_layout ()
 {
-  struct cmd_list_element *cmd;
-
-  cmd = add_com ("layout", class_tui, tui_layout_command, _("\
+  add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
 Change the layout of windows.\n\
-Usage: layout prev | next | LAYOUT-NAME\n\
-Layout names are:\n\
-   src   : Displays source and command windows.\n\
-   asm   : Displays disassembly and command windows.\n\
-   split : Displays source, disassembly and command windows.\n\
-   regs  : Displays register window. If existing layout\n\
-           is source/command or assembly/command, the \n\
-           register window is displayed. If the\n\
-           source/assembly/command (split) is displayed, \n\
-           the register window is displayed with \n\
-           the window that has current logical focus."));
-  set_cmd_completer (cmd, layout_completer);
+Usage: layout prev | next | LAYOUT-NAME"),
+                 &layout_list, "layout ", 0, &cmdlist);
+
+  add_cmd ("next", class_tui, tui_next_layout_command,
+          _("Apply the next TUI layout"),
+          &layout_list);
+  add_cmd ("prev", class_tui, tui_prev_layout_command,
+          _("Apply the previous TUI layout"),
+          &layout_list);
+  add_cmd ("regs", class_tui, tui_regs_layout_command,
+          _("Apply the TUI register layout"),
+          &layout_list);
 
   initialize_layouts ();
 }