/*
 * Copyright 2000-2015 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.ide.plugins;

import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.TransactionGuard;
import com.intellij.openapi.options.newEditor.SettingsDialog;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.GraphicsConfig;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.JBGradientPaint;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.GraphicsUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * @author Konstantin Bulenkov
 */
public class PluginHeaderPanel {
  private static final InstalledPluginsState ourState = InstalledPluginsState.getInstance();

  private final PluginManagerMain myManager;

  private IdeaPluginDescriptor myPlugin;
  private JBLabel myCategory;
  private JBLabel myName;
  private JBLabel myDownloads;
  private RatesPanel myRating;
  private JBLabel myUpdated;
  private JButton myInstallButton;
  private JBLabel myVersion;
  private JPanel myRoot;
  private JPanel myButtonPanel;
  private JPanel myDownloadsPanel;
  private JPanel myVersionInfoPanel;

  enum ACTION_ID {UPDATE, INSTALL, UNINSTALL, RESTART}
  private ACTION_ID myActionId = ACTION_ID.INSTALL;

  public PluginHeaderPanel(@Nullable PluginManagerMain manager) {
    myManager = manager;
    final Font font = myName.getFont();
    myName.setFont(new Font(font.getFontName(), font.getStyle(), font.getSize() + 2));
    final JBColor greyed = new JBColor(Gray._130, Gray._200);
    myCategory.setForeground(greyed);
    myDownloads.setForeground(greyed);
    myUpdated.setForeground(greyed);
    myVersion.setForeground(greyed);
    final Font smallFont = new Font(font.getFontName(), font.getStyle(), font.getSize() - 1);
    myCategory.setFont(smallFont);
    myVersion.setFont(smallFont);
    myVersion.setCopyable(true);
    myDownloads.setFont(smallFont);
    myUpdated.setFont(smallFont);
    myRoot.setVisible(false);
  }

  public void setPlugin(IdeaPluginDescriptor plugin) {
    myPlugin = plugin;
    myRoot.setVisible(true);
    myRoot.setBackground(UIUtil.getTextFieldBackground());
    myCategory.setVisible(true);
    myDownloadsPanel.setVisible(true);
    myButtonPanel.setVisible(true);
    myUpdated.setVisible(true);
    myName.setFont(UIUtil.getLabelFont().deriveFont(4f + UIUtil.getLabelFont().getSize()));

    //data
    myName.setText("<html><body>" + plugin.getName() + "</body></html>");
    myCategory.setText(plugin.getCategory() == null ? "UNKNOWN" : plugin.getCategory().toUpperCase());
    final boolean hasNewerVersion = ourState.hasNewerVersion(plugin.getPluginId());
    if (plugin instanceof PluginNode) {
      final PluginNode node = (PluginNode)plugin;
      myRating.setRate(node.getRating());
      myDownloads.setText(node.getDownloads() + " downloads");
      myVersion.setText("v" + node.getVersion());
      myUpdated.setText("Updated " + DateFormatUtil.formatDate(node.getDate()));
      switch (node.getStatus()) {
        case PluginNode.STATUS_INSTALLED:
          myActionId = hasNewerVersion ? ACTION_ID.UPDATE : ACTION_ID.UNINSTALL;
          break;
        case PluginNode.STATUS_DOWNLOADED:
          myActionId = ACTION_ID.RESTART;
          break;
        default:
          myActionId = ACTION_ID.INSTALL;
      }
      if (node.getRepositoryName() != null) {
        myCategory.setVisible(false);
        myDownloadsPanel.setVisible(false);
        myUpdated.setVisible(false);
      }

      final IdeaPluginDescriptor installed = PluginManager.getPlugin(plugin.getPluginId());
      if ((PluginManagerColumnInfo.isDownloaded(node)) ||
          (installed != null && ourState.wasUpdated(installed.getPluginId())) ||
          (installed instanceof IdeaPluginDescriptorImpl && !plugin.isBundled() && ((IdeaPluginDescriptorImpl)installed).isDeleted())) {
        myActionId = ACTION_ID.RESTART;
      }
    }
    else {
      myActionId = null;
      myVersionInfoPanel.remove(myUpdated);
      myCategory.setVisible(false);
      myDownloadsPanel.setVisible(false);
      final String version = plugin.getVersion();
      if (ourState.wasUpdated(plugin.getPluginId())) {
        myVersion.setText("New version will be available after restart");
      }
      else {
        myVersion.setText("Version: " + (version == null ? "N/A" : version));
      }
      myUpdated.setVisible(false);
      if (ourState.wasUpdated(plugin.getPluginId()) || ourState.wasInstalled(plugin.getPluginId())) {
        myActionId = ACTION_ID.RESTART;
      }
      else if (!plugin.isBundled() || hasNewerVersion) {
        if (((IdeaPluginDescriptorImpl)plugin).isDeleted()) {
          myActionId = ACTION_ID.RESTART;
        } else if (hasNewerVersion) {
          myActionId = ACTION_ID.UPDATE;
        } else {
          myActionId = ACTION_ID.UNINSTALL;
        }
      }
      if (myActionId == ACTION_ID.RESTART && myManager != null && !myManager.isRequireShutdown()) {
        myActionId = null;
      }
    }
    UIUtil.setEnabled(myButtonPanel, true, true);
    if (myManager == null || myActionId == null || (myManager.getInstalled() != myManager.getAvailable() && myActionId == ACTION_ID.UNINSTALL)) {
      myActionId = ACTION_ID.INSTALL;
      myButtonPanel.setVisible(false);
    }
    else if (InstallPluginAction.isInstalling(plugin)) {
      UIUtil.setEnabled(myButtonPanel, false, true);
    }
    myRoot.revalidate();
    ((JComponent)myInstallButton.getParent()).revalidate();
    myInstallButton.revalidate();
    ((JComponent)myVersion.getParent()).revalidate();
    myVersion.revalidate();
  }

