/*
 * Copyright 2000-2012 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.util.ui;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;

import javax.swing.*;
import java.awt.*;

/**
 * @author Kirill Kalishev
 * @author Konstantin Bulenkov
 */
public class AnimatedIcon extends JComponent implements Disposable {
  private final Icon[] myIcons;
  private final Dimension myPrefSize = new Dimension();

  //no need to make it volatile, all r/w operations are from EDT
  private int myCurrentIconIndex;

  private final Icon myPassiveIcon;
  private final Icon myEmptyPassiveIcon;

  private boolean myPaintPassive = true;
  private boolean myRunning = true;

  protected final Animator myAnimator;

  private final String myName;

  //private boolean myPaintingBgNow;

  public AnimatedIcon(final String name, Icon[] icons, Icon passiveIcon, int cycleLength) {
    myName = name;
    myIcons = icons.length == 0 ? new Icon[]{passiveIcon} : icons;
    myPassiveIcon = passiveIcon;

    for (Icon each : icons) {
      myPrefSize.width = Math.max(each.getIconWidth(), myPrefSize.width);
      myPrefSize.height = Math.max(each.getIconHeight(), myPrefSize.height);
    }

    myPrefSize.width = Math.max(passiveIcon.getIconWidth(), myPrefSize.width);
    myPrefSize.height = Math.max(passiveIcon.getIconHeight(), myPrefSize.height);

    myAnimator = new Animator(myName, icons.length, cycleLength, true) {
      @Override
      public void paintNow(final int frame, final int totalFrames, final int cycle) {
        final int len = myIcons.length;
        myCurrentIconIndex = frame < 0 ? 0 : frame >= len ? len - 1 : frame;
        paintImmediately(0, 0, getWidth(), getHeight());
      }
    };

    if (icons.length > 0) {
      myEmptyPassiveIcon = EmptyIcon.create(icons[0]);
    } else {
      myEmptyPassiveIcon = EmptyIcon.ICON_0;
    }

    setOpaque(false);
  }

  public void setPaintPassiveIcon(boolean paintPassive) {
    myPaintPassive = paintPassive;
  }

  private boolean ensureAnimation(boolean running) {
    boolean changes = myAnimator.isRunning() != running;

    if (running) {
      myAnimator.resume();
    } else {
      myAnimator.suspend();
    }

    return changes;
  }

  @Override
  public void addNotify() {
    super.addNotify();
    if (myRunning) {
      ensureAnimation(true);
    }
  }

  @Override
  public void removeNotify() {
    super.removeNotify();
    ensureAnimation(false);
  }

  public void resume() {
    myRunning = true;
    ensureAnimation(true);
  }

  public void suspend() {
    myRunning = false;
    if (ensureAnimation(false)) {
      repaint();
    }
  }

  @Override
  public void dispose() {
    Disposer.dispose(myAnimator);
  }

  @Override
  public Dimension getPreferredSize() {
    final Insets insets = getInsets();
    return new Dimension(myPrefSize.width + insets.left + insets.right, myPrefSize.height + insets.top + insets.bottom);
  }

  @Override
  public Dimension getMinimumSize() {
    return getPreferredSize();
  }

  @Override
  public Dimension getMaximumSize() {
    return getPreferredSize();
  }

  @Override
  protected void paintComponent(Graphics g) {
    //if (myPaintingBgNow) return;

    if (isOpaque()) {
      final Container parent = getParent();
      JComponent opaque = null;
      if (parent instanceof JComponent) {
        opaque = (JComponent)UIUtil.findNearestOpaque((JComponent)parent);
      }
      Color bg = opaque != null ? opaque.getBackground() : UIUtil.getPanelBackground();
      g.setColor(bg);
      g.fillRect(0, 0, getWidth(), getHeight());
    } 

    Icon icon;

    if (myAnimator.isRunning()) {
      icon = myIcons[myCurrentIconIndex];
    } else {
      icon = getPassiveIcon();
    }

    final Dimension size = getSize();
    int x = (size.width - icon.getIconWidth()) / 2;
    int y = (size.height - icon.getIconHeight()) / 2;

    paintIcon(g, icon, x, y);

    //boolean lastPaintWasRunning = myAnimator.isRunning();
  }

  protected void paintIcon(Graphics g, Icon icon, int x, int y) {
    icon.paintIcon(this, g, x, y);
  }

  protected Icon getPassiveIcon() {
    return myPaintPassive ? myPassiveIcon : myEmptyPassiveIcon;
  }

  public boolean isRunning() {
    return myAnimator.isRunning();
  }

  @Override
  public String toString() {
    return myName + " isRunning=" + myRunning + " isOpaque=" + isOpaque() + " paintPassive=" + myPaintPassive;
  }
}

@@ -708,16 +708,118 @@
 isposer;
+%0Aimport com.intellij.util.ui.update.Activatable;%0Aimport com.intellij.util.ui.update.UiNotifyConnector;
 %0A%0Aimport
@@ -2532,32 +2532,308 @@
 etOpaque(false);
+%0A%0A    new UiNotifyConnector(this, new Activatable() %7B%0A      @Override%0A      public void showNotify() %7B%0A        if (myRunning) %7B%0A          ensureAnimation(true);%0A        %7D%0A      %7D%0A%0A      @Override%0A      public void hideNotify() %7B%0A        ensureAnimation(false);%0A      %7D%0A    %7D);
 %0A  %7D%0A%0A  public v
@@ -3154,234 +3154,8 @@
  %7D%0A%0A
-  @Override%0A  public void addNotify() %7B%0A    super.addNotify();%0A    if (myRunning) %7B%0A      ensureAnimation(true);%0A    %7D%0A  %7D%0A%0A  @Override%0A  public void removeNotify() %7B%0A    super.removeNotify();%0A    ensureAnimation(false);%0A  %7D%0A%0A
   pu

stop AnimatedIcon when it's hidden