import commonUtil from './CommonUtil.js'

/**
 * 通用工具类</br>
 * 引用方式：<b>this.imgUtil.&lt;method_name&gt;</b></br>
 * 其中&lt;method_name&gt;包括：
 * 
 * @property {} <b>图片处理</b>
 * @property {method} file2Image(file) 根据图片file对象创建Image对象<br/>
 *    参数： file = object 图片File对象<br/>
 *    返回： resolve Image对象信息，格式为：<br/>
 *           &emsp;&emsp;&emsp;&ensp;{ <br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;img: object &emsp;&emsp;&emsp;&ensp; //Image对象<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;width: number &emsp;&ensp; //图片宽<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;height: number &emsp; //图片高<br/>
 *           &emsp;&emsp;&emsp;&ensp;}<br/>
 *           &emsp;&emsp;&emsp;&ensp;reject 错误信息<br/>
 * @property {method} imgBase64ToFile(data,filename) 将图片的Base64数据转换为File对象<br/>
 *    参数： data = string base64数据，带Base64类型头部信息<br/>
 *           &emsp;&emsp;&emsp;&ensp;filename = string 指定的文件名<br/> 
 *    返回： object = 转换后的File对象<br/>
 * 
 * @property {method} blobToBase64(imgBlob) 将图片Blob对象(即file.raw)转换为Base64数据<br/>
 *     参数：imgBlob = object 图片的Blob对象，即file.raw<br/>
 *     返回：string = base64数据，带Base64类型头部信息<br/>
 * 
 * @property {method} compressImg(image,imgType,width,height) 压缩图片数据到指定的宽高<br/>
 *    参数： image = object Image对象<br/>
 *           &emsp;&emsp;&emsp;&ensp;imgType = string 图片的MIME类型<br/> 
 *           &emsp;&emsp;&emsp;&ensp;width = number 目标宽度<br/> 
 *           &emsp;&emsp;&emsp;&ensp;height = number 目标高度<br/> 
 *    返回： string = 压缩后的图片数据<br/>
 * 
 * @property {} <b>图片校验</b>
 * @property {method} validateAndCompressImg(file,limitation) (上传的)图片文件全校验，并压缩至指定宽高。校验规则如下：<br/>
 *    1、图片文件属性校验，包括：<br/>
 *    &emsp;&emsp;(a) 文件名只能包含字母、数字、汉字和“.”，不能有其他特殊字符<br/>
 *    &emsp;&emsp;(b) MIME类型只能是image/jpeg、image/jpg或image/png<br/>
 *    &emsp;&emsp;(c) 文件大小是否超限<br/>
 *    2、图片属性校验，包括：<br/>
 *    &emsp;&emsp;(a) 最小宽(如100) 验证提示：图片宽度不应低于100像素<br/>
 *    &emsp;&emsp;(b) 以下二选一：<br/>
 *    &emsp;&emsp;&emsp;&emsp;(b1) 宽高比(如100:60-100:70) 验证提示：图片宽高比例应在100:60~100:70之间<br/>
 *    &emsp;&emsp;&emsp;&emsp;(b2) 最小高度(100:60) 验证提示：图片宽高比例不应低于100:60<br/>
 *    参数： file = object 图片的File对象<br/>
 *           &emsp;&emsp;&emsp;&ensp;limitation = object 校验用的限制条件，格式为：<br/>
 *           &emsp;&emsp;&emsp;&ensp;{<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;maxSize: number &emsp;&emsp;&emsp;&emsp;&ensp;//文件最大尺寸，单位为KB<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;maxWidth: number &emsp;&emsp;&emsp;&ensp;//图片最大宽度<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;maxHeight: number &emsp;&emsp;&emsp;//图片最大高度<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;extLimit: { &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;//扩展限制条件，用于2-(b)<br/>
 *           &emsp;&emsp;&emsp;&emsp;&emsp;&ensp;limitType: number &emsp;&emsp;&emsp;//适用条件类型。1=适用于2-(b1), 2=适用于2-(b1)<br/>
 *           &emsp;&emsp;&emsp;&emsp;&emsp;&ensp;minRatio: number &emsp;&emsp;&emsp;//最小高宽比<br/>
 *           &emsp;&emsp;&emsp;&emsp;&emsp;&ensp;minRatioText: string &emsp;&emsp;//最小高宽比文本<br/>
 *           &emsp;&emsp;&emsp;&emsp;&emsp;&ensp;maxRatio: string &emsp;&emsp;&emsp;&emsp;//最大高宽比，limitType=1时有效<br/>
 *           &emsp;&emsp;&emsp;&emsp;&emsp;&ensp;maxRatioText: string &emsp;&emsp;//最大高宽比文本，limitType=1时有效<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;},<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;returnData: {} &emsp;&emsp;&emsp;//自定义返回数据。方法返回时，直接将此对象作为返回结果的一部分<br/>
 *           &emsp;&emsp;&emsp;&ensp;}<br/>
 *    返回： resolve = 结果对象，格式为：<br/>
 *           &emsp;&emsp;&emsp;&ensp;{ <br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;file: object &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;//参数传入的File对象，其中图片被改为压缩后的数据<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;errorText: string &emsp;&emsp;&emsp;//''表示正常，否则表示校验错误<br/>
 *           &emsp;&emsp;&emsp;&emsp;&ensp;returnData: object &emsp;&emsp;//直接引用limitation参数中的returnData<br/>
 *           &emsp;&emsp;&emsp;&ensp;}<br/>
 * @property {method} validateFileAttrs(file,maxSize) 校验文件的基本属性，包括文件名、MIME类型、大小。校验规则参考validateAndCompressImg()<br/>
 *    参数： file = object 待校验的图片File对象<br/>
 *           &emsp;&emsp;&emsp;&ensp;maxSize = number 文件最大值，单位为KB<br/>
 *    返回： string = 空表示正常，否则为错误信息<br/>
 * @property {method} validateImageAttrs(imgObj,limitation) 校验图片属性，如宽高比等等。校验规则参考validateAndCompressImg()<br/>
 *    参数： imgObj = object 待校验的Image对象<br/>
 *           &emsp;&emsp;&emsp;&ensp;limitation = object 校验限制条件，说明参考validateAndCompressImg()<br/>
 *    返回： string = 空表示正常，否则为错误信息<br/>
 * 
 * @Author: liusheng,wanqian
 * @Date: 2022-9-5 12:28:58
 */
