/**
 *
 */
package com.wisedu.coeus.util;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.JPEGDecodeParam;
import com.sun.media.jai.codec.JPEGEncodeParam;
import com.sun.media.jai.codec.PNGDecodeParam;
import com.sun.media.jai.codec.PNGEncodeParam;
import com.wisedu.coeus.core.exception.CoeusCoreException;
import com.wisedu.coeus.debug.Logger;
import com.wisedu.coeus.debug.LoggerFactory;
import com.wisedu.mooc.app.upload.exception.UploadException;

/**
 * @author fornane
 * @date 2012-1-5 下午04:27:38
 */
public class ImageTools {
    private static final Logger LOGGER =LoggerFactory.getLogger(ImageTools.class);

    public static final int SNAPSHOT_TYPE_DEFAULT = 0;      // 默认
    public static final int SNAPSHOT_TYPE_STRETCH = 1;      // 拉伸
    public static final int SNAPSHOT_TYPE_FILL = 2;         // 填充
    public static final int SNAPSHOT_TYPE_FIT = 3;          // 适应
    public static final int SNAPSHOT_ALIGN_LEFT =4;         //在3的基础上让图片左对齐，右边留白
    public static final int SNAPSHOT_TYPE_SCALE_FIT = 5;        //按照比例适应

    public static final int SNAPSHOT_TYPE_WIDTH_FIXED = 6;        //固定宽度，按比例缩放
    public enum ImageFormat {
        INVAILD,
        JPEG,
        PNG,
        GIF,
        BMP,
        TIFF,
        UNKNOWN;
    }

    private static ImageFormat getImageFormat(String filePath) {
        FileInputStream fis = null;
        ImageFormat imageFormat = ImageFormat.INVAILD;
        try {
            fis = new FileInputStream(filePath);
            byte[] header= new byte[10];
            fis.read(header);
            imageFormat = getImageFormat(header);
        } catch (Exception e) {

        } finally {
            FileUtils.closeStream(fis);
        }

        return imageFormat;
    }

    private static ImageFormat getImageFormat(byte[] header) {
        if (header == null) return ImageFormat.INVAILD;
        if (header.length < 4) return ImageFormat.INVAILD;

        if (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8 && header[2] == (byte) 0xFF) {
            return ImageFormat.JPEG;
        }

        if (header[0] == 0x42 && header[1] == 0x4D && header[2] == 0x36) {
            return ImageFormat.BMP;
        }

        if (header[0] == 0x47 && header[1] == 0x49 && header[2] == 0x46) {
            return ImageFormat.GIF;
        }

        if (header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47) {
            return ImageFormat.PNG;
        }

        if (header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) {
            return ImageFormat.TIFF;
        }

        return ImageFormat.UNKNOWN;
    }
    

    public static BufferedImage readImage(String imageFilePath) {
        ImageFormat imageFormat = getImageFormat(imageFilePath);
        if (imageFormat == ImageFormat.INVAILD) return null;

        return readImage(imageFormat, imageFilePath);
    }

    public static BufferedImage readImage(ImageFormat imageFromat, String imageFilePath) {
        BufferedImage image = null;
        File file = new File(imageFilePath);
        FileInputStream inputStream = null;

        try {
            if (imageFromat == ImageFormat.JPEG) {
                inputStream = new FileInputStream(file);
                JPEGDecodeParam  decodeParam = new JPEGDecodeParam();
                ImageDecoder decoder = ImageCodec.createImageDecoder("jpeg", inputStream, decodeParam);
                image = getBufferedImage(decoder.decodeAsRenderedImage());
            }else if(imageFromat == ImageFormat.PNG){
                inputStream = new FileInputStream(file);
                PNGDecodeParam  decodeParam = new PNGDecodeParam();
                ImageDecoder decoder = ImageCodec.createImageDecoder("png", inputStream, decodeParam);
                image = getBufferedImage(decoder.decodeAsRenderedImage());
            }
            else {
                image = ImageIO.read(file);
            }
        } catch (Exception e) {

        } finally {
            FileUtils.closeStream(inputStream);
        }

        return image;
    }

    public static boolean zoomImage(int zoomType, String imageFilePath, String saveFilePath, int targetThumbWidth, int targetThumbHeight) throws IOException{
        return zoomImage( zoomType,  imageFilePath,  saveFilePath,  targetThumbWidth,  targetThumbHeight, 0.8f);
    }

