@Override
  public void update(AnActionEvent e) {
    Presentation presentation = e.getPresentation();
    IdeaPluginDescriptor[] selection = getPluginTable().getSelectedObjects();
    boolean enabled = (selection != null);

    if (enabled) {
      for (IdeaPluginDescriptor descr : selection) {
        presentation.setText(IdeBundle.message("action.download.and.install.plugin"));
        presentation.setDescription(IdeBundle.message("action.download.and.install.plugin"));
        enabled &= !ourInstallingNodes.contains(descr);
        if (descr instanceof PluginNode) {
          enabled &= !PluginManagerColumnInfo.isDownloaded((PluginNode) descr);
          if (((PluginNode) descr).getStatus() == PluginNode.STATUS_INSTALLED) {
            presentation.setText(IdeBundle.message("action.update.plugin"));
            presentation.setDescription(IdeBundle.message("action.update.plugin"));
            enabled &= ourState.hasNewerVersion(descr.getPluginId());
          }
        } else if (descr instanceof IdeaPluginDescriptorImpl) {
          presentation.setText(IdeBundle.message("action.update.plugin"));
          presentation.setDescription(IdeBundle.message("action.update.plugin"));
          PluginId id = descr.getPluginId();
          enabled = enabled && ourState.hasNewerVersion(id);
        }
      }
    }

    presentation.setEnabled(enabled);
  }
/** @author lloix */
public class InstallPluginAction extends AnAction implements DumbAware {
  private static final InstalledPluginsState ourState = InstalledPluginsState.getInstance();
  private static final Set<IdeaPluginDescriptor> ourInstallingNodes =
      new HashSet<IdeaPluginDescriptor>();

  private final PluginManagerMain myHost;
  private final PluginManagerMain myInstalled;

  public InstallPluginAction(PluginManagerMain mgr, PluginManagerMain installed) {
    super(
        IdeBundle.message("action.download.and.install.plugin"),
        IdeBundle.message("action.download.and.install.plugin"),
        AllIcons.Actions.Install);
    myHost = mgr;
    myInstalled = installed;
  }

  @Override
  public void update(AnActionEvent e) {
    Presentation presentation = e.getPresentation();
    IdeaPluginDescriptor[] selection = getPluginTable().getSelectedObjects();
    boolean enabled = (selection != null);

    if (enabled) {
      for (IdeaPluginDescriptor descr : selection) {
        presentation.setText(IdeBundle.message("action.download.and.install.plugin"));
        presentation.setDescription(IdeBundle.message("action.download.and.install.plugin"));
        enabled &= !ourInstallingNodes.contains(descr);
        if (descr instanceof PluginNode) {
          enabled &= !PluginManagerColumnInfo.isDownloaded((PluginNode) descr);
          if (((PluginNode) descr).getStatus() == PluginNode.STATUS_INSTALLED) {
            presentation.setText(IdeBundle.message("action.update.plugin"));
            presentation.setDescription(IdeBundle.message("action.update.plugin"));
            enabled &= ourState.hasNewerVersion(descr.getPluginId());
          }
        } else if (descr instanceof IdeaPluginDescriptorImpl) {
          presentation.setText(IdeBundle.message("action.update.plugin"));
          presentation.setDescription(IdeBundle.message("action.update.plugin"));
          PluginId id = descr.getPluginId();
          enabled = enabled && ourState.hasNewerVersion(id);
        }
      }
    }

    presentation.setEnabled(enabled);
  }

  @Override
  public void actionPerformed(AnActionEvent e) {
    install(null);
  }

  public static boolean isInstalling(IdeaPluginDescriptor node) {
    return ourInstallingNodes.contains(node);
  }

  public void install(@Nullable final Runnable onSuccess) {
    install(onSuccess, false);
  }

