package com.pcloud.common.utils;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itextpdf.text.Image;
import com.pcloud.common.constant.AliyunConstant;
import com.pcloud.common.constant.ImageConstant;
import com.pcloud.common.enums.ImageEnum;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.exceptions.FileException;
import com.pcloud.common.utils.aliyun.OssUtils;
import com.pcloud.common.utils.string.StringTools;
import com.pcloud.common.utils.string.StringUtil;

import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;

/**
 * @描述：图像工具类
 * @作者：songx
 * @创建时间：2016年7月13日,下午4:12:38 @版本：1.0
 */
public class ImageUtils {

	/**
	 * logger
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(ImageUtils.class);

	/**
	 * 校验是否是图片
	 * 
	 * @param fileUrl
	 *            图片地址，网路地址或者本地地址
	 * @throws FileException
	 */
	public static void checkIsImage(String fileUrl) throws FileException {
		String fileType = FileUtils.getFileType(fileUrl);
		if (!FileUtils.IMAGE.equals(FileUtils.getGatherName(fileType))) {
			throw new FileException(FileException.FILE_TYPE_ERROR, "不是有效的图片文件");
		}
	}

	/**
	 * 图片复制
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @throws IOException
	 */
	public static void imageCopy(File source, File target) throws IOException {
		setTarget(target);
		Thumbnails.of(source).scale(1F).outputQuality(1F).toFile(target);
	}

