/*
 * Copyright (C) oStorybook Team
 *
 * This program is free software, see <http://www.gnu.org/licenses/>.
 *
 * Parts of this code are from the NovaWorx project. Other parts are
 * from the SHEF project developed and published by Bob Tantlinger.
 */
package api.shef.tools;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.text.Element;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.ImageView;

/**
 *
 * An ImageView which caches images to a local directory after they're initially loaded. After the images are cached,
 * they're subsequently loaded from the local directory, thereby reducing the overhead of refetching them from their
 * remote location.
 *
 * @author Bob Tantlinger
 *
 */
public class CachedImageView extends ImageView {

	public static final String USER_HOME = System.getProperty("user.home");
	/**
	 * Directory where app properties are stored
	 */
	public static final String PROP_DIR = USER_HOME + File.separator + ".SHEF";
	public static final String IMG_CACHE_DIR = PROP_DIR + File.separator + "img_cache";

	//flag which keeps track of if the image has been writen to disk
	private boolean hasCached;

	public CachedImageView(Element elem) {
		super(elem);
	}

	@Override
	public void paint(Graphics g, Shape a) {
		super.paint(g, a);

		Image img = getImage();

		//test if the image is completely loaded.
		int flags = Toolkit.getDefaultToolkit().checkImage(
				img, -1, -1, getContainer());
		boolean done = ((flags & (ImageObserver.ALLBITS)) != 0);
		if (done && !hasCached) {
			//If it is, write it out to the cache
			cacheImage(img);
		}
	}

	/**
	 * Writes an image out to the cache
	 *
	 * @param image
	 */
	private void cacheImage(final Image image) {
		final File cImg = getCachedImg();
		if (image == null || cImg == null || cImg.exists()) {
			return;
		}
		hasCached = true;//set the hasCached flag so we dont write it twice
		Runnable runner = () -> {
			String type = "png";//assume png for non-jpegs
			if (cImg.getAbsolutePath().endsWith("jpg")) {
				type = "jpg";
			}

			BufferedImage bufImg = makeBufferedImage(image);
			try {
				ImageIO.write(bufImg, type, cImg);
			} catch (IOException ex) {
				ex.printStackTrace(System.err);
			}
		};
		Thread th = new Thread(runner, "Image Cacher");
		th.setPriority(Thread.NORM_PRIORITY);
		th.start();
	}

	/**
	 * Gets the real image URL as specified in the src attribute of the image tag
	 *
	 * @return
	 */
	private String srcAttribute() {
		return (String) getElement().getAttributes().getAttribute(HTML.Attribute.SRC);
	}

	/**
	 * Return a URL for the image source, or null if it could not be determined.
	 *
	 * If the image is cached, the local file URL is returned. The superclass treats this the same as the actual url,
	 * but the image is loaded from the local cache rather than redownloading it
	 */
	@SuppressWarnings("deprecation")
	@Override
	public URL getImageURL() {
		String src = srcAttribute();
		if (src == null) {
			return null;
		}
		URL imgURL = null;
		File diskCachedImg = getCachedImg();
		try {
			if (diskCachedImg.exists()) {
				imgURL = diskCachedImg.toURL();
			} else {
				URL reference = ((HTMLDocument) getDocument()).getBase();
				imgURL = new URL(reference, src);
			}
		} catch (MalformedURLException e) {

		}

		return imgURL;
	}

	private BufferedImage makeBufferedImage(Image image) {
		BufferedImage buffered;
		Graphics2D g2;
		buffered = new BufferedImage(image.getWidth(null),
				image.getHeight(null),
				BufferedImage.TYPE_INT_RGB);
		g2 = buffered.createGraphics();
		g2.drawImage(image, null, null);
		g2.dispose();
		return buffered;
	}

	/**
	 * Returns the local file of the cached version of the image. The local file is named with the absolute value of the
	 * hashcode for the actual image url and a "png" or "jpg" file extension
	 *
	 * @return
	 */
	private File getCachedImg() {
		String iUrl = srcAttribute();
		if (iUrl == null) {
			return null;
		}
		String imgType = ".png";
		if (iUrl.toLowerCase().endsWith(".jpg") || iUrl.toLowerCase().endsWith(".jpeg")) {
			imgType = ".jpg";
		}
		int hc = Math.abs(iUrl.hashCode());
		File cacheImg = new File(IMG_CACHE_DIR, hc + imgType);
		return cacheImg;
	}

}