  public void install(@Nullable final Runnable onSuccess, boolean confirmed) {
    IdeaPluginDescriptor[] selection = getPluginTable().getSelectedObjects();

    if (confirmed || userConfirm(selection)) {
      final List<PluginNode> list = new ArrayList<PluginNode>();
      for (IdeaPluginDescriptor descr : selection) {
        PluginNode pluginNode = null;
        if (descr instanceof PluginNode) {
          pluginNode = (PluginNode) descr;
        } else if (descr instanceof IdeaPluginDescriptorImpl) {
          PluginId pluginId = descr.getPluginId();
          pluginNode = new PluginNode(pluginId);
          pluginNode.setName(descr.getName());
          pluginNode.setDepends(
              Arrays.asList(descr.getDependentPluginIds()), descr.getOptionalDependentPluginIds());
          pluginNode.setSize("-1");
          pluginNode.setRepositoryName(PluginInstaller.UNKNOWN_HOST_MARKER);
        }

        if (pluginNode != null) {
          list.add(pluginNode);
          ourInstallingNodes.add(pluginNode);
        }
      }

      final InstalledPluginsTableModel installedModel =
          (InstalledPluginsTableModel) myInstalled.getPluginsModel();
      final Set<IdeaPluginDescriptor> disabled = new HashSet<IdeaPluginDescriptor>();
      final Set<IdeaPluginDescriptor> disabledDependants = new HashSet<IdeaPluginDescriptor>();
      for (PluginNode node : list) {
        final PluginId pluginId = node.getPluginId();
        if (installedModel.isDisabled(pluginId)) {
          disabled.add(node);
        }
        final List<PluginId> depends = node.getDepends();
        if (depends != null) {
          final Set<PluginId> optionalDeps =
              new HashSet<PluginId>(Arrays.asList(node.getOptionalDependentPluginIds()));
          for (PluginId dependantId : depends) {
            if (optionalDeps.contains(dependantId)) continue;
            final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(dependantId);
            if (pluginDescriptor != null && installedModel.isDisabled(dependantId)) {
              disabledDependants.add(pluginDescriptor);
            }
          }
        }
      }

      if (suggestToEnableInstalledPlugins(installedModel, disabled, disabledDependants, list)) {
        myInstalled.setRequireShutdown(true);
      }

      try {
        Runnable onInstallRunnable =
            new Runnable() {
              @Override
              public void run() {
                for (PluginNode node : list) {
                  installedModel.appendOrUpdateDescriptor(node);
                }
                if (!myInstalled.isDisposed()) {
                  getPluginTable().updateUI();
                  myInstalled.setRequireShutdown(true);
                } else {
                  boolean needToRestart = false;
                  for (PluginNode node : list) {
                    final IdeaPluginDescriptor pluginDescriptor =
                        PluginManager.getPlugin(node.getPluginId());
                    if (pluginDescriptor == null || pluginDescriptor.isEnabled()) {
                      needToRestart = true;
                      break;
                    }
                  }

                  if (needToRestart) {
                    PluginManagerMain.notifyPluginsUpdated(null);
                  }
                }
                if (onSuccess != null) {
                  onSuccess.run();
                }
              }
            };
        Runnable cleanupRunnable =
            new Runnable() {
              @Override
              public void run() {
                ourInstallingNodes.removeAll(list);
              }
            };
        PluginManagerMain.downloadPlugins(
            list, myHost.getPluginsModel().getAllPlugins(), onInstallRunnable, cleanupRunnable);
      } catch (final IOException e1) {
        ourInstallingNodes.removeAll(list);
        PluginManagerMain.LOG.error(e1);
        //noinspection SSBasedInspection
        SwingUtilities.invokeLater(
            new Runnable() {
              @Override
              public void run() {
                IOExceptionDialog.showErrorDialog(
                    IdeBundle.message("action.download.and.install.plugin"),
                    IdeBundle.message("error.plugin.download.failed"));
              }
            });
      }
    }
  }

