package net.sf.jasperreports.engine;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.net.URLStreamHandlerFactory;
import net.sf.jasperreports.engine.util.FileResolver;
import net.sf.jasperreports.engine.util.JRImageLoader;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRResourcesUtil;
import net.sf.jasperreports.engine.util.JRTypeSniffer;

public class JRImageRenderer extends JRAbstractRenderer {
  private static final long serialVersionUID = 10200L;
  
  private byte[] imageData = null;
  
  private String imageLocation = null;
  
  private byte imageType = 0;
  
  private transient SoftReference awtImageRef = null;
  
  protected JRImageRenderer(byte[] imageData) {
    this.imageData = imageData;
    if (imageData != null)
      this.imageType = JRTypeSniffer.getImageType(imageData); 
  }
  
  protected JRImageRenderer(String imageLocation) {
    this.imageLocation = imageLocation;
  }
  
  public static ClassLoader getClassLoader() {
    return JRResourcesUtil.getThreadClassLoader();
  }
  
  public static void setClassLoader(ClassLoader classLoader) {
    JRResourcesUtil.setThreadClassLoader(classLoader);
  }
  
  public static JRImageRenderer getInstance(byte[] imageData) {
    return new JRImageRenderer(imageData);
  }
  
  public static JRRenderable getInstance(String imageLocation) throws JRException {
    return getInstance(imageLocation, (byte)1, true);
  }
  
  public static JRRenderable getInstance(String imageLocation, byte onErrorType) throws JRException {
    return getInstance(imageLocation, onErrorType, true);
  }
  
  public static JRRenderable getInstance(String imageLocation, byte onErrorType, boolean isLazy) throws JRException {
    return getInstance(imageLocation, onErrorType, isLazy, null, null, null);
  }
  
  public static JRRenderable getInstance(String imageLocation, byte onErrorType, boolean isLazy, ClassLoader classLoader, URLStreamHandlerFactory urlHandlerFactory, FileResolver fileResolver) throws JRException {
    if (imageLocation == null)
      return null; 
    if (isLazy)
      return new JRImageRenderer(imageLocation); 
    try {
      byte[] data = JRLoader.loadBytesFromLocation(imageLocation, classLoader, urlHandlerFactory, fileResolver);
      return new JRImageRenderer(data);
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getInstance(Image img, byte onErrorType) throws JRException {
    return getInstance(img, (byte)2, onErrorType);
  }
  
  public static JRRenderable getInstance(Image image, byte imageType, byte onErrorType) throws JRException {
    try {
      return new JRImageRenderer(JRImageLoader.loadImageDataFromAWTImage(image, imageType));
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getInstance(InputStream is, byte onErrorType) throws JRException {
    try {
      return new JRImageRenderer(JRLoader.loadBytes(is));
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getInstance(URL url, byte onErrorType) throws JRException {
    try {
      return new JRImageRenderer(JRLoader.loadBytes(url));
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getInstance(File file, byte onErrorType) throws JRException {
    try {
      return new JRImageRenderer(JRLoader.loadBytes(file));
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getOnErrorRendererForDimension(JRRenderable renderer, byte onErrorType) throws JRException {
    try {
      renderer.getDimension();
      return renderer;
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRRenderable getOnErrorRendererForImageData(JRRenderable renderer, byte onErrorType) throws JRException {
    try {
      renderer.getImageData();
      return renderer;
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  public static JRImageRenderer getOnErrorRendererForImage(JRImageRenderer renderer, byte onErrorType) throws JRException {
    try {
      renderer.getImage();
      return renderer;
    } catch (JRException e) {
      return getOnErrorRenderer(onErrorType, e);
    } 
  }
  
  private static JRImageRenderer getOnErrorRenderer(byte onErrorType, JRException e) throws JRException {
    JRImageRenderer renderer = null;
    switch (onErrorType) {
      case 3:
        renderer = new JRImageRenderer("net/sf/jasperreports/engine/images/noimage.GIF");
      case 2:
        return renderer;
    } 
    throw e;
  }
  
  public Image getImage() throws JRException {
    if (this.awtImageRef == null || this.awtImageRef.get() == null) {
      Image awtImage = JRImageLoader.loadImage(getImageData());
      this.awtImageRef = new SoftReference(awtImage);
    } 
    return this.awtImageRef.get();
  }
  
  public String getImageLocation() {
    return this.imageLocation;
  }
  
  public byte getType() {
    return 0;
  }
  
  public byte getImageType() {
    return this.imageType;
  }
  
  public Dimension2D getDimension() throws JRException {
    Image img = getImage();
    return new Dimension(img.getWidth(null), img.getHeight(null));
  }
  
  public byte[] getImageData() throws JRException {
    if (this.imageData == null) {
      this.imageData = JRLoader.loadBytesFromLocation(this.imageLocation);
      if (this.imageData != null)
        this.imageType = JRTypeSniffer.getImageType(this.imageData); 
    } 
    return this.imageData;
  }
  
  public void render(Graphics2D grx, Rectangle2D rectanle) throws JRException {
    Image img = getImage();
    grx.drawImage(img, (int)rectanle.getX(), (int)rectanle.getY(), (int)rectanle.getWidth(), (int)rectanle.getHeight(), null);
  }
}
