/* eslint-disable */
// 基于 canvas 压缩图片为 jpg 格式
// 不支持裁剪，只做压缩

/*=============================================================================================*/
// 已发现并修复的 andriod bug:

//   1.调用 canvas.toDataURL('image/jpeg') 返回 image/png，导致图片变大
//     解决：
//       使用 JPEGEncode 生成 jpg

//   2.调用 FileReader.readDataAsURL() 没有返回 mime type，导致图片载入失败
//     解决：
//       侦测文件类型，将获取的 mime type 补进 base64
/*=============================================================================================*/

/*=============================================================================================*/
// 已发现并修复的 ios bug:

// ios bug 修复参考：https://github.com/stomita/ios-imagefile-megapixel
/*=============================================================================================*/





import JPEGEncoder from './jpegencoder';
import EXIF        from './exif';
import blobutil    from './blobutil';
import imagemime   from './imagemime';


var ua = window.navigator.userAgent;
var android = /Android/.test(ua);
var ios = /(?:iPhone|iPod|iPad)/.test(ua);





/**
 * Detect subsampling in loaded image.
 * In iOS, larger images than 2M pixels may be subsampled in rendering.
 *
 * 用载入的图片检测二次抽样
 * 在 ios 中，如果图片的像素数大于 2M 的话，渲染的时候可能会被二次抽样
 */
function detectSubsampling(img) {
  var iw = img.naturalWidth || img.width;
  var ih = img.naturalHeight || img.height;
  if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
    var canvas = document.createElement('canvas');
    canvas.width = canvas.height = 1;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(img, -iw + 1, 0);
    // subsampled image becomes half smaller in rendering size.
    // check alpha channel value to confirm image is covering edge pixel or not.
    // if alpha value is 0 image is not covering, hence subsampled.

    // 渲染的时候，二次抽样后的图片会变成一半的大小。
    // 检查 alpha 通道的值来确定图片是否覆盖了边缘像素。
    // 如果 alpha 的值是 0 的话，说明图片没有覆盖边缘像素，于是可以确定图片已经被二次抽样了。

    return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
  } else {
    return false;
  }
}

/**
 * Detecting vertical squash in loaded image.
 * Fixes a bug which squash image vertically while drawing into canvas for some images.
 *
 * 用载入的图片检测垂直压扁
 * 修复了一个这样的BUG：当绘制一些图片到画布中时，图片会被垂直压扁
 */
function detectVerticalSquash(img, iw, ih) {
  var canvas = document.createElement('canvas');
  canvas.width = 1;
  canvas.height = ih;
  var ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  var data = ctx.getImageData(0, 0, 1, ih).data;
  // search image edge pixel position in case it is squashed vertically.
  var sy = 0;
  var ey = ih;
  var py = ih;
  while (py > sy) {
    var alpha = data[(py - 1) * 4 + 3];
    if (alpha === 0) {
      ey = py;
    } else {
      sy = py;
    }
    py = (ey + sy) >> 1;
  }
  var ratio = (py / ih);
  return (ratio === 0) ? 1 : ratio;
}

/**
  * Transform canvas coordination according to specified frame size and orientation
  * Orientation value is from EXIF tag
*/
function transformCoordinate(canvas, ctx, width, height, orientation) {
  switch (orientation) {
    case 5:
    case 6:
    case 7:
    case 8:
      canvas.width = height;
      canvas.height = width;
      break;
    default:
      canvas.width = width;
      canvas.height = height;
  }
  switch (orientation) {
    case 2:
      // horizontal flip
      ctx.translate(width, 0);
      ctx.scale(-1, 1);
      break;
    case 3:
      // 180 rotate left
      ctx.translate(width, height);
      ctx.rotate(Math.PI);
      break;
    case 4:
      // vertical flip
      ctx.translate(0, height);
      ctx.scale(1, -1);
      break;
    case 5:
      // vertical flip + 90 rotate right
      ctx.rotate(0.5 * Math.PI);
      ctx.scale(1, -1);
      break;
    case 6:
      // 90 rotate right
      ctx.rotate(0.5 * Math.PI);
      ctx.translate(0, -height);
      break;
    case 7:
      // horizontal flip + 90 rotate right
      ctx.rotate(0.5 * Math.PI);
      ctx.translate(width, -height);
      ctx.scale(-1, 1);
      break;
    case 8:
      // 90 rotate left
      ctx.rotate(-0.5 * Math.PI);
      ctx.translate(-width, 0);
      break;
    default:
      break;
  }
}




