package com.apobates.forum.thumbnail.handler;

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import com.apobates.forum.utils.image.OverlayBox;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.Thumbnails.Builder;
/**
 * 从中心点开始裁剪.支持缩放
 * 
 * @author xiaofanku
 * @since 20200120
 */
public class ZoomFixedCropHandler extends AbstractCropImageHandler{
	//图片的缩放比例(1-100)
	private final int scale;
	/**
	 * 初始化
	 * @param orginalImageFile 原始图片文件
	 * @param scale            缩放比例
	 */
	public ZoomFixedCropHandler(File orginalImageFile, int scale) {
		super(orginalImageFile);
		this.scale = scale;
	}
	/**
	 * 初始化
	 * @param orginalImageFile 原始图片文件
	 * @param waterBox         遮盖盒
	 * @param scale            缩放比例
	 */
	public ZoomFixedCropHandler(File orginalImageFile, OverlayBox waterBox, int scale) {
		super(orginalImageFile, waterBox);
		this.scale = scale;
	}
	
	@Override
	public void cropImage(File cropSaveFile, int width, int height) throws IOException {
		BufferedImage originalImage = ImageIO.read(getOrginalImageFile());
		int aw = originalImage.getWidth(); //A的宽
		int ah = originalImage.getHeight(); //A的高
		String fileExt = getFileExtension(getFileName(cropSaveFile));
		//水印是否有设置
		BufferedImage watermarkOverlay = null;
		try{
			watermarkOverlay = getWatermark();
		}catch(IOException | NullPointerException e){}
		if(width == 0 && height == 0){ //自动
			Builder<File> b = Thumbnails.of(getOrginalImageFile()).scale(Math.abs(scale) / 100.00D);
			if(null != watermarkOverlay){ //若有设置增加水印设置
				b = b.watermark(getThumbnailPosition(), watermarkOverlay, getWaterBoxOpacity());
			}
			b.outputFormat(fileExt).toFile(cropSaveFile);
			return;
		}
		if(aw == width && ah == height){ //无需裁剪
			saveFile(originalImage, watermarkOverlay, fileExt, cropSaveFile);
			return;
		}
		//计算缩放的比例
		double scaleRate = calcScaleRate(aw, ah, width, height);
		//缩放
		BufferedImage data = scaleQuality(originalImage, aw, ah, scaleRate);
		//中心法裁剪
		int[] xyPointers = getCropXYPointer(data.getWidth(), data.getHeight(), width, height);
		if(xyPointers.length != 2){
			throw new IOException("起始坐标计算失败");
		}
		//最后一公里
		try(ByteArrayOutputStream os = new ByteArrayOutputStream();ImageOutputStream ios = ImageIO.createImageOutputStream(os)){ //20200329@AutoCloseable
			BufferedImage croppedImage = data.getSubimage(xyPointers[0], xyPointers[1], width, height);//
			//
			ImageWriter writer = ImageIO.getImageWritersByFormatName(fileExt).next();
			//
			ImageWriteParam param = writer.getDefaultWriteParam();
			if (fileExt.equalsIgnoreCase("bmp")) {
				try{
					param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
					param.setCompressionType("BI_RGB");
				}catch(java.lang.UnsupportedOperationException e){}
			} else if (fileExt.equalsIgnoreCase("gif")) {
				try{
					param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
				}catch(java.lang.UnsupportedOperationException e){}
			} else {
				try{
					param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
					param.setCompressionQuality(1.0f);
				}catch(java.lang.UnsupportedOperationException e){}
			}
			//
			writer.setOutput(ios);
			writer.write(null, new IIOImage(croppedImage, null, null), param);
			writer.dispose();
			//
			ByteArrayInputStream in = new ByteArrayInputStream(os.toByteArray()); //将b作为输入流；
			BufferedImage image = ImageIO.read(in); 
			saveFile(image, watermarkOverlay, fileExt, cropSaveFile);
		}
	}
	
	//没有设置水印直接保存
	//若有设置,改用OverlayBox.toFile(BufferedImage, File)保存
	private void saveFile(BufferedImage im, BufferedImage watermarkOverlay, String formatName, File output) throws IOException{
		//若有设置增加水印设置 && 水印与图片的面积是否允许水印
		if(null != watermarkOverlay){
			getWaterBox().watermark(im, watermarkOverlay, output);
			return;
		}
		ImageIO.write(im, formatName, output);
	}
	
	//计算缩放的比例
	//以宽度为参考,让高度满足最小大于
	private double calcScaleRate(int originalImageWidth, int originalImageHeight, int cropWidth, int cropHeight){
		double scaleRate = cropWidth / (originalImageWidth * 1.00D);
		//当前是缩小?
		boolean isZoomOut = originalImageWidth > cropWidth;
		
		int currentHeight = Double.valueOf(originalImageHeight * scaleRate).intValue();
		while(currentHeight < cropHeight){
			//提升多少呢?
			if(isZoomOut){
				scaleRate += 0.1D;
			}else{
				scaleRate +=1;
			}
			currentHeight = Double.valueOf(originalImageHeight * scaleRate).intValue();
		}
		return scaleRate;
	}
	
	//缩放图像
	private BufferedImage scaleQuality(BufferedImage originalImage, int originalImageWidth, int originalImageHeight, double scaleRate){
		int w,h;
		w = Double.valueOf(originalImageWidth * scaleRate).intValue();
		h = Double.valueOf(originalImageHeight * scaleRate).intValue();
		//构建图片对象
		BufferedImage _image = new BufferedImage(w, h, originalImage.getType());
		//画质优先
		AffineTransform transform = AffineTransform.getScaleInstance(scaleRate, scaleRate);
		AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);
		op.filter(originalImage, _image);
		return _image;
	}
	
	/**
	 * 获取裁剪的起始点坐标
	 * 
	 * @param originalImageWidth  图片的宽度
	 * @param originalImageHeight 图片的高度
	 * @param width               裁剪的宽度
	 * @param height              裁剪的高度
	 * @return
	 */
	private int[] getCropXYPointer(int imageWidth, int imageHeight, int width, int height){
		if(imageWidth <= width && imageHeight <= height){
			return new int[]{};
		}
		int startX=0;
		if(imageWidth > width){
			int centerX = imageWidth / 2; //宽度中心点X
			startX = centerX - width / 2;
		}
		//条漫?
		if(isVerticalComic(imageWidth, imageHeight)){
			return new int[]{startX, 0};
		}
		int startY=0;
		if(imageHeight > height){
			int centerY = imageHeight / 2; //高度中心点Y
			startY = centerY - height / 2;
		}
		return new int[]{startX, startY};
	}
	
	private boolean isVerticalComic(int imageWidth, int imageHeight){
		return imageHeight / imageWidth >= 2;
	}
}
