var angular = require('angular');
require('../modules');
require('../directives/$toast');

/**
 * 依赖于cordova.plugins.filetransfer和cordova.plugins.file插件。
 */

var module = angular.module('commons.fileService'),
  localfsPromise;

module.run(function($q, $window, fileService) {
  var requestFileSystem = $window.requestFileSystem || $window.webkitRequestFileSystem,
    deferred = $q.defer();

  localfsPromise = deferred.promise;
  // requestFileSystem($window.TEMPORARY, 100 * 1024 * 1024 /*100MB*/ , deferred.resolve, deferred.reject);
  if (requestFileSystem) {
    requestFileSystem($window.PERSISTENT, 100 * 1024 * 1024 /*100MB*/ , deferred.resolve, deferred.reject);
  }

  window.fileService = fileService;
});

module.factory('fileService', function($q, CoreAppConfig, $toast) {

  function fcall(obj, methodName) {
    var deferred = $q.defer(),
      args = Array.prototype.slice.call(arguments, 2);

    args.push(deferred.resolve, deferred.reject);

    obj[methodName].apply(obj, args);

    return deferred.promise;
  }

  /**
   * 将文件系统的路径转换成设备系统的绝对路径。
   *
   * @param  {String} path
   * @return {Promise}
   */
  function toURL(path) {
    return localfsPromise.then(function(fs) {
      var url = fs.root.toInternalURL ? fs.root.toInternalURL(): fs.root.toURL();
      return url + path;
    });
  }
  /**
   * 获取文件，不创建。
   * @param  {String} path 文件路径
   * @return {Promise}
   */
  function getFile(path) {
    return localfsPromise.then(function(fs) {
      return fcall(fs.root, 'getFile', path, {
        create: false
      });
    });
  }

  function exist(url) {
    var defer = $q.defer();
    resolveURLtoFileEntry(url).then(function(fileEntry) {
      return fcall(fileEntry, 'file');
    }).then(function() {
      defer.resolve(true);
    }, function() {
      defer.resolve(false);
    });
    return defer.promise;
  }

  /**
   * 创建文件
   * @param  {String} path
   * @return {Promise}
   */
  function createFile(path) {
    return localfsPromise.then(function(fs) {
      return fcall(fs.root, 'getFile', path, {
        create: true,
        exclusive: true
      });
    });
  }

  /**
   * 删除缓存中的文件
   * @param  {Entry} fileEntry 文件对象
   * @return {Promise}           [description]
   */
  function deleteCacheFile(fileEntry) {
    return fcall(fileEntry, 'remove');
  }

  /**
  * 读取文件内容
  * @param path 文件路径
  */
  function readFileAsText(path) {
    var _getFile = getFile;
    if (path.indexOf(':') !== -1) {
      _getFile = resolveURLtoFileEntry;
    }
    return _getFile(path).then(function(fileEntry) {
      var deferred = $q.defer();

      fileEntry.file(function(file) {
        var fileReader = new FileReader();
        fileReader.onload = function() {
          deferred.resolve(fileReader.result);
        };
        fileReader.onerror = deferred.reject;
        fileReader.readAsText(file, 'utf-8');
      }, deferred.reject);

      return deferred.promise;
    });
  }

  /**
   * 向文件中写入文本内容
   *
   * @param  {String} path 文件路径
   * @param  {String} content 文件内容
   * @param  {Boolean} append 是否是追加写入
   * @return {Promise}
   */
  function writeText(path, content, append) {
    append = angular.isDefined(append) ? append : false;
    var blob = new Blob([content], {
      type: 'text/plain'
    });
    return write(path, blob, append);
  }

  /**
   * 向文件中写入ArrayBuffer内容
   *
   * @param  {String} fileName 文件路径
   * @param  {ArrayBuffer} buf 文件内容
   * @param  {String} mimeString 文件mime类型
   * @param  {Boolean} append 是否是追加写入
   * @return {Promise}
   */
  function writeArrayBuffer(fileName, buf, mimeString, append) {
    append = angular.isDefined(append) ? append : false;
    var blob = new Blob([new Uint8Array(buf)], {
      type: mimeString
    });

    return write(fileName, blob, append);
  }

  /**
   * 向文件中写入内容
   *
   * @param  {String} path 文件路径
   * @param  {Blob} blob 文件内容块
   * @param  {Boolean} append 是否是追加写入
   * @return {Promise}
   */
  function write(path, blob, append) {
    append = angular.isDefined(append) ? append : false;
    var fileEntry = null;

    return localfsPromise.then(function(fs) {
        return fcall(fs.root, 'getFile', path, {
          create: true
        });
      })
      .then(function(_fileEntry) {
        fileEntry = _fileEntry;
        return fcall(_fileEntry, 'createWriter');
      })
      .then(function(fileWriter) {
        if (append) {
          fileWriter.seek(fileWriter.length);
        }

        var deferred = $q.defer();
        var truncated = false;

        fileWriter.onwriteend = function() {
          //truncate all data after current position
          if (!truncated) {
            truncated = true;
            this.truncate(this.position);
            return;
          }
          deferred.resolve(path, fileEntry);
        };

        fileWriter.onerror = deferred.reject;

        fileWriter.write(blob);

        return deferred.promise;
      });
  }

  /**
   * 将文件URL解析成文件对象
   * @param  {String} url 文件路径地址
   * @return {Object}     由文件路径解析后的文件对象
   */
  function resolveURLtoFileEntry(url) {
    return fcall(window, 'resolveLocalFileSystemURL', url);
  }

  /**
   * 根据目的路径创建目录对象
   * @param  {String} url 目的路径
   * @return {Object}     解析后创建的目录对象
   */
  function createDirectory(url) {
    return localfsPromise.then(function(fs) {
      return fcall(fs.root, 'getDirectory', url, {
          create: true
        })
        .then(function(fileEntry) {
          return fileEntry;
        }, function(error) {
          console.error('createDirectory error: [' + url + '] ' + error.code);
          throw {
            url: url,
            code: error.code
          };
        });
    });
  }

  /**
   * 文件的拷贝
   * @param  {String} srcURL           被拷贝的文件的路径
   * @param  {String} destDirectoryURL 文件拷贝的目的路径
   * @param  {String} newName          拷贝后的文件的新名称[可选]
   * @return {Entrty}                  文件对象
   */
  function copyFile(srcURL, destDirectoryURL, newName) {
    return $q.all([resolveURLtoFileEntry(srcURL), createDirectory(destDirectoryURL)])
      .then(function(args) {
        var fileEntry = args[0],
          directoryEntry = args[1];
        return fcall(fileEntry, 'copyTo', directoryEntry, newName);
      });
  }

  /**
   * 上传文件
   * @example
   *
   *  localfile.upload('url', {mimeType: 'image/jpeg'})
   *  .then(function(result) {
   *     console.log(result.responseCode);
   *     console.log(result.reponse);
   *     console.log(result.bytesSent);
   *  });
   *
   * 更多文件上传的说明请参考：https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md
   *
   * @param  {String} fileURL 文件本地路径
   * @param  {String} remoteUri 上传的远程路径（可选）
   * @param  {Object} options 配置
   * @param  {String} options.category 业务类型
   * @param  {String} options.recordId 业务id（可选）
   * @param  {String} options.mimeType 文件的mime类型
   * @return {Promise}
   */
  function upload(fileURL, remoteUri, options) {
    if (typeof remoteUri === 'object') {
      options = remoteUri;
      remoteUri = null;
    }
    remoteUri = remoteUri || CoreAppConfig.serverUrl + '/file';
    options = options || {};
    options.fileKey = options.fileKey || 'files';
    options.fileName = options.fileName || fileURL.substr(fileURL.lastIndexOf('/') + 1);
    options.mimeType = options.mimeType || 'text/plain';

    options.params = options.params || {};

    if (options.category) {
      options.params.category = options.category;
    }

    if (options.recordId) {
      options.params.recordId = options.recordId;
    }

    var ft = new FileTransfer(),
      deferred = $q.defer();

    ft.upload(fileURL, encodeURI(remoteUri), deferred.resolve, deferred.reject, options);
    return deferred.promise;
  }

  /**
   * 指定相对路径上传文件
   *
   * @param  {String} path 文件的相对路径
   * @param  {String} remoteUri
   * @param  {Object} options
   * @return {Promise}
   */
  function uploadByPath(path, remoteUri, options) {
    return toURL(path)
      .then(function(fileURL) {
        return upload(fileURL, remoteUri, options);
      });
  }

  var identitiyRegexp = /^[a-zA-Z0-9]+$/;
  /**
   * 下载文件
   *
   * @param  {String} source 远程文件路径，或者文件id
   * @param  {String} target 本地文件路径
   * @return {Promise}
   */
  function download(source, target) {
    if (identitiyRegexp.test(source)) {
      source = CoreAppConfig.serverUrl + '/file/' + source;
    } else {
      source = CoreAppConfig.serverUrl + source;
    }
    var down = function(fileURL) {
      var ft = new FileTransfer(),
        deferred = $q.defer();
      ft.download(encodeURI(source), fileURL, deferred.resolve, deferred.reject, true);

      return deferred.promise;
    };

    if (!target) {
      return toURL(guid())
        .then(down);
    } else {
      return down(target);
    }
  }

  /**
   * 下载文件
   * @param  {String} url 文件路径
   * @param  {String} name 文件名
   */
  function downFile(url, name) {
    var originUrl = url;
    if (url.indexOf('http') === 0) {
      url = url.replace(CoreAppConfig.serverUrl, '/');
    } else {
      originUrl = CoreAppConfig.serverUrl + originUrl;
    }

    if (window.cordova && window.device) {
      if(window.device.platform === 'Android') {
        return downFileOnAndroid(url, name);
      } else if (window.device.platform === 'iOS') {
        return downFileOnIOS(url, name);
      } else {
        return $q.when(new Error('无法在' + window.device.platform + '手机平台上下载文件'));
      }
    } else {//在浏览器上下载文件
      window.open(originUrl, '_system', 'location=no,closebuttoncaption=Close,enableViewportScale=yes');
      return $q.when(true);
    }
  }

  function downFileOnAndroid(url, name) {
    var root = cordova.file.externalCacheDirectory;
    var target = root + name;

    var getTargetPromise = downloadPromise[target];

    if (!getTargetPromise) {//已经验证了文件是否存在，并且防止了短时间内多次点击
      getTargetPromise = exist(target).then(function(stat) {
        if (!stat) {
          return download(url, target);
        } else {
          return resolveURLtoFileEntry(target);
        }
      }).then(function() {
        return $q.when(true);
      }, function() {
        return $q.when(false);
      }).finally(function() {
        delete downloadPromise[target];
      });
    }
  }

  var downloadPromise = {};

  function downFileOnIOS(url, name) {
    var cacheId = '$$sino.viewfiles.' + url;

    var getTargetPromise;

    if (window.localStorage[cacheId]) {
      getTargetPromise = $q.when(window.localStorage[cacheId]);
    } else {
      getTargetPromise = toURL(name).then(function(target) {
        if (!downloadPromise[target]) {
          downloadPromise[target] = download(url, target);
          downloadPromise[target].finally(function() {
            delete downloadPromise[target];
          });
        }

        return downloadPromise[target];
      }).then(function(fileEntry) {
        window.localStorage[cacheId] = fileEntry.toURL();
        return fileEntry.toURL();
      },function() {
        return $q.when(false);
      });
    }
  }

  /**
   * 查看文件
   *
   * @param  {String} url 文件路径
   */
  function viewFile(url, name) {
    var originUrl = url;
    if (url.indexOf('http') === 0) {
      url = url.replace(CoreAppConfig.serverUrl, '/');
    } else {
      originUrl = CoreAppConfig.serverUrl + originUrl;
    }

    if (window.cordova && window.device) {
      if (window.device.platform === 'Android') {
        return viewFileOnAndroid(url, name);
      } else if (window.device.platform === 'iOS') {
        return viewFileOnIOS(url, name);
      } else {
        return $q.when(new Error('无法在' + window.device.platform + '手机平台上查看文件'));
      }
    } else { //在浏览器上查看文件
      window.open(originUrl, '_system', 'location=no,closebuttoncaption=Close,enableViewportScale=yes');
      return $q.when(true);
    }
  }


  function viewFileOnAndroid(url, name) {
    if (name && name.indexOf('.') >= 0) {
      name = name.substring(0, name.lastIndexOf('.')) + name.substring(name.lastIndexOf('.')).toLowerCase();
    }
    window.SinoNetwork.viewFile(url, name);
  }

  function viewFileOnIOS(url, name) {
    if (name && name.indexOf('.') >= 0) {
      name = name.substring(0, name.lastIndexOf('.')) + name.substring(name.lastIndexOf('.')).toLowerCase();
    }
    window.SinoNetwork.viewFile(url, name);
  }

  function viewAffix(affixId, title) {
    var url = CoreAppConfig.serverUrl + '/affix/' + affixId + '/content';
    return viewFile(url, title);
  }

  /**
   * 清空已下载的文件和缓存
   */
  function clearFile(url, name) {

    if (window.cordova && window.device) {
      if (window.device.platform === 'Android') {
        return clearLocalFileOnAndroid(url, name);
      } else if (window.device.platform === 'iOS') {
        clearCache();
        return clearLocalFileOnIOS(url, name);
      } else {
        return $q.when(false);
      }
    } else { //在浏览器上不会清除文件
      return $q.when(true);
    }
  }

  /**
   * android上删除缓存的本地文件
   */
  function clearLocalFileOnAndroid(url, name) {
    var root = cordova.file.externalCacheDirectory;
    var target = root + name;

    var defer = $q.defer();
    resolveURLtoFileEntry(target).then(function(fileEntry) {
      return fcall(fileEntry, 'remove');
    }).then(function() {
      defer.resolve(true);
    }, function() {
      defer.resolve(false);
    });
    return defer.promise;
  }
  /**
   * 清除ISO的缓存
   */
  function clearCache (url) {
    var cacheId = '$$sino.viewfiles.' + url;
    if (window.localStorage[cacheId]) {
      window.localStorage.setItem(cacheId, '');
      return true;
    }
  }
  /**
   * ISO上删除缓存的本地文件
   */
  function clearLocalFileOnIOS(url, name) {
    var extname = name.substring(name.lastIndexOf('.'));
    name = guid() + extname;


    return toURL(name).then(function(target) {
      resolveURLtoFileEntry(target).then(function(fileEntry) {
        return fcall(fileEntry, 'remove');
      }, function() {
        return $q.when(false);
      });
    });

  }

  /**
   * 生成guid
   *
   * @return {String}
   */
  function guid() {
    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
      s4() + '-' + s4() + s4() + s4();
  }

  var fileService = {
    toURL: toURL,
    getFile: getFile,
    createFile: createFile,
    writeText: writeText,
    readAsText: readFileAsText,
    write: write,
    writeArrayBuffer: writeArrayBuffer,
    upload: upload,
    uploadByPath: uploadByPath,
    download: download,
    guid: guid,
    fs: localfsPromise,
    fcall: fcall,
    resolveURLtoFileEntry: resolveURLtoFileEntry,
    copyFile: copyFile,
    deleteCacheFile: deleteCacheFile,
    viewAffix: viewAffix,
    viewFile: viewFile,
    clearFile: clearFile,
    downFile: downFile
  };

  return fileService;
});