// 压缩图片
// file 为从 input file 控件中选择的文件对象
// options：
// {
//   quality: 80 压缩质量，数值越大质量越高，反之则越小
//   size: 640 压缩后的图片大小，按长边等比例缩放至 size 指定的大小
//   cb: function(dataURL) {
//     // 压缩后的回调函数
//     // dataURL 为jpg数据的base64字符串
//   }
// }
function androidCompress(file, options) {
  var reader = new FileReader();
  reader.onloadend = function(e) {
    // reader 对象
    var host = e.target;
    // 读取的文件内容
    var data = host.result;
    // 错误对象
    var error= host.error;
    // 错误消息
    var errorMsg;

    // 二进制字节数组
    var rawBinary;
    // 内存图片
    var memImg;
    // 图片 mime type
    var mimeType;

    if (error != null) {
      switch (error.code) {
        case error.NOT_FOUND_ERR:
          errorMsg = 'File not found!';
          break;
        case error.SECURITY_ERR:
          errorMsg = 'Security issue with file!';
          break;
        case error.NOT_READABLE_ERR:
          errorMsg = 'File could not be read!';
          break;
        case error.ENCODING_ERR:
          errorMsg = 'Encoding error!';
          break;
        default:
          errorMsg = 'Unknown issue!';
      };
      throw new Error(errorMsg);
    } else {
      // 检测文件类型必须为图片
      rawBinary = blobutil.base64ToArrayBuffer(data);
      mimeType = imagemime(rawBinary);
      if (mimeType == null) {
        throw new Error('请选择图片');
      }

      // 如果没有返回 mime type ，则自动补
      if (!/^data:image\/.+?;base64,/i.test(data)) {
        data = 'data:' + mimeType + ';base64,' + data.split(',')[1];
      }

      // 载入内存图片，根据配置压缩图片
      memImg = new Image();
      memImg.onload = function() {
        // 获取选择的图片的方向信息
        var tags = EXIF.readFromBinaryFile(rawBinary.buffer);
        var orientation = tags.Orientation || 1;

        // 配置参数
        var opts = Object.create(options || {});
        var optsQuality = opts.quality || 80;
        var optsSize = opts.size || 640;
        var optsCb = opts.cb;

        // 原始图尺寸
        var iw = this.naturalWidth || this.width;
        var ih = this.naturalHeight || this.height;
        // 原始图长边大小
        var lss = iw > ih ? iw : ih;

        // 压缩图尺寸
        var cw;
        var ch;

        // 画布
        var canvas;
        var ctx;
        var dataURL;

        if (optsSize < lss) {
          // 指定的大小比原图小，计算长边的缩放比例，从而计算出短边的大小
          if (iw > ih) {
            cw = optsSize;
            // 向左移动 0 位的作用是转换为整数
            ch = (ih * cw / iw) << 0;
          } else {
            ch = optsSize;
            // 向左移动 0 位的作用是转换为整数
            cw = (iw * ch / ih) << 0;
          }
        } else {
          // 指定的大小比原图大，则使用原图尺寸压缩
          cw = iw;
          ch = ih;
        }

        // 根据图片方向信息设置画布宽高，变换画布坐标系
        canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        transformCoordinate(canvas, ctx, cw, ch, orientation);
        ctx.drawImage(this, 0, 0, cw, ch);
        // 导出压缩后的图片数据
        dataURL = canvas.toDataURL('image/jpeg', optsQuality / 100);

        // 部分设备可能无法正确导出 jpeg data url
        if (!/^data:image\/jpeg;base64,.+$/i.test(dataURL)) {
          // 压缩图片为 jpg 格式
          imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          dataURL = new JPEGEncoder().encode(imageData, optsQuality);
        }
        if (optsCb) {
          optsCb(dataURL);
        }
      };
      memImg.onerror = function() {
        throw new Error('不支持' + mimeType + '格式');
      };
      memImg.src = data;
    }
  };
  reader.readAsDataURL(file);
}
function iosCompress(file, options) {
  var reader = new FileReader();
  reader.onloadend = function(e) {
    // reader 对象
    var host = e.target;
    // 读取的文件内容
    var data = host.result;
    // 错误对象
    var error= host.error;
    // 错误消息
    var errorMsg;

    // 二进制字节数组
    var rawBinary;
    // 内存图片
    var memImg;
    // 图片 mime type
    var mimeType;

    if (error != null) {
      switch (error.code) {
        case error.NOT_FOUND_ERR:
          errorMsg = 'File not found!';
          break;
        case error.SECURITY_ERR:
          errorMsg = 'Security issue with file!';
          break;
        case error.NOT_READABLE_ERR:
          errorMsg = 'File could not be read!';
          break;
        case error.ENCODING_ERR:
          errorMsg = 'Encoding error!';
          break;
        default:
          errorMsg = 'Unknown issue!';
      };
      throw new Error(errorMsg);
    } else {
      // 检测文件类型必须为图片
      mimeType = file.type;
      if (!/^image\/.+/i.test(mimeType)) {
        throw new Error('请选择图片');
      }

      // 载入内存图片，根据配置压缩图片
      memImg = new Image();
      memImg.onload = function() {
        // 获取选择的图片的方向信息
        rawBinary = blobutil.base64ToArrayBuffer(data);
        var tags = EXIF.readFromBinaryFile(rawBinary.buffer);
        var orientation = tags.Orientation || 1;

        // 配置参数
        var opts = Object.create(options || {});
        var optsQuality = opts.quality || 80;
        var optsSize = opts.size || 640;
        var optsCb = opts.cb;


        // 原始图尺寸
        var iw = this.naturalWidth || this.width;
        var ih = this.naturalHeight || this.height;
        // 原始图长边大小
        var lss = iw > ih ? iw : ih;

        // 压缩图尺寸
        var cw;
        var ch;
        var cr;

        // 画布
        var canvas;
        var ctx;
        var subsampled;
        var d;
        var tmpCanvas;
        var tmpCtx;
        var vertSquashRatio;
        var dx;
        var dy;
        var dw;
        var dh;
        var sx;
        var sy;
        var sw;
        var sh;
        var dataURL;
        var imageData;
        var jpegURL;
        var doSquash = mimeType === 'image/jpeg';

        if (optsSize < lss) {
          // 指定的大小比原图小，计算长边的缩放比例，从而计算出短边的大小
          if (iw > ih) {
            cw = optsSize;
            // 向左移动 0 位的作用是转换为整数
            ch = (ih * cw / iw) << 0;
          } else {
            ch = optsSize;
            // 向左移动 0 位的作用是转换为整数
            cw = (iw * ch / ih) << 0;
          }
        } else {
          // 指定的大小比原图大，则使用原图尺寸压缩
          cw = iw;
          ch = ih;
        }


        // 根据图片方向信息设置画布宽高，变换画布坐标系
        canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        ctx.save();
        transformCoordinate(canvas, ctx, cw, ch, orientation);

        // 图片二次抽样处理
        subsampled = detectSubsampling(this);
        if (subsampled) {
          iw /= 2;
          ih /= 2;
        }
        // 超大图片分块处理
        // 将一幅大图分成若干个小块绘制到目标画布上
        // 每个块的大小为 1024 * 1024

        d = 1024;
        tmpCanvas = document.createElement('canvas');
        tmpCanvas.width = tmpCanvas.height = d;
        tmpCtx = tmpCanvas.getContext('2d');
        vertSquashRatio = doSquash ? detectVerticalSquash(this, iw, ih) : 1;
        sy = 0;
        // 分割块循环
        while (sy < ih) {
          // 计算分割块高
          sh = sy + d > ih ? ih - sy : d;
          sx = 0;
          while (sx < iw) {
            // 计算分割块宽
            sw = sx + d > iw ? iw - sx : d;
            // 清空临时画布上的所有内容
            tmpCtx.clearRect(0, 0, d, d);
            // 将整个源图绘制到临时画布上的指定位置，通过指定负坐标将不是当前块的内容绘制到临时画布以外，从而
            // 画布以内的区域就是当前块
            tmpCtx.drawImage(this, -sx, -sy);
            // 当前块绘制到目标画布上的 x 轴的指定位置(如果有缩放进行缩放)
            dx = Math.floor(sx * cw / iw);
            // 当前块绘制到目标画布上的宽(如果有缩放进行缩放)
            dw = Math.ceil(sw * cw / iw);
            // 当前块绘制到目标画布上的 y 轴的指定位置(如果有缩放进行缩放，同时处理垂直压扁比例)
            dy = Math.floor(sy * ch / ih / vertSquashRatio);
            // 当前块绘制到目标画布上的高(如果有缩放进行缩放，同时处理垂直压扁比例)
            dh = Math.ceil(sh * ch / ih / vertSquashRatio);
            // 绘制当前块到目标画布上的指定位置
            ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
            sx += d;
          }
          sy += d;
        }
        ctx.restore();
        tmpCanvas = tmpCtx = null;

        // 导出压缩后的图片数据
        dataURL = canvas.toDataURL('image/jpeg', optsQuality / 100);

        if (optsCb) {
          optsCb(dataURL);
        }
      };
      memImg.onerror = function() {
        throw new Error('不支持' + mimeType + '格式');
      };
      memImg.src = data;
    }
  };
  reader.readAsDataURL(file);
}

var compress = (function() {
  if (ios) {
    return iosCompress;
  }
  return androidCompress;
})();

export default compress
