import java.awt.*;
import java.awt.image.*;
import java.awt.Graphics2D;
import java.io.*;
import javax.imageio.ImageIO;

public class Demo {
	private static int width, height;
	private static String configFile;
	private static BufferedImage image;
	private static BufferedImage background;
	private static Graphics2D graphics;
	private static byte[] transferBuffer;
	private static final double lensWidth = 0.2;
	private static final double lensHeight = 0.2;
	private static int[][] shiftx;
	private static int[][] shifty;

	public static void init(int width_, int height_, String configFile_) throws Exception {
		System.out.println("Running Java init method: width=" + width_
				+ " height=" + height_ + " configFile=" + configFile_);

		width = width_;
		height = height_;
		configFile = configFile_;
		image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
		background = ImageIO.read(new FileInputStream(new File(configFile_)));
		graphics = image.createGraphics();
		transferBuffer = new byte[width * height * 4];

	}

	public static void adjustColor(int a[]) {
		a[0] = (int)(0.7*a[0]);
		a[1] = (int)(0.7*a[1]);
		a[2] = (int)(0.9*a[2]);
	}
	
	private static void buildLookup(int dx, int dy) {
		shiftx = new int[dx*2+1][dy*2+1];
		shifty = new int[dx*2+1][dy*2+1];
		final int R = dx;
		final int R2 = R * R;
		for (int i=-dx; i<=dx; i++) {
			for (int j=-dy; j<=dy; j++) {
				int sx, sy;
				double currentRadius2 = Math.pow(i, 2) + Math.pow(j, 2);
				// double theta = Math.atan2(j, i);
				if (currentRadius2 >= R2) {
					sx = Integer.MAX_VALUE;
					sy = Integer.MAX_VALUE;
				} else {
					double foo2 = currentRadius2/R2;
					// sx = i + (int)(foo*foo*currentRadius*Math.cos(theta));
					// sy = j + (int)(foo*foo*currentRadius*Math.sin(theta));
					sx = i + (int)(foo2*i);
					sy = j + (int)(foo2*j);
				}
				shiftx[dx+i][dy+j] = sx;
				shifty[dx+i][dy+j] = sy;
			}
		}
	}
		
	public static void drawLens(double x, double y) {
		WritableRaster outputRaster = image.getRaster();
		WritableRaster inputRaster = background.getRaster();
		int mx = (int)(x*image.getWidth());
		int my = (int)(y*image.getHeight());
		int dx = (int)(lensWidth*image.getWidth());
		int dy = (int)(lensHeight*image.getHeight());
		final int R = dx;
		if (shiftx == null) {
			buildLookup(dx, dy);
		}
		for (int i=-dx; i<=dx; i++) {
			int curX = mx + i;
			for (int j=-dy; j<=dy; j++) {
				int curY = my + j;
				if (shiftx[dx+i][dy+j] == Integer.MAX_VALUE) {
					continue;
				}
				int sampleX = mx + shiftx[dx+i][dy+j];
				int sampleY = my + shifty[dx+i][dy+j];
				//System.out.println("(" + sampleX + ", "  + sampleY + ")\n");
				if (sampleX < 0)
					sampleX = 0;
				if (sampleX >= image.getWidth())
					sampleX = image.getWidth()-1;
				if (sampleY < 0)
					sampleY = 0;
				if (sampleY >= image.getHeight())
					sampleY = image.getHeight()-1;
				
				int tempA[] = inputRaster.getPixel(sampleX, sampleY, (int []) null);
				adjustColor(tempA);
				outputRaster.setPixel(curX, curY, tempA);
			}
		}
	}
	
	public static byte[] render(int timeInMillis) {
		final int cycleTime = 4000;
		int wrappedTime = timeInMillis % cycleTime;
		double xloc, yloc, safety;
		if (wrappedTime < cycleTime/2) {
			xloc = ((double) wrappedTime) / (cycleTime/2);
		} else {
			xloc = 2.0 - 2.0*((double) wrappedTime / cycleTime);
		}
		yloc = 1.0 - Math.abs((Math.cos(2.0*Math.PI*timeInMillis/3000)));
		
		// Do not go out of screen.
		xloc = lensWidth + (1.0-2*lensWidth)*xloc;
		yloc = lensHeight + (1.0-2*lensHeight)*yloc;
		
		graphics.clearRect(0, 0, width, height);
		graphics.drawImage(background, 0, 0, null);
		graphics.setColor(Color.WHITE);
		drawLens(xloc, yloc);
		return (byte[]) image.getRaster().getDataElements(0, 0, width, height,
				transferBuffer);
	}

	public static void gc() {
		System.out.println("Java: Running gc.");
		Runtime.getRuntime().gc();
		System.out.println("Java: gc finished.");
	}
}
