/**
 * This software is released as part of the Pumpernickel project.
 * 
 * All com.pump resources in the Pumpernickel project are distributed under the
 * MIT License:
 * https://github.com/mickleness/pumpernickel/raw/master/License.txt
 * 
 * More information about the Pumpernickel project is available here:
 * https://mickleness.github.io/pumpernickel/
 */
package com.pump.image.transition;

import java.awt.Dimension;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * This is a playful series of dots bubbling up from nowhere to produce the
 * incoming image. Here is a playback sample:
 * <p>
 * <img src=
 * "https://github.com/mickleness/pumpernickel/raw/master/resources/transition/DotsTransition2D/Dots.gif"
 * alt="DotsTransition2D Demo">
 * 
 */
public class DotsTransition2D extends AbstractClippedTransition2D {

	Ellipse2D[] bubbles = new Ellipse2D[] {
			make(0.680542285974303, 0.18889704343632806, 0.19951594932207756),
			make(0.026909280976121663, 0.6901511430360959, 0.1906895473555708),
			make(199.0 / 200, 88.0 / 200, 0.03),
			make(0.29737678359980557, 0.3283705267534811, 0.0912077460942279),
			make(0.4940590401790048, 0.02553734599175539, 0.16311957162512475),
			make(0.43494816182266074, 0.25633754562508044, 0.1165154509890553),
			make(0.9809361717371541, 0.30209191539494906, 0.13332448677092352),
			make(0.94840475449474, 0.9709666965127903, 0.1916617398526077),
			make(0.20824174526117323, 0.6000480763996424, 0.16450145156027104),
			make(49.0 / 200, 6.0 / 200, 0.1264350652695335),
			make(0.63129647161756, 0.7748508554123787, 0.13777349084037474),
			make(24.0 / 200.0, 23.0 / 200.0, 0.05665589165107964),
			make(0.04541801270979995, 0.31964490650135213, 0.19585902380194273),
			make(0.568725862339568, 0.5659780448945103, 0.10445627774737297),
			make(113.0 / 200.0, 76.0 / 200.0, 0.10292414822016213),
			make(0.9407853703745508, 0.7099945865685147, 0.14021507084018198),
			make(0.8430323086248085, 0.5875060184753639, 0.19975669996812462),
			make(0.7908761878447338, 0.9138289915660384, 0.14630462931216134),
			make(0.7885669941774602, 0.45314728864907283, 0.19542608515347965),
			make(0.3908460558351622, 0.4880566885333508, 0.19958992150674115),
			make(0.2543162939332376, 0.16250491418006419, 0.13575009482043436),
			make(0.3009613498958983, 0.7949264449270182, 0.16819513825380095),
			make(0.007611521495801465, 0.015461107683142572,
					0.13790660268586205),
			make(0.18134036468339323, 0.9533112934957934, 0.19875969340753744),
			make(0.43773209673997937, 0.6987587742834488, 0.1450244188942148),
			make(0.5586433953881341, 0.9556322415516078, 0.18462455447167833),
			make(0.9464285339584122, 0.034394073749900445, 0.21788008758584236) };

	/**
	 * Creates a new dots transition.
	 * 
	 */
	public DotsTransition2D() {
		// here's the code that helps generate the list of bubbles:
		// note the final list was then pruned by hand and tweaked to really cut
		// back
		// on the number of bubbles.
		/*
		 * int bubbleCount = 20; long seed = System.currentTimeMillis(); boolean
		 * passed = false; int ctr = 0; while(passed==false) { ctr++; bubbles =
		 * new Ellipse2D[bubbleCount+ctr/5000];
		 * System.out.println(seed+", "+bubbles.length); Random r = new
		 * Random(seed); for(int a = 0; a<bubbles.length; a++) { bubbles[a] =
		 * make( r.nextDouble(), r.nextDouble(), .1+.1*r.nextDouble() ); }
		 * BasicShape sum = new BasicShape(); for(int a = 0; a<bubbles.length;
		 * a++) { Ellipse2D e = bubbles[a]; AffineTransform t = new
		 * AffineTransform(); t.rotate(2,e.getCenterX(),e.getCenterY());
		 * sum.add(new BasicShape(t.createTransformedShape(e))); } passed =
		 * sum.contains(new Rectangle(0,0,1,1)); seed++; } for(int a = 0;
		 * a<bubbles.length; a++) {
		 * System.out.println("make( "+bubbles[a].getCenterX
		 * ()+", "+bubbles[a].getCenterY()+", "+bubbles[a].getWidth()/2+" )"); }
		 */
	}

	protected Ellipse2D.Double make(double x, double y, double r) {
		return new Ellipse2D.Double(x - r, y - r, r * 2, r * 2);
	}

	@Override
	public Shape[] getShapes(float progress, Dimension size) {
		List<Shape> v = new ArrayList<Shape>();
		float domain = .9f;
		float span = 1 - domain;
		Random random = new Random();

		for (int a = 0; a < bubbles.length; a++) {
			float k = (a) / (bubbles.length - 1f);
			float base = (float) (Math.sqrt(k) * .25 + k * .75) * domain;

			float p = (progress - base) / span;
			if (p > 0.01) {
				if (p > 1)
					p = 1;

				// pop it over 1:
				p = (float) (-1.6666666666666186 * p * p + 2.6666666666666203 * p);

				float r = (float) Math.max(bubbles[a].getWidth() * size.width
						/ 2, bubbles[a].getHeight() * size.height / 2);
				r = r * p;

				random.setSeed(10 * a);
				float dx = (1 - p) * (2 * random.nextFloat() - 1);
				float dy = (1 - p) * (2 * random.nextFloat() - 1);
				dx = (1 - p)
						* (float) (r * Math.cos(random.nextFloat() * 10 + 8
								* (1 - p)));
				dy = (1 - p)
						* (float) (r * Math.sin(random.nextFloat() * 10 + 8
								* (1 - p)));

				v.add(new Ellipse2D.Double(bubbles[a].getCenterX() * size.width
						- r + dx, bubbles[a].getCenterY() * size.height - r
						+ dy, 2 * r, 2 * r));
			}
		}

		return v.toArray(new Shape[v.size()]);
	}

	@Override
	public float getStrokeWidth(float progress, Dimension size) {
		float f = Math.max(size.width - 300f, 0);
		f = f / 700f + .3f;
		return (float) (10 * (1 - Math.pow(progress, 5))) * f;
	}

	@Override
	public String toString() {
		return "Dots";
	}
}