/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.diagnostic.errordialog;

import com.intellij.diagnostic.DiagnosticBundle;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.util.ui.JBEmptyBorder;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class PluginConflictDialog extends DialogWrapper {
  public static final int WIDTH = 450;

  @NotNull
  private final List<PluginId> myConflictingPlugins;

  private final boolean myIsConflictWithPlatform;
  @Nullable
  private final List<JBRadioButton> myRadioButtons;

  private JPanel myContentPane;

  private JBLabel myTopMessageLabel;

  private JPanel myConflictingPluginsListPanel;

  public PluginConflictDialog(@NotNull List<PluginId> conflictingPlugins,
                              boolean isConflictWithPlatform) {
    super(false);

    myConflictingPlugins = conflictingPlugins;
    myIsConflictWithPlatform = isConflictWithPlatform;

    if (myIsConflictWithPlatform) {
      myRadioButtons = null;
    }
    else {
      myRadioButtons = new ArrayList<>();
    }

    $$$setupUI$$$();
    setTitle(DiagnosticBundle.message("error.dialog.conflict.plugin.title"));
    init();
    setCrossClosesWindow(false);

    getOKAction().updateText();
    myTopMessageLabel.setText(getTopMessageText(conflictingPlugins, isConflictWithPlatform));

    myTopMessageLabel.setPreferredSize(JBUI.size(WIDTH, (int)myTopMessageLabel.getPreferredSize().getHeight()));
    myContentPane.setPreferredSize(JBUI.size(WIDTH, (int)myContentPane.getMinimumSize().getHeight()));
  }

  private static String getTopMessageText(@NotNull List<PluginId> conflictingPlugins, boolean isConflictWithPlatform) {
    final int pluginsNumber = conflictingPlugins.size();
    if (isConflictWithPlatform) {
      return DiagnosticBundle.message("error.dialog.conflict.plugin.header.platform", pluginsNumber);
    }
    else {
      final List<String> names = conflictingPlugins.stream()
        .map(PluginConflictDialog::getPluginNameOrId)
        .map(s -> "<b>" + s + "</b>")
        .collect(Collectors.toList());
      return DiagnosticBundle.message("error.dialog.conflict.plugin.header.each.other",
                                      StringUtil.join(names.subList(0, pluginsNumber - 1), ", "),
                                      names.get(pluginsNumber - 1));
    }
  }

  private void $$$setupUI$$$() {
  }

  @Nullable
  @Override
  protected JComponent createCenterPanel() {
    return myContentPane;
  }

  private void createUIComponents() {
    final ButtonGroup buttonGroup = new ButtonGroup();

    myConflictingPluginsListPanel = new JPanel(new GridLayout(0, 1));
    final List<JPanel> pluginDescriptions = myConflictingPlugins.stream()
      .map(plugin -> getChooserPanelForPlugin(buttonGroup, plugin))
      .collect(Collectors.toList());

    if (!myIsConflictWithPlatform) {
      pluginDescriptions.add(getChooserPanelForPlugin(buttonGroup, null));
    }

    for (JPanel panel : pluginDescriptions) {
      myConflictingPluginsListPanel.add(panel);
    }

    setUpDefaultSelection();
  }

  @NotNull
  private JPanel getChooserPanelForPlugin(@NotNull ButtonGroup buttonGroup, @Nullable PluginId plugin) {
    final JPanel panel = new JPanel(new BorderLayout());
    if (!myIsConflictWithPlatform) {
      assert myRadioButtons != null;

      final JBRadioButton radioButton = new JBRadioButton();
      myRadioButtons.add(radioButton);
      buttonGroup.add(radioButton);

      radioButton.addChangeListener(e -> getOKAction().updateText());
      panel.add(radioButton, BorderLayout.WEST);
      panel.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
          radioButton.setSelected(true);
        }
      });
    }

    final JPanel descriptionPanel;
    if (plugin != null) {
      descriptionPanel = getPluginDescriptionPanel(plugin, !myIsConflictWithPlatform);
    }
    else {
      descriptionPanel = getDisableAllPanel();
    }
    descriptionPanel.setBorder(new JBEmptyBorder(10, myIsConflictWithPlatform ? 10 : 0, 10, 20));

    panel.add(descriptionPanel, BorderLayout.CENTER);
    return panel;
  }

  private void setUpDefaultSelection() {
    if (myIsConflictWithPlatform) {
      return;
    }
    assert myRadioButtons != null && myRadioButtons.size() == myConflictingPlugins.size() + 1;

    for (int i = 0; i < myConflictingPlugins.size(); i++) {
      final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(myConflictingPlugins.get(i));
      if (pluginDescriptor != null
          && (pluginDescriptor.isBundled() || StringUtil.equalsIgnoreCase(pluginDescriptor.getVendor(), "JetBrains"))) {
        myRadioButtons.get(i).setSelected(true);
        return;
      }
    }
    myRadioButtons.get(myRadioButtons.size() - 1).setSelected(true);
  }

  @NotNull
  private static JPanel getPluginDescriptionPanel(@NotNull PluginId plugin, boolean addUseWord) {
    final JPanel panel = new JPanel(new BorderLayout());

    final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(plugin);
    if (pluginDescriptor == null) {
      panel.add(new JBLabel(plugin.getIdString()), BorderLayout.CENTER);
      return panel;
    }

    final StringBuilder sb = new StringBuilder("<html>");
    if (addUseWord) {
      sb.append("Use ");
    }
    sb.append(pluginDescriptor.getName());
    if (pluginDescriptor.getVendor() != null) {
      sb.append(" by ").append(pluginDescriptor.getVendor());
    }
    sb.append("</html>");
    panel.add(new JBLabel(sb.toString()));
    return panel;
  }

  @NotNull
  private static String getPluginNameOrId(@NotNull PluginId pluginId) {
    final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(pluginId);
    if (pluginDescriptor == null) {
      return pluginId.getIdString();
    }
    else {
      return pluginDescriptor.getName();
    }
  }

  @NotNull
  private static JPanel getDisableAllPanel() {
    final JPanel panel = new JPanel(new BorderLayout());
    panel.add(new JBLabel(DiagnosticBundle.message("error.dialog.conflict.plugin.disable.all")));
    return panel;
  }

  @NotNull
  @Override
  protected Action[] createActions() {
    return new Action[]{getOKAction()};
  }

  @Override
  protected void createDefaultActions() {
    super.createDefaultActions();
    myOKAction = new DisableAction();
  }

  @NotNull
  @Override
  protected DisableAction getOKAction() {
    return ((DisableAction)myOKAction);
  }

  private class DisableAction extends DialogWrapperAction {
    protected DisableAction() {
      super("Disable");
      putValue(DEFAULT_ACTION, Boolean.TRUE);
    }

    public void updateText() {
      putValue(NAME, getButtonText());
      repaint();
    }

    @NotNull
    private String getButtonText() {
      if (myIsConflictWithPlatform) {
        return DiagnosticBundle.message("error.dialog.disable.plugin.action.disableAndRestart");
      }

      assert myRadioButtons != null;
      for (int i = 0; i < myConflictingPlugins.size(); ++i) {
        if (myRadioButtons.get(i).isSelected()) {
          return DiagnosticBundle.message("error.dialog.conflict.plugin.button.enable.and.restart");
        }
      }
      return DiagnosticBundle.message("error.dialog.conflict.plugin.button.disable.all");
    }

    @Override
    protected void doAction(ActionEvent e) {
      for (int i = 0; i < myConflictingPlugins.size(); ++i) {
        if (myRadioButtons == null || !myRadioButtons.get(i).isSelected()) {
          PluginManagerCore.disablePlugin(myConflictingPlugins.get(i).getIdString());
        }
      }
      close(OK_EXIT_CODE);
      ApplicationManagerEx.getApplicationEx().restart(true);
    }
  }
}