	/**
	 * 图片复制
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @throws IOException
	 */
	public static void imageCopy(InputStream source, OutputStream os) throws FileException {
		try {
			Thumbnails.of(source).scale(1F).outputQuality(1F).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】图片复制失败.[imageQualReduce]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "图片复制失败！");
		}
	}

	/**
	 * 图片质量压缩
	 * 
	 * @param source
	 *            元图片
	 * @param target
	 *            目标图片
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 */
	public static void imageQualReduce(InputStream source, float quality, OutputStream os) throws FileException {
		try {
			Thumbnails.of(source).scale(1F).outputQuality(quality).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】质量压缩失败.[imageQualReduce]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "图片质量压缩失败！");
		}
	}

	/**
	 * 图片-比例缩放
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @param scale
	 *            图片比例
	 * @throws IOException
	 */
	public static void imageScale(File source, File target, double scale) throws IOException {
		setTarget(target);
		Thumbnails.of(source).scale(scale).outputQuality(1F).toFile(target);
	}

	/**
	 * 图片-比例缩放
	 * 
	 * @param source
	 * @param scale
	 * @param os
	 * @throws IOException
	 */
	public static void imageScale(InputStream source, double scale, OutputStream os) throws FileException {
		try {
			Thumbnails.of(source).scale(scale).outputQuality(1F).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】比例缩放失败.[imageScale]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "图片比例缩放失败！");
		}
	}

	/**
	 * 图片-比例缩放带质量压缩
	 * 
	 * @param source
	 *            源图片
	 * @param scale
	 *            图片比例
	 * @param quality
	 *            压缩质量
	 * @return
	 * @throws IOException
	 */
	public static void imageScale(InputStream source, double scale, float quality, OutputStream os) throws IOException {
		Thumbnails.of(source).scale(scale).outputQuality(quality).toOutputStream(os);
	}

	/**
	 * 
	 * 图片-尺寸缩放
	 * 
	 * @param source
	 *            源图片
	 * @param width
	 *            图片宽度
	 * @param height
	 *            图片高度
	 * @param os
	 *            输出文件流
	 * @throws IOException
	 */
	public static void imageSizeScale(InputStream source, int width, int height, OutputStream os) throws FileException {
		try {
			Thumbnails.of(source).size(width, height).outputQuality(1F).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】尺寸缩放失败.[imageSizeScale]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "尺寸缩放失败！");
		}
	}

	/**
	 * 图片-尺寸缩放带质量压缩
	 * 
	 * @param source
	 *            源图片
	 * @param width
	 *            图片宽度
	 * @param height
	 *            图片高度
	 * @param quality
	 *            压缩质量
	 * @return
	 */
	public static void imageSizeScale(InputStream source, int width, int height, float quality, OutputStream os)
			throws FileException {
		try {
			Thumbnails.of(source).size(width, height).outputQuality(quality).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】尺寸缩放带质量压缩失败.[imageSizeScale]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "尺寸缩放带质量压缩失败！");
		}
	}

	/**
	 * 图片-以宽度尺寸缩放
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @param width
	 *            图片宽度
	 * @throws IOException
	 */
	public static void imageWidthScale(InputStream source, int width, OutputStream os) throws FileException {
		try {
			Thumbnails.of(source).width(width).outputQuality(1F).toOutputStream(os);
		} catch (Exception e) {
			LOGGER.error("【图片】以宽度尺寸缩放失败.[imageWidthScale]:" + e.getMessage(), e);
			throw new FileException(FileException.IMAGE_COMPRESS_FAIL, "以宽度尺寸缩放失败！");
		}
	}

	/**
	 * 图片-以宽度尺寸缩放带质量压缩
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @param width
	 *            图片宽度
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 */
	public static void imageWidthScale(File source, File target, int width, float quality) throws IOException {
		setTarget(target);
		Thumbnails.of(source).width(width).outputQuality(quality).toFile(target);
	}

	/**
	 * 图片-以高度尺寸缩放
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @param height
	 *            图片高度
	 * @throws IOException
	 */
	public static void imageHeightScale(File source, File target, int height) throws IOException {
		setTarget(target);
		Thumbnails.of(source).height(height).outputQuality(1F).toFile(target);
	}

	/**
	 * 图片-以高度尺寸缩放带质量压缩
	 * 
	 * @param source
	 *            源图片
	 * @param target
	 *            目标图片
	 * @param height
	 *            图片高度
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 */
	public static void imageHeightScale(File source, File target, int height, float quality) throws IOException {
		setTarget(target);
		Thumbnails.of(source).height(height).outputQuality(quality).toFile(target);
	}

	/**
	 * 自由裁剪 功能：自定义位置图片剪裁
	 * 
	 * @param source
	 *            原图像
	 * @param target
	 *            目标图像
	 * @param width
	 *            剪裁后的宽度
	 * @param height
	 *            剪裁后的高度
	 * @param posX
	 *            位置x
	 * @param posY
	 *            位置y
	 * @param scaleWidth
	 *            剪切前的缩放宽度
	 * @throws IOException
	 */
	public static void clip(File source, File target, int width, int height, int posX, int posY, int scaleWidth)
			throws IOException {
		setTarget(target);
		double scale = (scaleWidth > 0) ? getScale(scaleWidth, getSize(source)[0]) : 1.0;
		Thumbnails.of(source).scale(scale).sourceRegion(posX, posY, width, height).outputQuality(1F).toFile(target);
	}

	/**
	 * 自由裁剪带质量压缩 功能：自定义位置图片剪裁
	 * 
	 * @param source
	 *            原图像
	 * @param target
	 *            目标图像
	 * @param width
	 *            剪裁后的宽度
	 * @param height
	 *            剪裁后的高度
	 * @param posX
	 *            位置x
	 * @param posY
	 *            位置y
	 * @param scaleWidth
	 *            剪切前的缩放的图像宽度
	 * @param quality
	 *            质量压缩率
	 * @throws IOException
	 */
	public static void clip(File source, File target, int width, int height, int posX, int posY, int scaleWidth,
			float quality) throws IOException {
		setTarget(target);
		double scale = (scaleWidth > 0) ? getScale(scaleWidth, getSize(source)[0]) : 1.0;
		Thumbnails.of(source).scale(scale).sourceRegion(posX, posY, width, height).outputQuality(quality)
				.toFile(target);
	}

	/**
	 * 从中心剪裁图片
	 * 
	 * @param source
	 *            原图像
	 * @param target
	 *            目标图像
	 * @param width
	 *            剪裁后的宽度
	 * @param height
	 *            剪裁后的高度
	 * @param scaleWidth
	 *            剪切前的缩放的图像宽度
	 * @throws IOException
	 */
	public static void clip(File source, File target, int width, int height, int scaleWidth) throws IOException {
		setTarget(target);
		double scale = (scaleWidth > 0) ? getScale(scaleWidth, getSize(source)[0]) : 1.0;
		Thumbnails.of(source).scale(scale).sourceRegion(Positions.CENTER, width, height).outputQuality(1F)
				.toFile(target);
	}

	/**
	 * 从中心剪裁图片带质量压缩
	 * 
	 * @param source
	 *            原图像
	 * @param target
	 *            目标图像
	 * @param width
	 *            剪裁后的宽度
	 * @param height
	 *            剪裁后的高度
	 * @param scaleWidth
	 *            剪切前的缩放的图像宽度
	 * @param quality
	 *            质量压缩率
	 * @throws IOException
	 */
	public static void clip(File source, File target, int width, int height, int scaleWidth, float quality)
			throws IOException {
		setTarget(target);
		double scale = (scaleWidth > 0) ? getScale(scaleWidth, getSize(source)[0]) : 1.0; // scale为1.0表示原图剪裁，否则就缩放(扩大)
		Thumbnails.of(source).scale(scale).sourceRegion(Positions.CENTER, width, height).outputQuality(quality)
				.toFile(target);
	}

	/**
	 * 设置图片生成的路径
	 * 
	 * @param target
	 *            目标图片
	 * @return
	 */
	public static void setTarget(File target) {
		if (!target.getParentFile().exists()) {
			target.getParentFile().mkdirs();
		}
	}

	/**
	 * 获取图片的宽度和高度尺寸
	 * 
	 * @param img
	 *            图片文件
	 * @return
	 */
	private static int[] getSize(File img) {
		BufferedImage image = null;
		try {
			image = ImageIO.read(img);
		} catch (IOException e) {
			e.printStackTrace();
		}
		int width = image.getWidth();
		int height = image.getHeight();
		int[] size = { width, height };
		return size;
	}

	/**
	 * 获取图片的宽度,如果图片带有旋转参数，会自动纠正角度后在获取宽度
	 * 
	 * @param img
	 *            图片文件
	 * @return
	 */
	public static float getWidthSize(String fileUrl) {
		Image image = null;
		String localFile = null;
		try {
			if (StringTools.contains(fileUrl, AliyunConstant.OSS_CDN_URLS)) {
				localFile = OssUtils.imageAutoOrient(fileUrl, 0);
				image = Image.getInstance(localFile);
			} else if (fileUrl.startsWith("http")) {
				image = Image.getInstance(FileUtils.downloadByteFromUrl(fileUrl));
			} else {
				image = Image.getInstance(fileUrl);
			}
		} catch (Exception e) {
			LOGGER.error("【IMAGE API】获取图片的宽度:" + e.getMessage(), e);
		} finally {
			if (!StringUtil.isEmpty(localFile)) {
				FileUtils.deleteFile(localFile);
			}
		}
		return image == null ? 0F : image.getWidth();
	}

	/**
	 * 获取对比比例
	 * 
	 * @param num1
	 *            数字1
	 * @param num2
	 *            数字2
	 * @return
	 */
	private static double getScale(double num1, double num2) {
		BigDecimal b1 = new BigDecimal(Double.toString(num1));
		BigDecimal b2 = new BigDecimal(Double.toString(num2));
		double result = b1.divide(b2, 3, BigDecimal.ROUND_HALF_UP).doubleValue();
		return result;
	}

	/**
	 * 图片质量转换成文件流
	 */
	public static ByteArrayOutputStream quality(String filePath, Float ratio) throws BizException {
		// 拆解文件路径
		// Map<String, String> pathMap = FdfsUtils.splitFilePath(filePath);
		// 检测是否是有效文件类型
		// if
		// (!FileUtils.IMAGE.equals(FileUtils.getGatherName(pathMap.get(FdfsEnum.FILE_TYPE.value))))
		// {
		// throw new FileException(FileException.FILE_TYPE_ERROR, "非有效的文件类型");
		// }
		// 从文件服务器下载文件
		// byte[] bt =
		// FdfsUtils.download(pathMap.get(FdfsEnum.GROUP_NAME.value),
		// pathMap.get(FdfsEnum.FILE_ID.value));
		// InputStream is = new ByteArrayInputStream(bt);
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		// imageQualReduce(is, ratio, os);
		// closeInputStream(is);
		return os;
	}

	/**
	 * 如果上传的文件为图片就转换图片质量
	 */
	public static String image4Quality(byte[] file_buff, String[] fileIdInfo, String fileType) throws FileException {
		LOGGER.info("【图片】转换质量.<START>");
		String fileUrl = "";
		InputStream is = null;
		ByteArrayOutputStream os = null;
		String[] qualitys = ImageConstant.QUALITY.split(";");
		for (int i = 0; i < qualitys.length; i++) {
			// 大于100K的图片才需要转换
			if (file_buff.length > 100 * 1024) {
				is = new ByteArrayInputStream(file_buff);
				os = new ByteArrayOutputStream();
				String quality = getQuality(i, qualitys, file_buff.length);
				imageScale(is, Double.parseDouble(quality), os);
				// FdfsUtils.uploadSlave(os.toByteArray(), fileIdInfo[0],
				// fileIdInfo[1], "_" + i, fileType, null);
			} else {
				// FdfsUtils.uploadSlave(file_buff, fileIdInfo[0],
				// fileIdInfo[1], "_" + i, fileType, null);
			}
		}
		// 220宣传图
		convert(file_buff, fileIdInfo[0], fileIdInfo[1], "_4", fileType, ImageEnum.TITLE_220);
		// 375宣传图
		convert(file_buff, fileIdInfo[0], fileIdInfo[1], "_5", fileType, ImageEnum.TITLE_375);
		// 原图
		convert(file_buff, fileIdInfo[0], fileIdInfo[1], "_-1", fileType, ImageEnum.ORIGINAL);
		closeInputStream(is);
		closeoutputStream(os);
		LOGGER.info("【图片】转换质量.<END>");
		return fileUrl;
	}

	/**
	 * 如果上传的文件为图片就转换图片尺寸
	 */
	public static void image4Quality(String filePath) throws FileException {
		LOGGER.info("【图片】转换质量.<START>");
		InputStream is = null;
		ByteArrayOutputStream os = null;
		// 拆解文件路径
		// Map<String, String> pathMap = FdfsUtils.splitFilePath(filePath);
		// String groupName = pathMap.get(FdfsEnum.GROUP_NAME.value);
		// String fileId = pathMap.get(FdfsEnum.FILE_ID.value);
		// String fileType = pathMap.get(FdfsEnum.FILE_TYPE.value);
		// 下载文件
		// byte[] file_buff =
		// FdfsUtils.download(pathMap.get(FdfsEnum.GROUP_NAME.value),
		// pathMap.get(FdfsEnum.FILE_ID.value));
		// 比例图
		// String[] qualitys = ImageConstant.QUALITY.split(";");
		// for (int i = 0; i < qualitys.length; i++) {
		// // 大于100K的图片才需要转换
		// if (file_buff.length > 100 * 1024) {
		// is = new ByteArrayInputStream(file_buff);
		// os = new ByteArrayOutputStream();
		// String quality = getQuality(i, qualitys, file_buff.length);
		// imageScale(is, Double.parseDouble(quality), os);
		// FdfsUtils.uploadSlave(os.toByteArray(), groupName, fileId, "_" + i,
		// fileType, null);
		// } else {
		// FdfsUtils.uploadSlave(file_buff, groupName, fileId, "_" + i,
		// fileType, null);
		// }
		// }
		// 220宣传图
		// convert(file_buff, groupName, fileId, "_4", fileType,
		// ImageEnum.TITLE_220);
		// 375宣传图
		// convert(file_buff, groupName, fileId, "_5", fileType,
		// ImageEnum.TITLE_375);
		// 原图
		// convert(file_buff, groupName, fileId, "_-1", fileType,
		// ImageEnum.ORIGINAL);
		closeInputStream(is);
		closeoutputStream(os);
		LOGGER.info("【图片】转换质量.<END>");
	}

	/**
	 * 固定图转换
	 * 
	 * @param bt
	 * @param groupName
	 * @param fileId
	 * @param fileType
	 */
	public static void convert(byte[] file_buff, String groupName, String fileId, String suffix, String fileType,
			ImageEnum imageEnum) {
		LOGGER.info("【图片】固定图转换.<START>.[imageEnum]=" + imageEnum);
		InputStream is = new ByteArrayInputStream(file_buff);
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		if (imageEnum.equals(ImageEnum.TITLE_220)) {
			imageWidthScale(is, ImageConstant.TITLE_WITH_220, os);
		} else if (imageEnum.equals(ImageEnum.TITLE_375)) {
			imageWidthScale(is, ImageConstant.TITLE_WITH_375, os);
		} else if (imageEnum.equals(ImageEnum.BANNER)) {
			imageSizeScale(is, ImageConstant.BANNER_WITH, ImageConstant.BANNER_HEIGHT, os);
		} else if (imageEnum.equals(ImageEnum.SQUARE)) {
			imageSizeScale(is, ImageConstant.SQUARE_WITH, ImageConstant.SQUARE_HEIGHT, os);
		} else if (imageEnum.equals(ImageEnum.VERTICAL)) {
			imageSizeScale(is, ImageConstant.VERTICAL_WITH, ImageConstant.VERTICAL_HEIGHT, os);
		} else if (imageEnum.equals(ImageEnum.SMALL)) {
			imageSizeScale(is, ImageConstant.SMALL_WITH, ImageConstant.SMALL_HEIGHT, os);
		} else if (imageEnum.equals(ImageEnum.ORIGINAL)) {
			imageCopy(is, os);
		}
		// FdfsUtils.uploadSlave(os.toByteArray(), groupName, fileId, suffix,
		// fileType, null);
		closeInputStream(is);
		closeoutputStream(os);
		LOGGER.info("【图片】固定图转换.<END>");
	}

	/**
	 * 根据文件大小，判定缩放比列
	 * 
	 * @param i
	 * @param qualitys
	 * @param fileSize
	 * @return
	 */
	public static String getQuality(int i, String[] qualitys, int fileSize) {
		String quality = qualitys[i];
		if (fileSize > 1024 * 1024) {
			quality = qualitys[i];
		} else if (fileSize > 600 * 1024) {
			if (i > 2) {
				quality = qualitys[2];
			}
		} else if (fileSize > 400 * 1024) {
			if (i > 1) {
				quality = qualitys[1];
			}
		} else if (fileSize > 200 * 1024) {
			if (i > 0) {
				quality = qualitys[0];
			}
		}
		return quality;
	}

	/**
	 * 关闭文件流
	 * 
	 * @param is
	 */
	private static void closeInputStream(InputStream is) {
		try {
			if (is != null) {
				is.close();
				is = null;
			}
		} catch (Exception e) {
			LOGGER.error("【图片】输入文件流关闭失败:" + e.getMessage(), e);
		}
	}

	/**
	 * 关闭文件流
	 * 
	 * @param is
	 */
	private static void closeoutputStream(ByteArrayOutputStream os) {
		try {
			if (os != null) {
				os.close();
				os = null;
			}
		} catch (Exception e) {
			LOGGER.error("【图片】输出文件流关闭失败:" + e.getMessage(), e);
		}
	}
	
	/**
	 * 获取图片的宽度和高度
	 * 
	 * @param img
	 *            图片文件
	 * @return
	 */
	public static float[] getWidthHeightSize(String fileUrl) {
		Image image = null;
		String localFile = null;
		float[] size = new float[2];
		try {
			if (StringTools.contains(fileUrl, AliyunConstant.OSS_CDN_URLS)) {
				localFile = OssUtils.imageAutoOrient(fileUrl, 0);
				image = Image.getInstance(localFile);
			} else if (fileUrl.startsWith("http")) {
				image = Image.getInstance(FileUtils.downloadByteFromUrl(fileUrl));
			} else {
				image = Image.getInstance(fileUrl);
			}
		} catch (Exception e) {
			LOGGER.error("【IMAGE API】获取图片的宽度和高度:" + e.getMessage(), e);
		} finally {
			if (!StringUtil.isEmpty(localFile)) {
				FileUtils.deleteFile(localFile);
			}
		}
		if (image == null) {
			size[0] = 0;
			size[1] = 0;
		} else {
			size[0] = image.getWidth();
			size[1] = image.getHeight();
		}
		return size;
	}

}
