
package net.filebot.util.ui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;
import javax.swing.Timer;

public class ProgressIndicator extends JComponent {

	private float radius = 4.0f;
	private int shapeCount = 3;

	private float strokeWidth = 2f;
	private Stroke stroke = new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);

	private final Rectangle2D frame = new Rectangle2D.Double();
	private final Ellipse2D circle = new Ellipse2D.Double();
	private final Dimension baseSize = new Dimension(32, 32);

	private double alpha = 0;
	private double speed = 24;

	private Timer updateTimer;

	private final Color progressShapeColor;
	private final Color backgroundShapeColor;

	public ProgressIndicator(Color progressShapeColor, Color backgroundShapeColor) {
		this.progressShapeColor = progressShapeColor;
		this.backgroundShapeColor = backgroundShapeColor;

		setPreferredSize(baseSize);

		addComponentListener(new ComponentAdapter() {

			@Override
			public void componentShown(ComponentEvent e) {
				startAnimation();
			}

			@Override
			public void componentHidden(ComponentEvent e) {
				stopAnimation();
			}
		});
	}

	public void animateOnce() {
		if ((alpha += (speed / 1000)) >= 1) {
			alpha -= Math.floor(alpha);
		}
	}

	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;

		double a = Math.min(getWidth(), getHeight());

		g2d.scale(a / baseSize.width, a / baseSize.height);

		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		frame.setFrame(radius, radius, baseSize.width - radius * 2 - 1, baseSize.height - radius * 2 - 1);

		paintShapes(g2d);
	}

	private void paintShapes(Graphics2D g2d) {
		circle.setFrame(frame);

		g2d.setStroke(stroke);
		g2d.setPaint(backgroundShapeColor);

		g2d.draw(circle);

		Point2D center = new Point2D.Double(frame.getCenterX(), frame.getMinY());

		circle.setFrameFromCenter(center, new Point2D.Double(center.getX() + radius, center.getY() + radius));

		g2d.setStroke(stroke);
		g2d.setPaint(progressShapeColor);

		// base rotation
		g2d.rotate(getTheta(alpha, 1.0), frame.getCenterX(), frame.getCenterY());

		double theta = getTheta(1, shapeCount);

		for (int i = 0; i < shapeCount; i++) {
			g2d.rotate(theta, frame.getCenterX(), frame.getCenterY());
			g2d.fill(circle);
		}
	}

	private double getTheta(double value, double max) {
		return (value / max) * 2 * Math.PI;
	}

	public void startAnimation() {
		if (updateTimer == null) {
			updateTimer = new Timer(20, new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					animateOnce();
					repaint();
				}
			});

			updateTimer.start();
		}
	}

	public void stopAnimation() {
		if (updateTimer != null) {
			updateTimer.stop();
			updateTimer = null;
		}
	}

	public void setShapeCount(int indeterminateShapeCount) {
		this.shapeCount = indeterminateShapeCount;
	}

	public void setSpeed(double speed) {
		this.speed = speed;
	}

	public double getSpeed() {
		return speed;
	}

}
