/**
 * Copyright (c) 2016-2020, Michael Yang 杨福海 ([email protected]).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.jpress.commons.utils;

import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import org.apache.commons.io.FileUtils;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

public class ImageUtils {
    private static final Log log = Log.getLog(ImageUtils.class);

    private final static String[] imgExts = new String[]{"jpg", "jpeg", "png", "bmp"};

    public static String getExtName(String fileName) {
        int index = fileName.lastIndexOf('.');
        if (index != -1 && (index + 1) < fileName.length()) {
            return fileName.substring(index + 1);
        } else {
            return null;
        }
    }


    /**
     * 过文件扩展名,判断是否为支持的图像文件
     *
     * @param fileName
     * @return 是图片则返回 true,否则返回 false
     */
    public static boolean isImageExtName(String fileName) {
        if (StrKit.isBlank(fileName)) {
            return false;
        }
        fileName = fileName.trim().toLowerCase();
        String ext = getExtName(fileName);
        if (ext != null) {
            for (String s : imgExts) {
                if (s.equals(ext)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static final boolean notImageExtName(String fileName) {
        return !isImageExtName(fileName);
    }

    public static int[] ratio(String src) throws IOException {
        BufferedImage bufferedImage = ImageIO.read(new File(src));
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        return new int[]{width, height};
    }

    public static String ratioAsString(String src) throws IOException {
        File file = new File(src);
        if (!file.exists()) {
            return null;
        }
        BufferedImage bufferedImage = ImageIO.read(file);
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        return String.format("%s x %s", width, height);
    }

    /**
     * 等比缩放,居中剪切,自动在在当前目录下生产新图
     * 例如:aaa.jpg 宽高为100和200,自动在当前目录下生成新图 aaa_100x200.jpg 的图
     *
     * @param src
     * @param w
     * @param h
     * @return 放回新图的路径
     * @throws IOException
     */
    public static String scale(String src, int w, int h) throws IOException {
        int inserTo = src.lastIndexOf(".");
        String dest = src.substring(0, inserTo) + String.format("_%sx%s", w, h) + src.substring(inserTo);
        scale(src, dest, w, h);
        return dest;
    }

    /**
     * 等比缩放,居中剪切
     *
     * @param src
     * @param dest
     * @param w
     * @param h
     * @throws IOException
     */
    public static void scale(String src, String dest, int w, int h) throws IOException {

        if (notImageExtName(src)) {
            throw new IllegalArgumentException("只支持如下几种图片格式:jpg、jpeg、png、bmp");
        }

        Iterator<ImageReader> iterator = ImageIO.getImageReadersByFormatName(getExtName(src));
        ImageReader reader = (ImageReader) iterator.next();

        InputStream in = new FileInputStream(src);
        ImageInputStream iis = ImageIO.createImageInputStream(in);
        reader.setInput(iis);

        BufferedImage srcBuffered = readBuffereImage(reader, w, h);
        BufferedImage targetBuffered = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

        Graphics graphics = targetBuffered.getGraphics();
        graphics.drawImage(srcBuffered.getScaledInstance(w, h, Image.SCALE_DEFAULT), 0, 0, null); // 绘制缩小后的图

        graphics.dispose();
        srcBuffered.flush();
        save(targetBuffered, dest);
        targetBuffered.flush();
    }

    private static BufferedImage readBuffereImage(ImageReader reader, int w, int h) throws IOException {
        ImageReadParam param = reader.getDefaultReadParam();
        int srcWidth = reader.getWidth(0);
        int srcHeight = reader.getHeight(0);

        Rectangle rect = null;

        if ((float) w / h > (float) srcWidth / srcHeight) {
            h = h * srcWidth / w;
            w = srcWidth;
            rect = new Rectangle(0, (srcHeight - h) / 2, w, h);
        } else {
            w = w * srcHeight / h;
            h = srcHeight;
            rect = new Rectangle((srcWidth - w) / 2, 0, w, h);
        }
        param.setSourceRegion(rect);
        BufferedImage srcBuffered = reader.read(0, param);
        return srcBuffered;
    }

    public final static void pressImage(String watermarkImg, String srcImageFile) {
        pressImage(watermarkImg, srcImageFile, srcImageFile, 5, -1, -1, 0.2f, 1);
    }

    public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile) {
        pressImage(watermarkImg, srcImageFile, destImageFile, 5, -1, -1, 0.2f, 1);
    }

    public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile, int position,
                                        float alpha) {
        pressImage(watermarkImg, srcImageFile, destImageFile, position, -1, -1, 0.2f, alpha);
    }

    /**
     * @param watermarkImg  水印图片位置
     * @param srcImageFile  源图片位置
     * @param destImageFile 生成的图片位置
     * @param position      水印打印的位置: 1->左上角,2->右上角,3->居中,4->左下角,5->右下角
     * @param xOffset       x轴偏移量,xOffset小于0,自动偏移
     * @param yOffset       y轴偏移量,yOffset小于0,自动偏移
     * @param radio         默认为原图的 1/4
     * @param alpha         透明度(0~1),PNG图片建议设置为1
     */
    public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile, int position,
                                        int xOffset, int yOffset, float radio, float alpha) {

        if (notImageExtName(srcImageFile)) {
            throw new IllegalArgumentException("只支持如下几种图片格式:jpg、jpeg、png、bmp");
        }

        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int srcWidth = src.getWidth(null);
            int srcHeight = src.getHeight(null);

            BufferedImage image = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphics = image.createGraphics();
            graphics.drawImage(src, 0, 0, srcWidth, srcHeight, null);

            // 水印文件
            Image wmImage = ImageIO.read(new File(watermarkImg));
            int wmWidth = wmImage.getWidth(null);
            int wmHeight = wmImage.getHeight(null);

            radio = radio <= 0 ? 0.2f : radio;
            int newWidth = (int) (srcWidth * radio);
            int newHeight = (int) (wmHeight * (newWidth / (float) wmWidth));

            xOffset = (xOffset < 0) ? (int) (newWidth * 0.1f) : xOffset;
            yOffset = (yOffset < 0) ? (int) (newHeight * 0.1f) : yOffset;

            int xPostion = 0;
            int yPostion = 0;

            switch (position) {
                case 1:
                    xPostion = xOffset;
                    yPostion = yOffset;
                    break;
                case 2:
                    xPostion = (int) (srcWidth * (1 - radio) - xOffset);
                    yPostion = yOffset;
                    break;
                case 3:
                    xPostion = (int) (srcWidth - newWidth) / 2;
                    yPostion = (int) (srcHeight - newHeight) / 2;
                    break;
                case 4:
                    xPostion = xOffset;
                    yPostion = (int) (srcHeight - newHeight - yOffset);
                    break;
                case 5:
                    xPostion = (int) (srcWidth * (1 - radio) - xOffset);
                    yPostion = (int) (srcHeight - newHeight - yOffset);
                    break;
                default:
                    xPostion = xOffset;
                    yPostion = yOffset;
                    break;
            }

            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            graphics.drawImage(wmImage, xPostion, yPostion, newWidth, newHeight, null);
            // 水印文件结束
            graphics.dispose();

            save(image, destImageFile);

        } catch (Exception e) {
            log.warn("ImageUtils pressImage error", e);
        }
    }

    public static void zoom(int maxWidth, String srcImageFile, String destImageFile) {
        try {
            BufferedImage srcImage = ImageIO.read(new File(srcImageFile));
            int srcWidth = srcImage.getWidth();
            int srcHeight = srcImage.getHeight();

            // 当宽度在 maxWidth 范围之内,直接copy
            if (srcWidth <= maxWidth) {
                FileUtils.copyFile(new File(srcImageFile), new File(destImageFile));
            }
            // 当宽度超出 maxWidth 范围,将宽度变为 maxWidth,高度按比例缩放
            else {
                float scalingRatio = (float) maxWidth / (float) srcWidth;            // 计算缩放比率
                float maxHeight = ((float) srcHeight * scalingRatio);    // 计算缩放后的高度
                BufferedImage ret = resize(srcImage, maxWidth, (int) maxHeight);
                save(ret, destImageFile);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 剪切
     *
     * @param srcImageFile  原图
     * @param destImageFile 存放的目标位置
     * @param left          起始点:左
     * @param top           起始点:上
     * @param width         宽
     * @param height        高
     */
    public static void crop(String srcImageFile, String destImageFile, int left, int top, int width, int height) {

        if (notImageExtName(srcImageFile)) {
            throw new IllegalArgumentException("只支持如下几种图片格式:jpg、jpeg、png、bmp");
        }

        try {
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            width = Math.min(width, bi.getWidth());
            height = Math.min(height, bi.getHeight());
            if (width <= 0) {
                width = bi.getWidth();
            }
            if (height <= 0) {
                height = bi.getHeight();
            }

            left = Math.min(Math.max(0, left), bi.getWidth() - width);
            top = Math.min(Math.max(0, top), bi.getHeight() - height);

            BufferedImage subimage = bi.getSubimage(left, top, width, height);
            BufferedImage resizeImage = resize(subimage, 200, 200);

            save(resizeImage, destImageFile);

        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }


    /**
     * 高保真缩放
     */
    private static BufferedImage resize(BufferedImage bi, int toWidth, int toHeight) {
        Graphics g = null;
        try {
            Image scaledImage = bi.getScaledInstance(toWidth, toHeight, Image.SCALE_SMOOTH);
            BufferedImage ret = new BufferedImage(toWidth, toHeight, BufferedImage.TYPE_INT_RGB);
            g = ret.getGraphics();
            g.drawImage(scaledImage, 0, 0, null);
            return ret;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (g != null) {
                g.dispose();
            }
        }
    }


    private static void save(BufferedImage bi, String outputImageFile) {
        try {
            ImageIO.write(bi, getExtName(outputImageFile), new File(outputImageFile));
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }


}