import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';

/*
 * 文件下载
 * 懒加载单例
 */
class DownLoadManage {
  //用于记录正在下载的url，避免重复下载
//  var downloadingUrls = new List();
  var downloadingUrls = new Map<String, CancelToken>();

  // 单例公开访问点
  factory DownLoadManage() => _getInstance();

  // 静态私有成员，没有初始化
  static DownLoadManage _instance;

  // 私有构造函数
  DownLoadManage._() {
    // 具体初始化代码
  }

  Object _assureDioError(err) {
    if (err is DioError) {
      return err;
    } else {
      var _err = DioError(error: err);
      return _err;
    }
  }

  // 静态、同步、私有访问点
  static DownLoadManage _getInstance() {
    if (_instance == null) {
      _instance = DownLoadManage._();
    }
    return _instance;
  }

  /*
   *下载
   */
  Future download(url, savePath, {ProgressCallback onReceiveProgress, Function done, Function failed}) async {
    int downloadStart = 0;
    bool fileExists = false;
    File f = File(savePath);
    if (await f.exists()) {
      downloadStart = f.lengthSync();
      fileExists = true;
    }

    print("$url $fileExists ${downloadingUrls.containsKey(url) } ${downloadingUrls[url]?.isCancelled} ${downloadingUrls.length}");
    if (fileExists && downloadingUrls.containsKey(url) && !downloadingUrls[url].isCancelled) {
      print("$url $fileExists");
      //正在下载
      return;
    }

    CancelToken cancelToken = new CancelToken();
//   downloadingUrls[url] = cancelToken;
    if (downloadingUrls[url] == null) {
      downloadingUrls[url] = cancelToken;
    }

    var dio = Dio();
    String contentLength;
    int _total=0;
    try {
      contentLength = await _getContentLength(dio, url, cancelToken);
      print("contentLength :$contentLength");
      _total = int.parse(contentLength);
      print("start :$downloadStart -- contentLength: $contentLength");
      if (downloadStart >= _total) {
        //存在本地文件，命中缓存
        done();
        return;
      }
    } catch (e) {
      print(e);
      stop(url);
      return;
    }

    cancelToken = new CancelToken();
    downloadingUrls[url] = cancelToken;

    File tmp = File("$savePath.tmp$downloadStart");
    tmp.createSync();
    var resp = await dio
        .download(url, tmp.path,
            deleteOnError: false,
            onReceiveProgress: (index, total) => onReceiveProgress(index + downloadStart, _total),
            cancelToken: cancelToken,
            options: Options(
              followRedirects: false,
              headers: {"range": "bytes=$downloadStart-$contentLength"},
            ))
        .whenComplete(() {
      downloadingUrls.remove(url);
    }).catchError((e) async {
      failed.call(e);
    });
    await append(f, tmp);
    if (resp != null) {
      done.call();
    }
    // downloadingUrls.remove(url);
    // done.call();
  }

  append(File file, File tmp) async {
    IOSink ioSink = file.openWrite(mode: FileMode.writeOnlyAppend);
    print("save file ---------1111111111111 --- ${tmp.lengthSync()}");
    await ioSink.addStream(tmp.openRead());
    await tmp.delete(); //删除临时文件
    await ioSink.close();
  }

  /*
   * 获取下载的文件大小
   */
  Future _getContentLength(Dio dio, url, CancelToken cancelToken) async {
    try {
      Response response = await dio.head(url, cancelToken: cancelToken);
      return response.headers.value(Headers.contentLengthHeader);
    } catch (e) {
      print("_getContentLength Failed:" + e.toString());
      return 0;
    }
  }

  void stop(String url) {
    if (downloadingUrls.containsKey(url)) {
      try {
        downloadingUrls[url].cancel();
      } catch (e) {
        print(e);
        downloadingUrls.remove(url);
      }
    }
  }

  Future<T> _listenCancelForAsyncTask<T>(CancelToken cancelToken, Future<T> future) {
    if (cancelToken != null && cancelToken.cancelError == null) {
//      cancelToken.addCompleter(completer);
//      return Future.any([completer.future, future]).then<T>((result) {
//        cancelToken.removeCompleter(completer);
//        return result;
//      }).catchError((e) {
//        cancelToken.removeCompleter(completer);
//        throw e;
//      });
//    }
      return Future.any([cancelToken.whenCancel, future]).then((value) {
//        return value;
        cancelToken.cancel("下载取消");
        return value;
      }).catchError((e) {
        throw e;
      });
    } else {
      return future;
    }
  }
}