  private static boolean suggestToEnableInstalledPlugins(
      InstalledPluginsTableModel pluginsModel,
      Set<IdeaPluginDescriptor> disabled,
      Set<IdeaPluginDescriptor> disabledDependants,
      List<PluginNode> list) {
    if (!disabled.isEmpty() || !disabledDependants.isEmpty()) {
      String message = "";
      if (disabled.size() == 1) {
        message += "Updated plugin '" + disabled.iterator().next().getName() + "' is disabled.";
      } else if (!disabled.isEmpty()) {
        message +=
            "Updated plugins "
                + StringUtil.join(
                    disabled,
                    new Function<IdeaPluginDescriptor, String>() {
                      @Override
                      public String fun(IdeaPluginDescriptor pluginDescriptor) {
                        return pluginDescriptor.getName();
                      }
                    },
                    ", ")
                + " are disabled.";
      }

      if (!disabledDependants.isEmpty()) {
        message += "<br>";
        message += "Updated plugin" + (list.size() > 1 ? "s depend " : " depends ") + "on disabled";
        if (disabledDependants.size() == 1) {
          message += " plugin '" + disabledDependants.iterator().next().getName() + "'.";
        } else {
          message +=
              " plugins "
                  + StringUtil.join(
                      disabledDependants,
                      new Function<IdeaPluginDescriptor, String>() {
                        @Override
                        public String fun(IdeaPluginDescriptor pluginDescriptor) {
                          return pluginDescriptor.getName();
                        }
                      },
                      ", ")
                  + ".";
        }
      }
      message +=
          " Disabled plugins "
              + (disabled.isEmpty() ? "and plugins which depend on disabled " : "")
              + "won't be activated after restart.";

      int result;
      if (!disabled.isEmpty() && !disabledDependants.isEmpty()) {
        result =
            Messages.showYesNoCancelDialog(
                XmlStringUtil.wrapInHtml(message),
                CommonBundle.getWarningTitle(),
                "Enable all",
                "Enable updated plugin" + (disabled.size() > 1 ? "s" : ""),
                CommonBundle.getCancelButtonText(),
                Messages.getQuestionIcon());
        if (result == Messages.CANCEL) return false;
      } else {
        message += "<br>Would you like to enable ";
        if (!disabled.isEmpty()) {
          message += "updated plugin" + (disabled.size() > 1 ? "s" : "");
        } else {
          //noinspection SpellCheckingInspection
          message += "plugin dependenc" + (disabledDependants.size() > 1 ? "ies" : "y");
        }
        message += "?";
        result =
            Messages.showYesNoDialog(
                XmlStringUtil.wrapInHtml(message),
                CommonBundle.getWarningTitle(),
                Messages.getQuestionIcon());
        if (result == Messages.NO) return false;
      }

      if (result == Messages.YES) {
        disabled.addAll(disabledDependants);
        pluginsModel.enableRows(disabled.toArray(new IdeaPluginDescriptor[disabled.size()]), true);
      } else if (result == Messages.NO && !disabled.isEmpty()) {
        pluginsModel.enableRows(disabled.toArray(new IdeaPluginDescriptor[disabled.size()]), true);
      }
      return true;
    }
    return false;
  }

  public PluginTable getPluginTable() {
    return myHost.getPluginTable();
  }

  // ---------------------------------------------------------------------------
  //  Show confirmation message depending on the amount and type of the
  //  selected plugin descriptors: already downloaded plugins need "update"
  //  while non-installed yet need "install".
  // ---------------------------------------------------------------------------
  private boolean userConfirm(IdeaPluginDescriptor[] selection) {
    String message;
    if (selection.length == 1) {
      if (selection[0] instanceof IdeaPluginDescriptorImpl) {
        message = IdeBundle.message("prompt.update.plugin", selection[0].getName());
      } else {
        message = IdeBundle.message("prompt.download.and.install.plugin", selection[0].getName());
      }
    } else {
      message = IdeBundle.message("prompt.install.several.plugins", selection.length);
    }

    return Messages.showYesNoDialog(
            myHost.getMainPanel(),
            message,
            IdeBundle.message("action.download.and.install.plugin"),
            Messages.getQuestionIcon())
        == Messages.YES;
  }
}