/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package dvi.browser;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.lang.reflect.InvocationTargetException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Icon;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;

import dvi.util.DviUtils;

public class DviLoadIndicator
implements Icon
{
  private static final Logger LOGGER = Logger.getLogger(DviLoadIndicator.class
      .getName());
  
  private final EventListenerList listenerList = new EventListenerList();
  
  public DviLoadIndicator()
  {
  }
  
  public void addChangeListener(ChangeListener listener)
  {
    listenerList.add(ChangeListener.class, listener);
  }
  
  public void removeChangeListener(ChangeListener listener)
  {
    listenerList.remove(ChangeListener.class, listener);
  }
  
  public void fireUpdateEvent()
  {
    ChangeEvent event = null;
    Object[] listeners = listenerList.getListenerList();
    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == ChangeListener.class) {
        if (event == null)
          event = new ChangeEvent(this);
        ((ChangeListener)listeners[i+1]).stateChanged(event);
      }
    }
  }
  
  private int period = 16;
  private int count = 0;
  private TimerTask task;
  private final Timer timer = new Timer();

  public synchronized void start()
  {
    if (task != null) {
      LOGGER.fine("Load indicator already started.");
      return;
    }
    task = new TimerTask() {
      public void run() {
        increment();
        try {
          SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
              fireUpdateEvent();
            }
          });
        } catch (InterruptedException e) {
          DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
        } catch (InvocationTargetException e) {
          DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
        }
      }
    };
    timer.scheduleAtFixedRate(task, 0, 100);
    fireUpdateEvent();
  }
  
  public synchronized boolean isActive()
  {
    return task != null;
  }
  
  public synchronized void stop()
  {
    if (task == null) {
      return;
    }
    task.cancel();
    task = null;
    fireUpdateEvent();
  }
  
  protected synchronized void increment()
  {
    count++;
  }
  
  public int getIconHeight()
  {
    return 16;
  }

  public int getIconWidth()
  {
    return 16;
  }

  public void paintIcon(Component c, Graphics g, int x, int y)
  {
    if (isActive()) {
      double r = 6;
      int N = 32;
      for (int i=0; i<N; i++) {
        double theta = 2 * Math.PI * (count * N + i * 8) / (period * N);
        int f = (int)(i * 255 / N);
        float h = f/2550.0f*2 + 0.50f;
        float s = f/255.0f;
        float b = 0.99f;
        s = 1.0f - (float) Math.pow(s, 3);
        Color col = Color.getHSBColor(h, s, b);
        drawRadial(g, col, x + 2, y + 2, theta, r);
      }
    } else {
      g.drawRoundRect(x, y, 15, 15, 1, 1);
    }
  }
  
  public void drawRadial(Graphics g, Color c, int x, int y, double theta, double r)
  {
    double dx = Math.cos(theta);
    double dy = Math.sin(theta);
    g.setColor(c);
    g.drawLine( (int)(x + r * (1 - dx))
              , (int)(y + r * (1 - dy))
              , (int)(x + r * (1 + dx))
              , (int)(y + r * (1 + dy))
              );
  }
}
