package charactermanaj.graphics;

import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;

import charactermanaj.graphics.filters.ColorConvertFilter;
import charactermanaj.graphics.filters.ColorConvertParameter;
import charactermanaj.graphics.filters.GammaTableFactory;
import charactermanaj.graphics.io.ImageCachedLoader;
import charactermanaj.graphics.io.ImageResource;


public class ColorConvertedImageCachedLoader {

	public static final int MAX_CACHE = 20;
	
	public static final long DEFAULT_SWEEP_THRESHOLD = 60 * 1000; // 1min
	
	private int cacheSize;
	
	private LinkedList<ColorConvertedImageCache> caches = new LinkedList<ColorConvertedImageCache>();
	
	private ImageCachedLoader loader;
	
	public ColorConvertedImageCachedLoader() {
		this(MAX_CACHE, DEFAULT_SWEEP_THRESHOLD);
	}
	
	public ColorConvertedImageCachedLoader(int cacheSize, long sweepThreshold) {
		if (cacheSize < 1) {
			cacheSize = 1;
		}
		this.cacheSize = cacheSize;
		this.loader = new ImageCachedLoader(cacheSize, sweepThreshold);
	}
	
	public BufferedImage load(ImageResource file, ColorConvertParameter colorConvParam) throws IOException {
		if (file == null) {
			throw new IllegalArgumentException();
		}
		if (colorConvParam == null) {
			colorConvParam = new ColorConvertParameter();
		}
		
		final long lastModified = file.lastModified();

		Iterator<ColorConvertedImageCache> ite = caches.iterator();
		while (ite.hasNext()) {
			ColorConvertedImageCache cache = ite.next();
			if (file.equals(cache.getImageResource())) {
				ite.remove();
				if (cache.getLastModified() == lastModified
						&& cache.getColorConvParam().equals(colorConvParam)) {
					caches.addFirst(cache);
					loader.sweep();
					return cache.getImage();
				}
				break;
			}
		}
		
		colorConvParam = (ColorConvertParameter) colorConvParam.clone();

		BufferedImage img = loader.load(file);
		img = colorConvert(img, colorConvParam);
		
		ColorConvertedImageCache cache = new ColorConvertedImageCache();
		cache.setImageResource(file);
		cache.setLastModified(lastModified);
		cache.setImage(img);
		cache.setColorConvParam(colorConvParam);
		
		caches.addFirst(cache);

		while (caches.size() > cacheSize) {
			caches.removeLast();
		}
		
		return img;
	}
	
	protected BufferedImage colorConvert(BufferedImage img, ColorConvertParameter param) {

		float[] factors = {
				param.getFactorR(),
				param.getFactorG(),
				param.getFactorB(),
				param.getFactorA(),
				};
		float[] offsets = {
				param.getOffsetR(),
				param.getOffsetG(),
				param.getOffsetB(),
				param.getOffsetA(),
				};
		RescaleOp rescale_op = new RescaleOp(factors, offsets, null);

		float[] gammas = {
				param.getGammaA(),
				param.getGammaR(),
				param.getGammaG(),
				param.getGammaB(),
				};
		float[] hsbs = {
				param.getHue(),
				param.getSaturation(),
				param.getBrightness()
				};
		ColorConvertFilter colorConvert_op = new ColorConvertFilter(
				param.getColorReplace(),
				hsbs,
				param.getGrayLevel(),
				new GammaTableFactory(gammas)
				);

		img = colorConvert_op.filter(img, null);
		img = rescale_op.filter(img, img);

		return img;
	}
}

class ColorConvertedImageCache {
	
	private ImageResource imageResource;
	
	private long lastModified;
	
	private ColorConvertParameter colorConvParam;
	
	private BufferedImage image;
	
	public ColorConvertParameter getColorConvParam() {
		return colorConvParam;
	}
	
	public void setColorConvParam(ColorConvertParameter colorConvParam) {
		this.colorConvParam = colorConvParam;
	}
	
	public ImageResource getImageResource() {
		return imageResource;
	}
	
	public BufferedImage getImage() {
		return image;
	}
	
	public long getLastModified() {
		return lastModified;
	}
	
	public void setImageResource(ImageResource imageResource) {
		this.imageResource = imageResource;
	}
	
	public void setImage(BufferedImage image) {
		this.image = image;
	}
	
	public void setLastModified(long lastModified) {
		this.lastModified = lastModified;
	}
}