    /**
     * 图片缩放处理
     *
     * @param zoomType
     * @param imageFilePath
     * @param saveFilePath
     * @param targetThumbWidth      期望的缩放图宽度
     * @param targetThumbHeight     期望的缩放图高度
     * @return
     * @throws Exception
     */
    public static boolean zoomImage(int zoomType, String imageFilePath, String saveFilePath, int targetThumbWidth, int targetThumbHeight,Float quality)
            throws IOException {
        int targetWidth = targetThumbWidth;
        int targetHeight = targetThumbHeight;
        BufferedImage image = null;

        image = readImage(imageFilePath);
        if (image == null) return false;


        String exName = saveFilePath.substring(saveFilePath.lastIndexOf(".") + 1).toLowerCase();

        int thumbLeft = 0;      // 相对原图，要进行缩放处理的左边距
        int thumbTop = 0;       // 相对原图，要进行缩放处理的顶边距
        int thumbWidth = 0;     // 相对原图，要进行缩放处理的宽度
        int thumbHeight = 0;    // 相对原图，要进行缩放处理的高度

        int drawLeft = 0;       // 贴图位置，将缩放后的图片贴在输出图片上的左边距
        int drawTop = 0;        // 贴图位置，将缩放后的图片贴在输出图片上的顶边距
        int scaledWidth = 0;    // 缩放后的宽度
        int scaledHeight = 0;   // 缩放后的高度

        int width = image.getWidth();       // 原图宽度
        int height = image.getHeight();     // 原图高度

        if (targetThumbWidth * targetThumbHeight == 0) {
            targetWidth = targetThumbWidth = width;
            targetHeight = targetThumbHeight = height;
        }

        double rateWidth = ((double) targetWidth) / ((double) width);
        double rateHeight = ((double) targetHeight) / ((double) height);
        double rate = 0;

        if (zoomType == SNAPSHOT_TYPE_STRETCH) {
            thumbWidth = width;
            thumbHeight = height;
            thumbLeft = 0;
            thumbTop = 0;

            drawLeft = 0;
            drawTop = 0;
            scaledWidth = targetWidth;
            scaledHeight = targetHeight;
        } else if (zoomType == SNAPSHOT_TYPE_FILL) {
            if (rateHeight < rateWidth) {
                // 对高度进行裁剪
                thumbWidth = width;
                thumbHeight = targetHeight * width / targetWidth;

                thumbTop = (height - thumbHeight) / 2;
            } else {
                // 对宽度进行裁剪
                thumbWidth = targetWidth * height / targetHeight;
                thumbHeight = height;

                thumbLeft = (width - thumbWidth) / 2;
            }

            if (thumbWidth == 0 || thumbHeight == 0) {
                return false;
            }

            scaledWidth = targetWidth;
            scaledHeight = targetHeight;
        } else if(zoomType==SNAPSHOT_TYPE_FIT){
            if (width <= targetWidth && height <= targetHeight) {
                thumbWidth = width;
                thumbHeight = height;

                scaledWidth = width;
                scaledHeight = height;

                drawLeft = (targetThumbWidth - scaledWidth) / 2;
                drawTop = (targetThumbHeight - scaledHeight) / 2;

                targetWidth = targetThumbWidth;
                targetHeight = targetThumbHeight;

            } else {
                if (rateHeight < rateWidth)
                    rate = rateHeight;
                else
                    rate = rateWidth;

                thumbWidth = width;
                thumbHeight = height;

                scaledWidth = (int) Math.ceil(width * rate);
                scaledHeight = (int) Math.ceil(height * rate);

                drawLeft = (targetThumbWidth - scaledWidth) / 2;
                drawTop = (targetThumbHeight - scaledHeight) / 2;

                targetWidth = targetThumbWidth;
                targetHeight = targetThumbHeight;
            }
        } else if(zoomType==SNAPSHOT_TYPE_SCALE_FIT){

            if (rateHeight < rateWidth)
                rate = rateHeight;
            else
                rate = rateWidth;

            thumbWidth = width;
            thumbHeight = height;

            scaledWidth = (int) Math.ceil(width * rate);
            scaledHeight = (int) Math.ceil(height * rate);

            drawLeft = (targetThumbWidth - scaledWidth) / 2;
            drawTop = (targetThumbHeight - scaledHeight) / 2;

            targetWidth = targetThumbWidth;
            targetHeight = targetThumbHeight;

        } else if(zoomType==SNAPSHOT_TYPE_WIDTH_FIXED){

            if (targetThumbWidth < width){//缩放图片
                rate = rateWidth;//按宽度缩放比例缩放

                scaledWidth = (int) Math.ceil(width * rate);//缩放后的宽度
                scaledHeight = (int) Math.ceil(height * rate);

                thumbWidth = targetThumbWidth;//相对原图，要进行缩放处理的宽度
                thumbHeight = height;

                targetWidth = targetThumbWidth;
                targetHeight = scaledHeight;
            }else{
                thumbWidth = width;
                thumbHeight = height;

                scaledWidth=width;
                scaledHeight=height;

                targetWidth = width;
                targetHeight = height;
            }

        } else  {
            if (width <= targetWidth && height <= targetHeight) {
                thumbWidth = width;
                thumbHeight = height;

                scaledWidth = width;
                scaledHeight = height;

                drawLeft = (targetThumbWidth - scaledWidth) / 2;
                drawTop = (targetThumbHeight - scaledHeight) / 2;

                targetWidth = targetThumbWidth;
                targetHeight = targetThumbHeight;

            } else {
                if (rateHeight < rateWidth)
                    rate = rateHeight;
                else
                    rate = rateWidth;

                thumbWidth = width;
                thumbHeight = height;

                scaledWidth = (int) Math.ceil(width * rate);
                scaledHeight = (int) Math.ceil(height * rate);

                drawLeft = 0;
                drawTop = (targetThumbHeight - scaledHeight) / 2;

                targetWidth = targetThumbWidth;
                targetHeight = targetThumbHeight;
            }
        }



        BufferedImage tagImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        BufferedImage srcImage = image;
        if (thumbLeft > 0 || thumbTop > 0) {
            srcImage = srcImage.getSubimage(thumbLeft, thumbTop, thumbWidth, thumbHeight);
        }
        Graphics2D g = tagImage.createGraphics();
        boolean alpha = false;
        if (!"png".equals(exName)) {

            g.setColor(Color.WHITE);
            g.fillRect(0, 0, targetWidth, targetHeight);
        }
        else {
            tagImage = g.getDeviceConfiguration().createCompatibleImage(targetWidth, targetHeight, Transparency.TRANSLUCENT);
            g.dispose();
            g = tagImage.createGraphics();
            alpha = true;
        }
        g.drawImage(srcImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH), drawLeft, drawTop,alpha?null:Color.WHITE , null);
        g.dispose();