let ImageUtil = {
    /**
     * @Description: 根据图片file对象创建Image对象
     * @Author: wanqian, liusheng
     * @Date: 2022-08-09 11:36:23
     * 
     * @param {object} file 图片File对象
     * @return {resolve} 包含Image对象信息，格式为：{ img: Image对象, width: 图片宽, height: 图片高}
     */
    file2Image(file) {
        return new Promise((resolve, reject) => {
            let img = new Image();
            img.src = commonUtil.file2Url(file);
            img.onload = () => {
                // 加载完之后才能获取到图片宽高
                resolve({ img: img, width: img.naturalWidth, height: img.naturalHeight });
            };
            img.onerror = () => {
                reject("图片文件加载失败");
            }
        })
    },

    /**
     * @Description: 将图片的Base64数据转换为File对象
     * @Author: wanqian, liusheng
     * @Date: 2022-08-10 11:36:23
     * 
     * @param {string} data base64数据，带Base64类型头部信息
     * @param {string} filename 指定的File对象文件名
     * @return {object} File对象
     */
    imgBase64ToFile(data, filename) {
        let result = commonUtil.decodeBase64FileData(data);
        return new File([result[0]], filename, { type: result[1] });
    },

    /**
     * @Description: 将图片Blob对象(file.raw)转换为Base64数据
     * @Author: liusheng
     * @Date: 2024-11-13 10:36:23
     * 
     * @param {object} imgBlob 图片的Blob对象，即file.raw
     * @return {string} base64数据，带Base64类型头部信息
     */
    blobToBase64(imgBlob) {
        return new Promise((resolve, reject) => {
            const fileReader = new FileReader();
            fileReader.onload = (e) => {
                resolve(e.target.result);
            };
            fileReader.readAsDataURL(imgBlob);
            fileReader.onerror = () => {
                reject(new Error("blobToBase64 error"));
            };
        });
    },

    /**
     * @Description: 压缩图片数据到指定的宽高
     * @Author: wanqian, liusheng
     * @Date: 2022-08-09 11:36:23
     * @param {string} image 图片数据
     * @param {string} imgType 图片的MIME类型
     * @param {number} width 目标宽度
     * @param {number} height 目标高度
     * @return {string} 压缩后的图片数据
     */
    compressImg(image, imgType, width, height) {
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');
        canvas.width = width;
        canvas.height = height;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(image, 0, 0, width, height);
        return canvas.toDataURL(imgType, 1);
    },

    /**
     * @Description: (上传的)图片文件全校验，并压缩至指定宽高。校验规则如下：
     * 1、图片文件属性校验，包括：
     *    (a) 文件名只能包含字母、数字、汉字和“.”，不能有其他特殊字符
     *    (b) MIME类型只能是image/jpeg、image/jpg或image/png
     *    (c) 文件大小是否超限
     * 2、图片属性校验，包括：
     *    (a) 最小宽(如100) 验证提示：图片宽度不应低于100像素
     *    (b) 以下二选一
     *       (b1) 宽高比(如100:60-100:70) 验证提示：图片宽高比例应在100:60~100:70之间
     *       (b2) 最小高度(100:60) 验证提示：图片宽高比例不应低于100:60
     * 
     * @Author: wanqian, liusheng
     * @Date: 2022-08-09 11:36:23
     * 
     * @param {object} file 必传，图片的File对象
     * @param {object} limitation 必传，校验用的限制条件，格式为：
     * {
     *     maxSize: {number}                //文件最大尺寸，单位为KB
     *     maxWidth: {number}               //图片最大宽度
     *     maxHeight: {number}              //图片最大高度
     *     extLimit: {                      //扩展限制条件，用于2-(b)
     *         limitType: {number}          //适用条件类型。1=适用于2-(b1), 2=适用于2-(b1)
     *         minRatio: {number}           //最小高宽比
     *         minRatioText: {string}       //最小高宽比文本
     *         maxRatio: {string}           //最大高宽比，limitType=1时有效
     *         maxRatioText: {string}       //最大高宽比文本，limitType=1时有效
     *     },
     *     returnData: {}                   //自定义返回数据。方法返回时，直接将此对象作为返回结果的一部分
     * }
     * @return {resolve} 结果对象,格式为：
     * { 
     *     file: {object}                   //参数File对象，图片被改为压缩后的数据
     *     errorText: {string}              //''-表示正常，否则表示校验错误
     *     returnData: {object}             //直接引用limitation参数中的returnData
     * }
     */
    async validateAndCompressImg(file, limitation) {
        //校验1
        let result = this.validateFileAttrs(file, limitation.maxSize);
        if (result) {
            return { file: file, errorText: result, returnData: limitation.returnData };
        }

        let imgObj;
        try {
            imgObj = await this.file2Image(file);
        } catch (err) {
            return { file: file, errorText: err, returnData: limitation.returnData };
        }

        //校验2
        result = this.validateImageAttrs(imgObj, limitation);
        if (result) {
            return { file: file, errorText: result, returnData: limitation.returnData };
        }

        //宽度为限制宽度后得到的比例高度
        let scaleHeight = Math.ceil(imgObj.height * limitation.maxWidth / imgObj.width);
        let imgType = file && file.raw && file.raw.type ? file.raw.type : 'image/jpeg';
        let compressedData = this.compressImg(imgObj.img, imgType, limitation.maxWidth, scaleHeight);

        // 压缩后调用方法进行base64转File
        let imgFile = this.imgBase64ToFile(compressedData, file.name);
        file.raw = imgFile;
        file.size = imgFile.size;
        return { file: file, errorText: '', returnData: limitation.returnData };
    },

    /**
     * @Description: 校验文件的基本属性，包括文件名、MIME类型、大小。校验规则参考validateAndCompressImg()
     * @Author: wanqian, liusheng
     * @Date: 2022-05-16 11:29:23
     * 
     * @param {object} file 待校验的图片File对象
     * @param {number} maxSize 文件最大值，单位为KB
     * @return {string} ''-表示正常，否则为错误信息
     */
    validateFileAttrs(file, maxSize) {
        const filenameReg = /^[A-Za-z0-9.\-\(\)\u4e00-\u9fa5]+$/;
        if (!filenameReg.test(file.raw.name)) {
            return '文件名只能包含字母、数字、汉字、()、- 和 .';
        }

        let mimeType = file.raw.type;
        if (mimeType != 'image/jpeg' && mimeType != 'image/jpg' && mimeType != 'image/png') {
            return '图片只能是JPG、JPEG或PNG格式';
        }

        let size = file.raw.size / 1024.0;
        if (maxSize && maxSize > 0) {
            if (size > maxSize) {
                return (maxSize < 1024) ? ('文件不应超过' + maxSize + 'K') :
                    ('文件不应超过' + maxSize / parseFloat(1024.0) + 'M');
            }
        }
        return '';
    },

    /**
     * @Description: 校验图片属性，如宽高比等等。校验规则参考validateAndCompressImg()
     * @Author: wanqian, liusheng
     * @Date: 2022-05-16 11:29:23
     * 
     * @param {object} imgObj 待校验的Image对象
     * @param {object} limitation 校验限制条件，对象说明参考validateAndCompressImg()
     * @return {string} ''-表示正常，否则为错误信息
     */
    validateImageAttrs(imgObj, limitation) {
        if (imgObj.width < limitation.maxWidth) {
            return "图片宽度不应低于" + limitation.maxWidth + '像素';
        }

        let extLimit = limitation.extLimit;
        // 校验类型1
        if (extLimit.limitType == 1) {
            // 根据图片宽度和宽高比范围算得的高
            let imgHeightMin = Math.ceil(imgObj.width * extLimit.minRatio);
            let imgHeightMax = Math.ceil(imgObj.width * extLimit.maxRatio);
            if (imgObj.height < imgHeightMin || imgObj.height >= imgHeightMax) {
                return "请上传" + imgObj.width + 'x' + imgHeightMin + '~' +
                    imgObj.width + 'x' + imgHeightMax + '之间的图片';
            }
        }
        //校验类型2
        if (extLimit.limitType == 2) {
            // 根据图片宽度和最小高度比例算得的高
            let imgHeight = Math.ceil(imgObj.width * extLimit.minRatio);
            if (imgObj.height < imgHeight) {
                return "请上传宽度为" + imgObj.width + '、高度不小于' + imgHeight + '的图片';
            }
        }

        return '';
    },
}

export default ImageUtil