package org.wikiwebserver.distribute.se.worker.task;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.wikiwebserver.distribute.interfaces.Generator;
import org.wikiwebserver.distribute.interfaces.Task;
import org.wikiwebserver.distribute.se.ConfigManager;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class ImagePostTask extends Task implements Generator {
    
    public static final String[] IMAGE_EXTS = ImageListingTask.IMAGE_EXTS;     

	private static final long serialVersionUID = 1L;
	
	private File file = null;
	private byte[] resizedImageData = null;
	
	public Object generate() throws IOException {

	    int idx = getTaskInputMeta().indexOf('|');
	    int targetSize = Integer.parseInt(getTaskInputMeta().substring(0, idx));
	    String path = getTaskInputMeta().substring(idx+1);
	    
        file = ConfigManager.getFileForPath(path);	    
	    
		if (!file.exists()) {
			throw new FileNotFoundException(file.getPath());
		}		
		
		BufferedImage resizedImage = getScaledImage(file, targetSize);

        resizedImageData = getBytes(resizedImage);
        
        setTaskOutputMeta(String.valueOf(getStreamLength()));     
        
        return resizedImageData;
	}
	
	
	
    public static int getDimension(URL url, boolean max) throws IOException {
        return getDimension((Object)url, max);
    }
    
    public static int getDimension(File file, boolean max) throws IOException {
        return getDimension((Object)file, max);
    }    
    
    private static int getDimension(Object obj, boolean max) throws IOException {

        ImageInputStream iis = null;
        try {
            if (obj instanceof URL) {
                obj = ((URL)obj).openStream();
            }
            iis = ImageIO.createImageInputStream(obj);
            if (iis == null) {
                throw new FileNotFoundException(obj + " not found");
            }
            Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
            ImageReader reader = iter.next();
            reader.setInput(iis);
            
            int w = reader.getWidth(0);
            int h = reader.getHeight(0);
            
            return max ? (w > h ? w : h) : (w < h ? w : h);
        }
        catch (IOException ex) {
            throw ex;
        }
        finally {
            if (iis != null) iis.close();
        }
    } 
    
    public static BufferedImage getScaledImage(URL url, int targetSize) throws IOException {
        return getScaledImage((Object)url, targetSize);
    }
    
    public static BufferedImage getScaledImage(File file, int targetSize) throws IOException {
        return getScaledImage((Object)file, targetSize);
    }    
    
    private static BufferedImage getScaledImage(Object obj, int targetSize) throws IOException {  
        
        int maxDim = getDimension(obj, true);
        
        int factor = maxDim / targetSize;
        if (factor < 1) factor = 1;     
        
        BufferedImage sourceImage = getImage(obj, factor);
        
        int w = sourceImage.getWidth();
        int h = sourceImage.getHeight();
        float r = (float)w/h;
        
        int width, height;
        if (r > 1) {
            width = Math.min(targetSize, w);
            height = (int) (width/r);
        } else {
            height = Math.min(targetSize, h);
            width = (int) (height*r);
        }
        if (width == 0) width = 1;
        if (height == 0) height = 1;        
        
        
        BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = resizedImage.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        
        g2d.drawImage(sourceImage, 0, 0, width, height, Color.white, null);  
        
        return resizedImage;
    }
    
    public static BufferedImage getImage(URL url, int subsample) throws IOException {
        return getImage((Object)url, subsample);
    }
    
    public static BufferedImage getImage(File file, int subsample) throws IOException {
        return getImage((Object)file, subsample);
    }    
        
    private static BufferedImage getImage(Object obj, int subsample) throws IOException {

        ImageInputStream iis = null;
        try {
            if (obj instanceof URL) {
                obj = ((URL)obj).openStream();
            }            
            iis = ImageIO.createImageInputStream(obj);
            Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
            ImageReader reader = iter.next();
            reader.setInput(iis);
            ImageReadParam param = reader.getDefaultReadParam();
            param.setSourceSubsampling(subsample, subsample, 0, 0);
            
            return reader.read(0, param);  
        }
        catch (IOException ex) {
            throw ex;
        }
        finally {
            if (iis != null) iis.close();
        }
    }	
    
    public static byte[] getBytes(BufferedImage image) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
            param.setQuality(0.90f, false);
            encoder.setJPEGEncodeParam(param);
            encoder.encode(image);
        } finally {
            out.close(); 
            image.flush();
        }
        return out.toByteArray();
    }    
	
	public long getStreamLength() {
		return resizedImageData.length;
	}
	
	public String toString() {
	    if (file == null) return "Starting...";
		return "Posting resized image " + file;
	}	
}

