/**
 * 文件上传SDK
 *     1、兼容性说明：
 *         仅兼容IE10+、Firefox、Chrome等支持slice和localStorage的浏览器。
 *     2、SDK依赖于jQuery和crypto-js插件，请提前引入。
 *     3、执行getInitInfo初始化前需要先调用鉴权接口（应用服务器自行实现）：http://dev.yunxin.163.com/docs/product/%E7%82%B9%E6%92%AD/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E7%BB%88%E7%AB%AF%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E（API token校验）。
 *     4、获取鉴权信息后，请修改getInitInfo函数，传入获取的鉴权信息，并修改AJAX回调实现。
 *     5、调用方法：Uploader().init({
 *         //配置对象，将覆盖默认配置
 *         fileInputId: '',
 *         fileUploadId: '',ajax
 *         getInitInfo: function(file, callback){
 *             ...
 *         }
 *         ...
 *     })
 *     其中，配置对象的fileInputId、fileUploadId、getInitInfo，以及所有onXxx等回调函数需要自行修改实现（以配置对象参数方式传入init函数）。
 * @module uploader
 * @class Uploader
 * @static
 * @param {Object} options 配置项对象
 * @return {Object} 接口对象
 * @author luoweiping
 * @version 1.0.0
 */
import $ from 'jquery/src/core';
import 'jquery/src/ajax';
import 'jquery/src/ajax/xhr';
import 'jquery/src/event';
import MD5 from 'crypto-js/md5';
function Uploader(options) {
  'use strict';

  var opts = {},
    defaults = {
      // 字段accid、token为用户鉴权后获取（服务端实现）
      AppKey: '',
      // 开发者平台appKey
      nonce: '',
      //随机数（随机数，最大长度128个字符）
      curTime: '',
      //当前UTC时间戳，从1970年1月1日0点0分0秒开始到现在的秒数
      checkSum: '',
      //服务器认证需要,SHA1(AppSecret+Nonce+CurTime),16进制字符小写

      /**
       * 分片大小
       * @attribute trunkSize
       * @writeOnce
       * @type {Number}
       */
      trunkSize: 4 * 1024 * 1024,
      /**
       * 获取dns列表的URL
       * @attribute urlDns
       * @writeOnce
       * @type {String}
       */
      urlDns: 'https://wanproxy.127.net/lbs',
      /**
       * 上传输入框元素ID
       * @attribute fileInputId
       * @writeOnce
       * @type {String}
       */
      fileInputId: 'fileInput',
      /**
       * 上传按钮ID
       * @attribute fileUploadId
       * @writeOnce
       * @type {String}
       */
      fileUploadId: 'fileUpload',
      fileExts: ['JPG', 'PNG', 'WMV', 'ASF', 'AVI', '3GP', 'MKV', 'MP4', 'DVD', 'OGM', 'MOV', 'MPG', 'MPEG', 'MPE', 'FLV', 'F4V', 'SWF', 'M4V', 'QT', 'DAT', 'VOB', 'RMVB', 'RM', 'OGM', 'M2TS', 'MTS', 'TS', 'TP', 'WEBM', 'MP3', 'AAC'],
      /**
       * 获取初始化信息
       *     发送请求到视频云服务端或应用服务器，参数见代码注释；
       *     其中，typeId和presetId需自行获取(接口文档暂未发布，请联系客服)，headers参数为API token校验返回的结果(必填)
       * @method fileUploadId
       * @static
       * @param  {Object}   file     文件对象
       *      fileKey: 对文件名和文件大小进行md5后的结果
       *      file: File对象
       *      fileName: 文件名（作为file.name的备份）
       *      fileSizeMb: 文件大小（MB）
       *      format: 文件后缀
       *      status: 上传状态（0：待上传，1：上传中；2：上传完成）
       *      checked: 是否选中（用于列表）
       *      progress: 上传进度
       * @param  {Function} callback 回调函数
       *      回调函数的参数包括：
       *      bucketName: 桶名
       *      objectName: 对象名
       *      nosToken: x-nos-token
       * @return {void}
       * @version 1.0.0
       */
      getInitInfo: function getInitInfo(file, callback) {
        var context;
        context = localStorage.getItem(file.fileKey + '_context');
        if (!context) {
          $.ajax({
            type: 'post',
            url: 'https://vcloud.163.com/app/vod/upload/init',
            data: JSON.stringify({
              originFileName: file.file.name,
              //上传文件的原始名称(包含后缀名)(必填)(规则同Windows文件名规则)
              userFileName: file.file.name,
              //用户命名的上传文件名称(规则同Windows文件名规则)
              typeId: null,
              //视频所属的类别ID
              presetId: 104560494,
              //视频所需转码模板ID
              // watermarkId: 28147, //视频水印ID
              callbackUrl: null,
              //转码成功后回调客户端的URL地址
              description: null //上传视频的描述信息
            }),

            headers: {
              AppKey: opts.AppKey,
              Nonce: opts.nonce,
              CurTime: opts.curTime,
              CheckSum: opts.checkSum
            },
            dataType: 'json',
            contentType: 'application/json',
            success: function success(data) {
              if (data.code === 200) {
                localStorage.setItem(file.fileKey + '_bucket', data.ret.bucket);
                localStorage.setItem(file.fileKey + '_object', data.ret.object);
                localStorage.setItem(file.fileKey + '_xNosToken', data.ret.xNosToken);
                callback({
                  bucketName: data.ret.bucket,
                  objectName: data.ret.object,
                  nosToken: data.ret.xNosToken
                });
              } else {
                opts.onError({
                  errCode: data.Code,
                  errMsg: data.msg
                });
              }
            },
            error: function error(xhr, s, err) {
              opts.onError(err);
            }
          });
        } else {
          callback({
            bucketName: localStorage.getItem(file.fileKey + '_bucket'),
            objectName: localStorage.getItem(file.fileKey + '_object'),
            nosToken: localStorage.getItem(file.fileKey + '_xNosToken')
          });
        }
      },
      /**
       * 错误处理回调
       * @method onError
       * @static
       * @param  {Object} errObj 带errCode和errMsg的Object或XHR错误对象
       * @return {void}
       * @version 1.0.0
       */
      onError: function onError(errObj) {
        console.error(errObj);
      },
      /**
       * 文件上传成功处理回调
       * @method onError
       * @static
       * @param  {Object} errObj 带errCode和errMsg的Object或XHR错误对象
       * @return {void}
       * @version 1.0.0
       */
      onSuccess: function onSuccess() {
        console.log('文件上传成功');
      },
      /**
       * 上传进度回调
       * @method onProgress
       * @static
       * @param  {Object} curFile 文件对象
       * @return {void}
       * @version 1.0.0
       */
      onProgress: function onProgress(curFile) {
        console.log(curFile.status);
        console.log(curFile.progress);
      },
      /**
       * 单文件上传成功回调
       * @method onUploaded
       * @static
       * @param  {Object} curFile 文件对象
       * @return {void}
       * @version 1.0.0
       */
      onUploaded: function onUploaded(service, curFile) {
        // 将文件信息存入上传成功列表
        service.successList.push(curFile);
        // opts.onSuccess(curFile);
        /**
         * 用于获取vid等信息，暂只支持在单个文件上传成功后的回调中进行
         * 在全部上传成功的回调中发起请求会导致在上传失败时无法执行请求（接口的URL、参数格式、响应格式等均相同）
         */
        $.ajax({
          type: 'post',
          url: 'https://vcloud.163.com/app/vod/video/query',
          data: JSON.stringify({
            objectNames: [curFile.objectName]
          }),
          dataType: 'json',
          contentType: 'application/json',
          headers: {
            AppKey: opts.AppKey,
            Nonce: opts.nonce,
            CurTime: opts.curTime,
            CheckSum: opts.checkSum
          },
          success: function success(data) {
            if (data.code === 200) {
              var _data$ret, _data$ret$list$;
              // 根据vid查询音视频文件信息
              $.ajax({
                type: 'post',
                url: 'https://vcloud.163.com/app/vod/video/get',
                data: JSON.stringify({
                  vid: (_data$ret = data.ret) === null || _data$ret === void 0 ? void 0 : (_data$ret$list$ = _data$ret.list[0]) === null || _data$ret$list$ === void 0 ? void 0 : _data$ret$list$.vid
                }),
                dataType: 'json',
                contentType: 'application/json',
                headers: {
                  AppKey: opts.AppKey,
                  Nonce: opts.nonce,
                  CurTime: opts.curTime,
                  CheckSum: opts.checkSum
                },
                success: function success(data) {
                  opts.onSuccess(data.ret);
                }
              });
            } else {
              opts.onError({
                errCode: data.Code,
                errMsg: data.msg
              });
            }
          },
          error: function error(xhr, s, err) {
            opts.onError(err);
          }
        });
      },
      /**
       * 全部文件上传成功回调
       * @method onAllUploaded
       * @static
       * @return {void}
       * @version 1.0.0
       */
      onAllUploaded: function onAllUploaded() {
        console.log('All done.');
      },
      /**
       * 文件添加成功回调
       * @method onAdd
       * @static
       * @param  {File} fileObj 文件对象
       * @return {void}
       * @version 1.0.0
       */
      onAdd: function onAdd(curFile) {
        console.log(curFile.file.name + ': ' + curFile.fileSizeMb + ' MB');
      },
      /**
       * 无文件上传时的处理函数
       * @method noUploadFn
       * @static
       * @return {void}
       * @version 1.0.0
       */
      noUploadFn: function noUploadFn() {
        console.error('请选择待上传的文件');
      },
      /**
       * 文件已存在列表中的处理函数
       * @method existFn
       * @static
       * @return {void}
       * @version 1.0.0
       */
      existFn: function existFn() {
        console.error('文件已存在列表中');
      },
      /**
       * 文件格式不匹配的处理函数
       * @method existFn
       * @static
       * @return {void}
       * @version 1.0.0
       */
      mismatchFn: function mismatchFn() {
        console.error('格式错误');
        opts.onError();
      }
    },
    service;
  opts = $.extend({}, defaults, options);
  service = {
    /**
     * @property {Array} fileList 文件列表
     */
    fileList: [],
    /**
     * @property {Array} successList 上传成功的文件列表
     */
    successList: [],
    /**
     * @property {Array} dnsList DNS列表
     */
    dnsList: null,
    /**
     * 获取上传DNS地址
     * @method getDNS
     * @static
     * @param  {Object}   param    AJAX参数
     * @param  {Function} callback 成功回调
     * @return {void}
     * @version 1.0.0
     */
    getDNS: function getDNS(param, callback) {
      if (service.dnsList) {
        //已缓存则直接取缓存数据
        callback(service.dnsList);
      } else {
        return callback(['https://wanproxy-web.127.net']);
      }
    },
    /**
     * 删除文件，终止上传并从列表中移除（进度保持不变）
     * @method removeFile
     * @static
     * @param  {Object} file 文件对象
     * @return {void}
     * @version 1.0.0
     */
    removeFile: function removeFile(file) {
      $.each(service.fileList, function (i, v) {
        if (v.fileKey === file.fileKey) {
          if (v.xhr) {
            v.xhr.upload.onprogress = $.noop;
            v.xhr.onreadystatechange = $.noop;
            v.xhr.abort();
            v.xhr = null;
          }
          service.fileList.splice(i, 1);
          if (v.status === 1) {
            service.upload(i);
          }
          return false;
        }
      });
    },
    /**
     * 根据fileKey获取指定文件对象
     * @method getFile
     * @static
     * @param  {String} fileKey 文件名和文件大小md5值
     * @return {Obejct}         文件对象
     * @version 1.0.0
     */
    getFile: function getFile(fileKey) {
      var curFile;
      $.each(service.fileList, function (i, v) {
        if (v.fileKey === fileKey) {
          curFile = v;
          return false;
        }
      });
      return curFile;
    },
    /**
     * 上传分片
     * @method uploadTrunk
     * @static
     * @param  {Object}   param     AJAX参数
     * @param  {Object}   trunkData 分片数据
     * @param  {Function} callback  文件（非分片）上传成功回调函数
     * @return {void}
     * @version 1.0.0
     */
    uploadTrunk: function uploadTrunk(param, trunkData, callback) {
      var xhr,
        xhrParam = '',
        curFile,
        context;
      curFile = service.getFile(trunkData.fileKey);
      context = localStorage.getItem(trunkData.fileKey + '_context');
      if (curFile.xhr) {
        xhr = curFile.xhr;
      } else {
        xhr = new XMLHttpRequest();
        curFile.xhr = xhr;
      }
      xhr.upload.onprogress = function (e) {
        var progress = 0;
        if (e.lengthComputable) {
          progress = (trunkData.offset + e.loaded) / trunkData.file.size;
          curFile.progress = (progress * 100).toFixed(2);
          if (progress > 0 && progress < 1) {
            curFile.status = 1;
          } else if (progress === 1) {
            curFile.status = 2;
          }
          localStorage.setItem(trunkData.fileKey + '_progress', curFile.progress);
          opts.onProgress(curFile);
        } else {
          opts.onError({
            errCode: 501,
            errMsg: '浏览器不支持进度事件'
          });
        }
      };
      xhr.onreadystatechange = function () {
        if (xhr.readyState !== 4) {
          return;
        }
        var result;
        try {
          result = JSON.parse(xhr.responseText);
        } catch (e) {
          result = {
            errCode: 500,
            errMsg: '未知错误'
          };
        }
        if (xhr.status === 200) {
          if (!result.errCode) {
            localStorage.setItem(trunkData.fileKey + '_context', result.context);
            if (result.offset < trunkData.file.size) {
              //上传下一片
              service.uploadTrunk(param, $.extend({}, trunkData, {
                offset: result.offset,
                trunkEnd: result.offset + trunkData.trunkSize,
                context: context || result.context
              }), callback);
            } else {
              //单文件上传结束
              callback(service, trunkData);
            }
          } else {
            service.clearStorage(trunkData.fileKey);
            opts.onError({
              errCode: result.errCode,
              errMsg: result.errMsg
            });
          }
        } else {
          if (xhr.status) {
            //nos error
            service.clearStorage(trunkData.fileKey);
          }
          //取消、关闭情况
          opts.onError(xhr.responseText);
        }
      };
      xhrParam = '?offset=' + trunkData.offset + '&complete=' + (trunkData.trunkEnd >= trunkData.file.size) + '&context=' + (context || trunkData.context) + '&version=1.0';
      xhr.open('post', param.serveIp + '/' + param.bucketName + '/' + param.objectName + xhrParam);
      xhr.setRequestHeader('x-nos-token', param.nosToken);
      xhr.send(trunkData.file.slice(trunkData.offset, trunkData.trunkEnd));
    },
    /**
     * 获取上传断点位置
     * @method getOffset
     * @static
     * @param  {Object}   param    AJAX参数
     * @param  {Function} callback 获取成功回调
     * @return {void}
     * @version 1.0.0
     */
    getOffset: function getOffset(param, callback) {
      var context;
      context = localStorage.getItem(param.fileKey + '_context');
      if (!context) {
        return callback(0);
      }
      $.ajax({
        type: 'get',
        url: param.serveIp + '/' + param.bucketName + '/' + param.objectName + '?uploadContext',
        data: {
          version: '1.0',
          context: context
        },
        dataType: 'json',
        beforeSend: function beforeSend(xhr) {
          xhr.setRequestHeader('x-nos-token', param.nosToken);
        },
        success: function success(data) {
          if (data.errCode) {
            opts.onError({
              errCode: data.errCode,
              errMsg: data.errMsg
            });
          } else {
            callback(data.offset);
          }
        },
        error: function error(xhr, s, err) {
          opts.onError(err);
        }
      });
    },
    clearStorage: function clearStorage(fileKey) {
      localStorage.removeItem(fileKey + '_progress');
      localStorage.removeItem(fileKey + '_context');
      localStorage.removeItem(fileKey + '_created');
      localStorage.removeItem(fileKey + '_bucket');
      localStorage.removeItem(fileKey + '_object');
      localStorage.removeItem(fileKey + '_xNosToken');
    },
    /**
     * 上传文件操作
     * @method upload
     * @static
     * @param  {Number} fileIdx 文件索引
     * @return {void}
     * @version 1.0.0
     */
    upload: function upload(fileIdx) {
      if (fileIdx < service.fileList.length) {
        if (service.fileList[fileIdx].status === 2 || !service.fileList[fileIdx].checked) {
          //上传完成或未勾选
          return service.upload(fileIdx + 1);
        }
        opts.getInitInfo(service.fileList[fileIdx], function (data) {
          var curFile = service.fileList[fileIdx];
          curFile.objectName = data.objectName;
          curFile.bucketName = data.bucketName;
          service.getDNS(data, function (dnsList) {
            var curFile = service.fileList[fileIdx];
            service.getOffset({
              serveIp: dnsList[0],
              bucketName: data.bucketName,
              objectName: data.objectName,
              nosToken: data.nosToken,
              fileKey: service.fileList[fileIdx].fileKey
            }, function (offset) {
              service.uploadTrunk({
                serveIp: dnsList[0],
                bucketName: data.bucketName,
                objectName: data.objectName,
                nosToken: data.nosToken
              }, {
                file: service.fileList[fileIdx].file,
                fileKey: service.fileList[fileIdx].fileKey,
                fileIdx: fileIdx,
                offset: offset || 0,
                trunkSize: opts.trunkSize,
                trunkEnd: (offset || 0) + opts.trunkSize,
                context: ''
              }, function (trunkData) {
                service.clearStorage(trunkData.fileKey);
                opts.onUploaded(service, curFile);
                service.upload(fileIdx + 1);
              });
            });
          });
        });
      } else {
        opts.onAllUploaded();
      }
    },
    /**
     * 添加文件
     * @method addFile
     * @static
     * @param {File}   file 上传文件
     * @param {Function} callback  添加成功回调
     * @return {void}
     * @version 1.0.0
     */
    addFile: function addFile(file, callback) {
      var fileKey = MD5(file.name + ':' + file.size).toString(),
        fileObj;
      fileObj = {
        fileKey: fileKey,
        file: file,
        fileName: file.name,
        fileSizeMb: (file.size / 1024 / 1024).toFixed(2),
        format: file.name.split('.').pop(),
        status: 0,
        checked: true,
        progress: localStorage.getItem(fileKey + '_progress') || 0
      };
      service.fileList.push(fileObj);
      localStorage.setItem(fileKey + '_created', +new Date());
      callback(file, fileObj);
    },
    /**
     * 判断文件是否已存在列表中
     * @method checkExist
     * @static
     * @param  {File} file File对象
     * @return {Boolean}      存在：true，不存在：false
     * @version 1.0.0
     */
    checkExist: function checkExist(file) {
      var exist = false,
        curKey = MD5(file.name + ':' + file.size).toString();
      $.each(service.fileList, function (i, v) {
        if (curKey === v.fileKey) {
          exist = true;
          return false;
        }
      });
      return exist;
    },
    /**
     * 判断是否有待上传（已选中且上传未完成）的文件
     * @method checkedPending
     * @static
     * @return {Boolean} 有：true，无：false
     * @version 1.0.0
     */
    checkedPending: function checkedPending() {
      var checked = false;
      $.each(service.fileList, function (i, v) {
        if (v.checked && v.status === 0) {
          checked = true;
          return false;
        }
      });
      return checked;
    },
    /**
     * 事件绑定
     * @method initEvent
     * @static
     * @return {void}
     * @version 1.0.0
     */
    initEvent: function initEvent() {
      $('#' + opts.fileInputId).on('change', function (e) {
        var fileExt = '';
        if (e.target.files) {
          if (!service.checkExist(e.target.files[0])) {
            fileExt = e.target.files[0].name.split('.').pop();
            fileExt = fileExt.toUpperCase();
            if ($.inArray(fileExt, opts.fileExts) < 0) {
              return opts.mismatchFn();
            }
            service.addFile(e.target.files[0], function (file, fileObj) {
              opts.onAdd(fileObj);
            });
          } else {
            this.value = '';
            opts.existFn();
          }
        }
      });
      $('#' + opts.fileUploadId).on('click', function () {
        if (!service.checkedPending()) {
          opts.noUploadFn();
          return false;
        }
        localStorage.clear();
        service.upload(0);
        return false;
      });
    },
    /**
     * 初始化
     * @method init
     * @static
     * @return {void}
     * @version 1.0.0
     */
    init: function init() {
      service.initEvent();
    },
    /**
     * 自定义初始化
     * @method initCustom
     * @static
     * @return {void}
     * @version 1.0.0
     */
    initCustom: function initCustom(file) {
      var fileExt = '';
      if (file) {
        if (!service.checkExist(file)) {
          fileExt = file.name.split('.').pop();
          fileExt = fileExt.toUpperCase();
          if ($.inArray(fileExt, opts.fileExts) < 0) {
            return opts.mismatchFn();
          }
          service.addFile(file, function (file, fileObj) {
            opts.onAdd(fileObj);
          });
        } else {
          opts.existFn();
          return;
        }
        if (!service.checkedPending()) {
          opts.noUploadFn();
          return;
        }
        localStorage.clear();
        service.upload(0);
      }
    }
  };
  return service;
}
export default Uploader;