/**
   * Unsubscribe from all events. This also unsubscribes the internal model and all internal views
   */
  public void unsubscribeAll() {

    unsubscribe();
    for (AbstractWizardPanelView view : getWizardViewMap().values()) {
      view.unsubscribe();
    }

    getWizardModel().unsubscribe();
  }
  /**
   * Show the named panel
   *
   * <p>This is guaranteed to be on the EDT
   *
   * @param panelName The panel name
   */
  public void show(String panelName) {

    Preconditions.checkState(
        SwingUtilities.isEventDispatchThread(), "This method should run on the EDT");

    if (!wizardViewMap.containsKey(panelName)) {
      log.error(
          "'{}' is not a valid panel name. Check the panel has been registered in the view map. Registered panels are\n{}",
          wizardViewMap.keySet());
      return;
    }

    final AbstractWizardPanelView wizardPanelView = wizardViewMap.get(panelName);

    if (!wizardPanelView.isInitialised()) {

      // Initialise the wizard screen panel and add it to the card layout parent
      wizardScreenHolder.add(wizardPanelView.getWizardScreenPanel(true), panelName);
    }

    // De-register any existing default buttons from previous panels
    wizardPanelView.deregisterDefaultButton();

    // Provide warning that the panel is about to be shown
    if (wizardPanelView.beforeShow()) {

      // No abort so show (use info to assist with FEST debugging)
      log.info("Showing wizard panel: {}", panelName);
      cardLayout.show(wizardScreenHolder, panelName);

      // We must ensure that all other EDT processing has completed before
      // calling afterShow() to guarantee visibility of components
      // Failure to do this causes problems with popovers during startup
      SwingUtilities.invokeLater(
          new Runnable() {
            @Override
            public void run() {
              wizardPanelView.afterShow();
            }
          });
    }
  }
  /**
   * Hide the wizard if <code>beforeHide</code> returns true
   *
   * <p>Guaranteed to run on the EDT
   *
   * @param panelName The panel name
   * @param isExitCancel True if this hide operation comes from an exit or cancel
   */
  public void hide(final String panelName, final boolean isExitCancel) {

    log.debug("Hide requested for {} with exitCancel {} ", panelName, isExitCancel);

    if (!wizardViewMap.containsKey(panelName)) {
      log.error(
          "'{}' is not a valid panel name. Check the panel has been registered in the view map. Registered panels are\n{}",
          wizardViewMap.keySet());
      return;
    }

    final AbstractWizardPanelView wizardPanelView = wizardViewMap.get(panelName);

    // Provide warning that the panel is about to be hidden
    if (wizardPanelView.beforeHide(isExitCancel)) {

      // No cancellation so go ahead with the hide
      handleHide(panelName, isExitCancel, wizardPanelView);
    }
  }
  /**
   * Hide the wizard
   *
   * <p>This method is guaranteed to run on the EDT
   *
   * @param panelName The panel name
   * @param isExitCancel True if this hide operation comes from an exit or cancel
   * @param wizardPanelView The wizard panel view from the wizard view map
   */
  protected void handleHide(
      final String panelName, final boolean isExitCancel, AbstractWizardPanelView wizardPanelView) {

    log.debug("Handle hide starting: '{}' ExitCancel: {}", panelName, isExitCancel);

    // De-register
    wizardPanelView.deregisterDefaultButton();

    // Ensure we unsubscribe the wizard from all further events
    getWizardModel().unsubscribe();
    unsubscribe();

    // Issue the wizard hide event before the hide takes place to give panel views time to update
    ViewEvents.fireWizardHideEvent(panelName, wizardModel, isExitCancel);

    // Required to run on a new thread since this may take some time to complete
    wizardHideExecutorService.submit(
        new Runnable() {
          @Override
          public void run() {

            log.debug("Hide and deregister wizard: '{}'", this.getClass().getSimpleName());

            // Require some extra time to get the rest of the UI started for credentials wizard
            // There is no chance of the system showing a light box during this time so this
            // operation is safe
            if (CredentialsState.CREDENTIALS_ENTER_PASSWORD.name().equals(panelName)) {
              log.trace("Blocking to allow UI startup to complete");
              Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
            }

            // Work through the view map ensuring all components are deregistered from UI events
            log.trace("Deregister {} views and their component(s)", wizardViewMap.size());
            for (Map.Entry<String, AbstractWizardPanelView> entry : wizardViewMap.entrySet()) {

              AbstractWizardPanelView panelView = entry.getValue();

              // Ensure we deregister the wizard panel view (and model if present) for events
              try {

                // Unsubscribe from events
                panelView.unsubscribe();
                log.trace(
                    "Deregistered wizard panel view '{}' from UI events", panelView.getPanelName());

                if (panelView.getPanelModel().isPresent()) {
                  Object panelModel = panelView.getPanelModel().get();
                  // May get some false positives from this approach
                  CoreEvents.unsubscribe(panelModel);
                  log.trace(
                      "Deregistered wizard panel model '{}' from UI events",
                      panelView.getPanelName());
                }

              } catch (NullPointerException | IllegalArgumentException e) {
                log.warn(
                    "Wizard panel model/view '{}' was not registered", panelView.getPanelName(), e);
              }

              // Deregister all components
              @SuppressWarnings("unchecked")
              List<ModelAndView> mavs = panelView.getComponents();
              for (ModelAndView mav : mavs) {
                mav.unsubscribe();
              }
              log.trace(
                  "Closed {} registered component(s) from wizard panel view '{}'",
                  mavs.size(),
                  panelView.getPanelName());

              // Remove the references
              mavs.clear();
            }

            // Depopulate the map to ensure non-AWT references are removed
            wizardViewMap.clear();

            // Hiding the light box must be on the EDT
            SwingUtilities.invokeLater(
                new Runnable() {
                  @Override
                  public void run() {

                    log.trace("Handle hide remove light box: '{}'", panelName);

                    // This removes the reference to the wizard allowing for garbage collection
                    Panels.hideLightBoxIfPresent();

                    // Clear the deferred hide
                    Panels.setDeferredHideEventInProgress(false);
                  }
                });
          }
        });
  }