  private void createUIComponents() {
    myInstallButton = new JButton() {
      private final int TOP_BOTTOM_BORDER = JBUI.scale(2);
      private final int LEFT_RIGHT_BORDER = JBUI.scale(8);
      private final int H_GAP = JBUI.scale(4);
      private final int ICON_SIZE = getIcon().getIconWidth();

      {
        setOpaque(false);
        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }
      @Override
      public Dimension getPreferredSize() {
        final FontMetrics metrics = getFontMetrics(getFont());
        final int textWidth = metrics.stringWidth(getText());
        final int width = LEFT_RIGHT_BORDER + ICON_SIZE + H_GAP + textWidth + LEFT_RIGHT_BORDER;
        final int height = TOP_BOTTOM_BORDER + Math.max(ICON_SIZE, metrics.getHeight()) + TOP_BOTTOM_BORDER;
        return new Dimension(width, height);
      }

      @Override
      public void paint(Graphics g2) {
        final Graphics2D g = (Graphics2D)g2;
        final GraphicsConfig config = GraphicsUtil.setupAAPainting(g);
        final int w = g.getClipBounds().width;
        final int h = g.getClipBounds().height;

        int borderArc = JBUI.scale(7);
        int border = JBUI.scale(1);
        int buttonArc = borderArc - border;

        g.setPaint(getBackgroundBorderPaint());
        g.fillRoundRect(0, 0, w, h, borderArc, borderArc);

        g.setPaint(getBackgroundPaint());
        g.fillRoundRect(border, border, w - 2 * border, h - 2 * border, buttonArc, buttonArc);

        g.setColor(getButtonForeground());
        g.drawString(getText(), LEFT_RIGHT_BORDER + ICON_SIZE + H_GAP, getBaseline(w, h));
        getIcon().paintIcon(this, g, LEFT_RIGHT_BORDER, (getHeight() - getIcon().getIconHeight()) / 2);
        config.restore();
      }

      private Color getButtonForeground() {
        switch (myActionId) {
          case UPDATE: return new JBColor(Gray._240, Gray._210);
          case INSTALL: return new JBColor(Gray._240, Gray._210);
          case RESTART:
          case UNINSTALL: return new JBColor(Gray._0, Gray._210);
        }

        return new JBColor(Gray._80, Gray._60);
      }

      private Paint getBackgroundPaint() {
        switch (myActionId) {
          case UPDATE: return new JBGradientPaint(this,
                                                  new JBColor(new Color(98, 158, 225), new Color(98, 158, 225)),
                                                  new JBColor(new Color(58, 91, 181), new Color(58, 91, 181)));
          case INSTALL: return new JBGradientPaint(this,
                                                   new JBColor(new Color(96, 204, 105), new Color(81, 149, 87)),
                                                   new JBColor(new Color(50, 101, 41), new Color(40, 70, 47)));
          case RESTART:
          case UNINSTALL: return UIUtil.isUnderDarcula()
                                 ? new JBGradientPaint(this, UIManager.getColor("Button.darcula.color1"), UIManager.getColor("Button.darcula.color2"))
                                 : Gray._240;
        }
        return Gray._238;
      }

      private Paint getBackgroundBorderPaint() {
        switch (myActionId) {
          case UPDATE: return new JBColor(new Color(166, 180, 205), Gray._85);
          case INSTALL: return new JBColor(new Color(201, 223, 201), Gray._70);
          case RESTART:
          case UNINSTALL: return new JBColor(Gray._220, Gray._100.withAlpha(180));
        }
        return Gray._208;
      }


      @Override
      public String getText() {
        switch (myActionId) {
          case UPDATE: return "Update";
          case INSTALL: return "Install";
          case UNINSTALL: return "Uninstall";
          case RESTART: return "Restart " + ApplicationNamesInfo.getInstance().getFullProductName();
        }
        return super.getText();
      }

      @Override
      public Icon getIcon() {
        switch (myActionId) {
          case UPDATE: return AllIcons.General.DownloadPlugin;
          case INSTALL: return  AllIcons.General.DownloadPlugin;
          case UNINSTALL: return AllIcons.Actions.Delete;
          case RESTART: return AllIcons.Actions.Restart;
        }
        return super.getIcon();

      }
    };
    myInstallButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        switch (myActionId) {
          case UPDATE:
          case INSTALL:
            Runnable setPlugin = () -> setPlugin(myPlugin);
            new InstallPluginAction(myManager.getAvailable(), myManager.getInstalled()).install(setPlugin, setPlugin, true);
            break;
          case UNINSTALL:
            UninstallPluginAction.uninstall(myManager.getInstalled(), true, myPlugin);
            break;
          case RESTART:
            if (myManager != null) {
              myManager.apply();
            }
            final DialogWrapper dialog =
              DialogWrapper.findInstance(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
            if (dialog != null && dialog.isModal()) {
              dialog.close(DialogWrapper.OK_EXIT_CODE);
            }
            TransactionGuard.getInstance().submitTransactionLater(ApplicationManager.getApplication(), () -> {
              DialogWrapper settings = DialogWrapper.findInstance(IdeFocusManager.findInstance().getFocusOwner());
              if (settings instanceof SettingsDialog) {
                ((SettingsDialog)settings).doOKAction();
              }
              ApplicationManager.getApplication().restart();
            });
            break;
        }
        setPlugin(myPlugin);
      }
    });
  }

  public JBLabel getCategory() {
    return myCategory;
  }

  public JBLabel getName() {
    return myName;
  }

  public JBLabel getDownloads() {
    return myDownloads;
  }

  public RatesPanel getRating() {
    return myRating;
  }

  public JBLabel getUpdated() {
    return myUpdated;
  }

  public JButton getInstallButton() {
    return myInstallButton;
  }

  public JPanel getPanel() {
    return myRoot;
  }
}
