package charactermanaj.graphics.io;

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

import javax.imageio.ImageIO;



public class ImageCachedLoader implements ImageLoader {

	private int cacheSize;
	
	private long sweepThreshold;
	
	private LinkedList<ImageCache> caches = new LinkedList<ImageCache>();
	
	public ImageCachedLoader(int cacheSize, long sweepThreshold) {
		this.cacheSize = cacheSize;
		this.sweepThreshold = sweepThreshold;
	}
	
	public BufferedImage load(ImageResource imageResource) throws IOException {
		if (imageResource == null) {
			throw new IllegalArgumentException();
		}

		final long now = System.currentTimeMillis();
		final long lastModified = imageResource.lastModified();

		// キャッシュ済みの検索
		BufferedImage image = null;
		synchronized (caches) {
			Iterator<ImageCache> ite = caches.iterator();
			while (ite.hasNext()) {
				ImageCache cache = ite.next();
				if (imageResource.equals(cache.getImageResource())
						&& cache.getLastModified() == lastModified) {
					cache.setLastUsed(now);
					ite.remove();
					caches.addFirst(cache);
					image = cache.getImage();
					break;
				}
			}
		}
		
		if (image == null) {
			// 画像のロード
			image = ImageIO.read(imageResource.openStream());
			if (image == null) {
				throw new IOException("unsupported image");
			}

			// ARGB形式でなければ変換する.
			int typ = image.getType();
			if (typ != BufferedImage.TYPE_INT_ARGB) {
				BufferedImage img2 = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
				Graphics g = img2.getGraphics();
				try {
					g.drawImage(image, 0, 0, null);
				} finally {
					g.dispose();
				}
				image = img2;
			}
			
			// キャッシュに格納
			ImageCache cache = new ImageCache();
			cache.setImageResource(imageResource);
			cache.setLastModified(lastModified);
			cache.setLastUsed(now);
			cache.setImage(image);
			synchronized (caches) {
				caches.addFirst(cache);
			}
		}

		sweep();
		
		return image;
	}
	
	/**
	 * 期限切れキャッシュのスウィープ
	 */
	public void sweep() {
		sweep(null);
	}

	/**
	 * 期限切れキャッシュおよび指定ファイルのキャッシュのスウィープ.<br>
	 * @param file 指定ファイル、null可
	 */
	public void sweep(File file) {
		final long now = System.currentTimeMillis();

		synchronized (caches) {
			// 期限切れキャッシュ、および指定ファイルのスウィープ
			Iterator<ImageCache> ite = caches.iterator();
			while (ite.hasNext()) {
				ImageCache cache = ite.next();
				if ((now - cache.getLastUsed()) > sweepThreshold) {
					ite.remove();
				} else if (file != null && file.equals(cache.getImageResource())) {
					ite.remove();
					file = null;
				}
			}
			// 最大数を超えたものをスウィープ
			while (caches.size() > cacheSize) {
				caches.removeLast();
			}
		}
	}
}


class ImageCache {
	
	private ImageResource imageResource;
	
	private long lastModified;
	
	private long lastUsed;
	
	private BufferedImage image;

	public ImageResource getImageResource() {
		return imageResource;
	}

	public void setImageResource(ImageResource imageResource) {
		this.imageResource = imageResource;
	}

	public long getLastModified() {
		return lastModified;
	}

	public void setLastModified(long lastModified) {
		this.lastModified = lastModified;
	}

	public long getLastUsed() {
		return lastUsed;
	}

	public void setLastUsed(long lastUsed) {
		this.lastUsed = lastUsed;
	}

	public BufferedImage getImage() {
		return image;
	}

	public void setImage(BufferedImage image) {
		this.image = image;
	}
}