module.factory('ImageFileService', function(fileService) {
  return {
    download: function(id, size) {
      var surffix = '.jpg';
      size = size || '150x150';
      return fileService.toURL(id + surffix)
        .then(function(target) {
          return fileService.download('/image/' + id + '_' + size, target);
        });
    },
    getImage: function(id) {
      var surffix = '.jpg';
      return fileService.getFile(id + surffix);
    }
  };
});

module.factory('AudioFileService', function(fileService, $window, $q) {
  return {

    createMedia: function(path) {
      path = path || fileService.guid() + ($window.device.platform === 'Android' ? '.amr' : '.wav');
      var deferred2 = $q.defer(),
        deferred = $q.defer(),
        media;

      media = new Media(path, function() {
        deferred.resolve(media);
      }, deferred.reject);
      media.promise = deferred.promise;
      media._fileURL = path;

      deferred2.resolve(media);

      return deferred2.promise;
    },

    record: function() {
      var promise = this.createMedia()
        .then(function(media) {
          media.startRecord();
          media.beginRecordDate = new Date();
          return media;
        });

      promise.stopRecord = function() {
        return promise.then(function(media) {
          media.stopRecord();
          media.play();
          media.release();
          media.endRecordDate = new Date();
          media.duration = media.endRecordDate - media.beginRecordDate;
          return media;
        });
      };

      return promise;
    },

    upload: function(media) {
      var url = media._fileURL || media,
        mimeType = url.indexOf('.amr') >= 0 ? 'audio/amr' : 'audio/wav',
        uploadUrl = $window.device.platform === 'Android' ? '/storage/emulated/0/' + url : cordova.file.tempDirectory + url;

      return fileService.upload(uploadUrl, {
        category: 'mission-audio',
        mimeType: mimeType
      });
    },

    getAudio: function(id) {
      var surffix = $window.device.platform === 'Android' ? '.amr' : '.wav';
      return fileService.getFile(id + surffix);
    },

    download: function(id) {
      var surffix = $window.device.platform === 'Android' ? '.amr' : '.wav';
      return fileService.toURL(id + surffix)
        .then(function(target) {
          return fileService.download('/audio/' + id + '?format=mp3', target);
        });
    },

    play: function(id) {
      var surffix = $window.device.platform === 'Android' ? '.amr' : '.wav';
      return fileService.toURL(id + surffix)
        .then(function(target) {
          return fileService.download('/audio/' + id + '?format=mp3', target);
        })
        .then(function(fileEntry) {
          var media, successFn, deferred = $q.defer();
          successFn = function() {
            media.release();
            deferred.resolve(media);
          };
          media = new Media(fileEntry.toInternalURL(), successFn, deferred.reject);

          media.play();
          return deferred.promise;
        });
    }
  };
});