        FileOutputStream out = null;
        File file = null;
        try {
            file = new File(saveFilePath);
            out = new FileOutputStream(file);;
            ImageEncoder encoder = null;
            if (exName.equals("png")) {
                PNGEncodeParam pngEncodeParam = new PNGEncodeParam.Palette();
                encoder = ImageCodec.createImageEncoder("PNG", out, pngEncodeParam);
                ImageIO.write(tagImage, exName, file);
            } else {
                JPEGEncodeParam jpegEncodeParam = new JPEGEncodeParam();
                if(quality!=null){
                    jpegEncodeParam.setQuality(quality);
                } else {
                    jpegEncodeParam.setQuality(1);
                }
                encoder = ImageCodec.createImageEncoder("jpeg", out, jpegEncodeParam);
                encoder.encode(tagImage);
            }
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        } finally {
            FileUtils.closeStream(out);
        }

        return true;
    }


    /**
     * 图片切割
     * @param src  原图地址
     * @param x  目标切片坐标 X轴起点
     * @param y  目标切片坐标 Y轴起点
     * @param w  目标切片 宽度
     * @param h  目标切片 高度
     */
    public static void cutImage(String src, String dest, int x, int y, int w, int h) throws IOException,CoeusCoreException{
        String srcExtName = "";
        String destExtName = "";
        int pos = src.lastIndexOf(".");
        if (pos > -1) {
            srcExtName = src.substring(pos + 1);
        } else {
            throw new CoeusCoreException(UploadException.UPLOAD_FILE_EXT_INVALID, "文件扩展名无效!");
        }

        pos = dest.lastIndexOf(".");
        if (pos > -1) {
            destExtName = dest.substring(pos + 1);
        } else {
            throw new CoeusCoreException(UploadException.UPLOAD_FILE_EXT_INVALID, "文件扩展名无效!");
        }

        BufferedImage image = null;
        image = readImage(src);
        BufferedImage tagImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		if (x+w>=image.getWidth()){
			w = image.getWidth()-x;
		}
		if (y+h>=image.getHeight()){
			h = image.getHeight()-y;
		}
        BufferedImage srcImage = image.getSubimage(x, y, w, h);

        Graphics2D g = tagImage.createGraphics();
        if (!"png".equalsIgnoreCase(destExtName)) {
            // g.setColor(Color.WHITE);
            g.fillRect(0, 0, w, h);
        } else {
            tagImage = g.getDeviceConfiguration().createCompatibleImage(w, h, Transparency.TRANSLUCENT);
            g.dispose();
            g = tagImage.createGraphics();
        }
        g.drawImage(srcImage, 0, 0, Color.WHITE, null);
        g.dispose();

        FileOutputStream out = null;
        try {
            ImageIO.write(tagImage, destExtName, new File(dest));
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        }
    }

    public static BufferedImage getBufferedImage(RenderedImage img) {
        if (img instanceof BufferedImage) {
            return (BufferedImage)img;
        }
        ColorModel cm = img.getColorModel();
        int width = img.getWidth();
        int height = img.getHeight();
        WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        Hashtable properties = new Hashtable();
        String[] keys = img.getPropertyNames();
        if (keys!=null) {
            for (int i = 0; i < keys.length; i++) {
                properties.put(keys[i], img.getProperty(keys[i]));
            }
        }
        BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
        img.copyData(raster);
        return result;
    }

    /**
     * 得到图片文件的尺寸[w,h]
     * @param imgParh
     * @return
     * @throws IOException
     */
    public static Integer[] getImageWH(String imgParh)  throws IOException{
        BufferedImage  src = readImage(imgParh);
        int srcWidth = (int)src.getWidth(); // 得到源图宽
        int srcHeight = (int)src.getHeight();
        Integer[] wh = new Integer[]{srcWidth,srcHeight};
        return wh;

    }

    /**
     * 得到真实的像素
     * @param imgParh
     * @param imgHuaBu
     * @param orginX  原始的x
     * @param orginY
     * @param orginW
     * @param orginH
     * @return
     * @throws IOException
     */
    public static Integer[] getImageNewWHXY(String imgParh,Integer imgHuaBu,Integer orginX,Integer orginY,Integer orginW,Integer orginH)  throws IOException{
        Integer[] whxy = null;
        Integer[] wh = getImageWH(imgParh);
        int srcWidth = wh[0];
        int srcHeight = wh[1];

        //要放大的倍数
        double multiple = 1d;
        if(imgHuaBu<Math.max(srcWidth, srcHeight)){
            multiple = ((double)Math.max(srcWidth, srcHeight))/imgHuaBu;
        }
        //图片放大N倍，因为不放大截图的话效果很差
        //其实在页面上传过来的值只是在画布中的像素值，并不是图片的像素，
        //要切割清晰的图片我们需要这个值转换为图片的原始尺寸图下的x,y,w,h
        int xx=   (int) (orginX*multiple);
        int yy =  (int) (orginY*multiple);
        int ww =  (int) (orginW*multiple);
        int hh =  (int) (orginH*multiple);
        whxy = new Integer[]{xx,yy,ww,hh};
        return whxy;

    }

    /**
     * 验证码图像.
     *
     * @param out 流
     * @return String 验证码的值
     */
    public static String createImage(OutputStream out) {
        Font font = new Font("Courier New", Font.BOLD, 20);
        List<Color> color = new ArrayList<Color>();
        color.add(Color.BLACK);
        color.add(Color.BLUE);
        color.add(Color.CYAN);
        color.add(Color.GREEN);
        color.add(Color.MAGENTA);
        color.add(Color.ORANGE);
        color.add(Color.PINK);
        color.add(Color.RED);
        color.add(Color.WHITE);
        color.add(Color.YELLOW);
        int width = 70;
        int height = 30;
        int length = 4; // 设置默认生成4个数字
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = bi.createGraphics();
        // set background:
        //生成的二维码底色请使用此图的底色 color:#edf7ff
        Color bgColor = new Color(237, 247, 255);
        //g.setBackground(Color.LIGHT_GRAY);
        g.setBackground(bgColor);
        g.clearRect(0, 0, width, height);
        // set fore color:
        g.setColor(Color.red);
        g.setFont(font);
        // start draw:
        Random r = new Random(); // 设置随机种子
        StringBuffer str = new StringBuffer();
        for (int i = 0; i < length; i++) {
            str.append(r.nextInt(10)); // 生成随机数字
        }
        g.drawString(str.toString(), 10, 20);

        for (int i = 0; i < 40; i++) {
            int x = r.nextInt(width);
            int y = r.nextInt(height);
            int xl = r.nextInt(12);
            int yl = r.nextInt(12);
            g.setColor(color.get(r.nextInt(10)));
            g.drawLine(x, y, x + xl, y + yl);
        }

        // end draw:
        g.dispose();
        bi.flush();
        // encode:
        JPEGEncodeParam param = new JPEGEncodeParam();
        param.setQuality(0.8f);

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageEncoder encoder = ImageCodec.createImageEncoder("jpeg", os, param);
        try {
            encoder.encode(bi);
            out.write(os.toByteArray());
        } catch (IOException ioe) {
            LOGGER.error(ioe.getMessage(), ioe);
        }
        return str.toString();
    }
}
