/*
 * Copyright 2000-2013 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.openapi.wm.impl.status;

import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.UISettingsListener;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.openapi.wm.impl.ToolWindowImpl;
import com.intellij.ui.GotItMessage;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.UIBundle;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.util.Alarm;
import com.intellij.util.ui.BaseButtonBehavior;
import com.intellij.util.ui.TimedDeadzone;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

/**
 * @author Konstantin Bulenkov
 */
class ToolWindowsWidget extends JLabel implements CustomStatusBarWidget, StatusBarWidget, Disposable,
                                                  UISettingsListener, PropertyChangeListener {

  private final Alarm myAlarm;
  private StatusBar myStatusBar;
  private JBPopup popup;
  private boolean wasExited = false;

  ToolWindowsWidget(Disposable parent) {
    new BaseButtonBehavior(this, TimedDeadzone.NULL) {
      @Override
      protected void execute(MouseEvent e) {
        performAction();
      }
    }.setActionTrigger(MouseEvent.MOUSE_PRESSED);

    IdeEventQueue.getInstance().addDispatcher(new IdeEventQueue.EventDispatcher() {
      @Override
      public boolean dispatch(AWTEvent e) {
        if (e instanceof MouseEvent) {
          if (e.getID() == MouseEvent.MOUSE_MOVED && isShowing()) {
            Point p = ((MouseEvent)e).getLocationOnScreen();
            Point screen = ToolWindowsWidget.this.getLocationOnScreen();
            if (new Rectangle(screen.x - 4, screen.y - 2, getWidth() + 4, getHeight() + 4).contains(p)) {
              mouseEntered();
              wasExited = false;
            } else {
              if (!wasExited) {
                wasExited = mouseExited(p);
              }
            }
          } else if (e.getID() == MouseEvent.MOUSE_EXITED) {
            //mouse exits WND
            mouseExited(((MouseEvent)e).getLocationOnScreen());
          }
        }
        return false;
      }
    }, parent);

    UISettings.getInstance().addUISettingsListener(this, this);
    KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", this);
    myAlarm = new Alarm(parent);
  }

  public boolean mouseExited(Point currentLocationOnScreen) {
    myAlarm.cancelAllRequests();
    if (popup != null && popup.isVisible()) {
      final Point screen = popup.getLocationOnScreen();
      final Rectangle popupScreenRect = new Rectangle(screen.x, screen.y, popup.getSize().width, popup.getSize().height);
      if (! popupScreenRect.contains(currentLocationOnScreen)) {
        myAlarm.cancelAllRequests();
        myAlarm.addRequest(new Runnable() {
          @Override
          public void run() {
            if (popup != null && popup.isVisible()) {
              popup.cancel();
            }
          }
        }, 150);
        return true;
      }
    }
    return false;
  }

  public void mouseEntered() {
    if (myAlarm.getActiveRequestCount() == 0) {
      myAlarm.addRequest(new Runnable() {
        @Override
        public void run() {
          DefaultListModel model = new DefaultListModel();
          final IdeFrameImpl frame = UIUtil.getParentOfType(IdeFrameImpl.class, ToolWindowsWidget.this);
          if (frame == null) return;
          final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(frame.getProject());
          for (String id : toolWindowManager.getToolWindowIds()) {
            final ToolWindow tw = toolWindowManager.getToolWindow(id);
            if (tw.isAvailable()) {
              model.addElement(tw);
            }
          }

          final JBList list = new JBList(model);
          list.setCellRenderer(new ListCellRenderer() {
            final JBLabel label = new JBLabel();

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
              final ToolWindow toolWindow = (ToolWindow)value;
              label.setText(toolWindow instanceof ToolWindowImpl ? ((ToolWindowImpl)toolWindow).getId() : toolWindow.getTitle());
              label.setIcon(toolWindow.getIcon());
              label.setBorder(IdeBorderFactory.createEmptyBorder(4, 10, 4, 10));
              label.setForeground(UIUtil.getListForeground(isSelected));
              label.setBackground(UIUtil.getListBackground(isSelected));
              final JPanel panel = new JPanel(new BorderLayout());
              panel.add(label, BorderLayout.CENTER);
              panel.setBackground(UIUtil.getListBackground(isSelected));
              return panel;
            }
          });

          final Dimension size = list.getPreferredSize();
          final JComponent c = ToolWindowsWidget.this;
          final Insets padding = UIUtil.getListViewportPadding();
          final RelativePoint point = new RelativePoint(c, new Point(-4, -padding.top - padding.bottom -4 - size.height));

          if (popup != null && popup.isVisible()) {
            return;
          }

          list.setSelectedIndex(list.getItemsCount() - 1);
          PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(list);
          popup = builder
            .setAutoselectOnMouseMove(true)
            .setRequestFocus(false)
            .setItemChoosenCallback(new Runnable() {
              @Override
              public void run() {
                if (popup != null) popup.closeOk(null);
                final Object value = list.getSelectedValue();
                if (value instanceof ToolWindow) {
                  ((ToolWindow)value).activate(null, true, true);
                }
              }
            })
            .createPopup();

          popup.show(point);
        }
      }, 300);
    }
  }

  @Override
  public void addNotify() {
    super.addNotify();
    final String key = "toolwindow.stripes.buttons.info.shown";
    if (UISettings.getInstance().HIDE_TOOL_STRIPES && !PropertiesComponent.getInstance().isTrueValue(key)) {
      PropertiesComponent.getInstance().setValue(key, String.valueOf(true));
      final Alarm alarm = new Alarm();
      alarm.addRequest(new Runnable() {
        @Override
        public void run() {
          GotItMessage.createMessage(UIBundle.message("tool.window.quick.access.title"), UIBundle.message(
            "tool.window.quick.access.message"))
            .setDisposable(ToolWindowsWidget.this)
            .show(new RelativePoint(ToolWindowsWidget.this, new Point(10, 0)), Balloon.Position.above);
          Disposer.dispose(alarm);
        }
      }, 20000);
    }
  }

  @Override
  public void propertyChange(PropertyChangeEvent evt) {
    updateIcon();
  }

  @Override
  public void uiSettingsChanged(UISettings source) {
    updateIcon();
  }

  private void performAction() {
    if (isActive()) {
      UISettings.getInstance().HIDE_TOOL_STRIPES = !UISettings.getInstance().HIDE_TOOL_STRIPES;
      UISettings.getInstance().fireUISettingsChanged();
    }
  }

  private void updateIcon() {
    setToolTipText(null);
    if (isActive()) {
      boolean changes = false;

      if (!isVisible()) {
        setVisible(true);
        changes = true;
      }

      Icon icon = UISettings.getInstance().HIDE_TOOL_STRIPES ? AllIcons.General.TbShown : AllIcons.General.TbHidden;
      if (icon != getIcon()) {
        setIcon(icon);
        changes = true;
      }

      //Set<Integer> vks = ToolWindowManagerImpl.getActivateToolWindowVKs();
      //String text = "Click to show or hide the tool window bars";
      //if (vks.size() == 1) {
      //  Integer stroke = vks.iterator().next();
      //  String keystrokeText = KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(stroke.intValue(), 0));
      //  text += ".\nDouble-press and hold " + keystrokeText + " to show tool window bars when hidden.";
      //}
      //if (!text.equals(getToolTipText())) {
      //  setToolTipText(text);
      //  changes = true;
      //}

      if (changes) {
        revalidate();
        repaint();
      }
    }
    else {
      setVisible(false);
      setToolTipText(null);
    }
  }

  private boolean isActive() {
    return myStatusBar != null && myStatusBar.getFrame() != null && myStatusBar.getFrame().getProject() != null && Registry
      .is("ide.windowSystem.showTooWindowButtonsSwitcher");
  }

  @Override
  public JComponent getComponent() {
    return this;
  }

  @NotNull
  @Override
  public String ID() {
    return "ToolWindows Widget";
  }

  @Override
  public WidgetPresentation getPresentation(@NotNull PlatformType type) {
    return null;
  }

  @Override
  public void install(@NotNull StatusBar statusBar) {
    myStatusBar = statusBar;
    updateIcon();
  }

  @Override
  public void dispose() {
    Disposer.dispose(this);
    KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener("focusOwner", this);
    myStatusBar = null;
    popup = null;
  }
}
