import 'dart:convert';
import 'package:chat/data/UserData.dart';
import 'package:chat/generated/i18n.dart';
import 'package:chat/models/ChatMsg.dart';
import 'package:chat/models/group_info_model.dart';
import 'package:chat/models/group_info_server_model.dart';
import 'package:chat/proto/all.pbserver.dart';
import 'package:chat/data/constants.dart';
import 'package:chat/utils/file_cache_mgr.dart';
import 'package:convert/convert.dart';
import 'package:dio/dio.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:http_parser/http_parser.dart';
import 'package:chat/utils/MessageMgr.dart';
import 'package:oktoast/oktoast.dart';
import 'package:chat/utils/msgHandler.dart';
import 'package:chat/utils/sql_util.dart';

var signKey = 'O@MlSxWaeAlc5CYu';

class UploadUtil {
  // 工厂模式
  factory UploadUtil() => _getInstance();
  static UploadUtil get instance => _getInstance();
  static UploadUtil _instance;
  UploadUtil._internal() {
    // 初始化
  }

  static UploadUtil _getInstance() {
    if (_instance == null) {
      _instance = new UploadUtil._internal();
    }
    return _instance;
  }

  Dio dio;

  //进度流
  Map<String, StreamController<double>> streamMap = {};

  //缓存进度，方便快速显示
  Map<String, double> streamLastPercentMap = {};

  //上传地址
  String uploadUrl;
  String rootUrl;
  void setUploadUrl(url) {
    rootUrl = 'http://' + url;
    uploadUrl = 'http://' + url + '/chatresfile/';
    BaseOptions options = BaseOptions(
      baseUrl: uploadUrl,
      headers: {},
    );

    print(uploadUrl);
    dio = new Dio(options);
  }

// md5 加密
  String generateMd5(String data) {
    var content = new Utf8Encoder().convert(data);
    var digest = md5.convert(content);
    // 这里其实就是 digest.toString()
    return hex.encode(digest.bytes);
  }

  String getSign(Map parameter) {
    String k = '';
    var keyList = [];
    for (var key in parameter.keys) {
      keyList.add(key);
    }
    keyList.sort();
    for (var key in keyList) {
      k += '$key=${parameter[key]}&';
    }
    k += 'key=O@MlSxWaeAlc5CYu';
    return generateMd5(k);
  }

  String getFileType(String suffix) {
    if (suffix == 'png' || suffix == 'jpg' || suffix == 'jpeg') {
      return 'image';
    } else if (suffix == 'wav') {
      return 'audio';
    } else {
      return 'video';
    }
  }

  bool isImage(String suffix) {
    suffix = suffix.toLowerCase();
    return suffix == 'png' || suffix == 'jpg' || suffix == 'jpeg';
  }

  StreamController<double> getStream(String fileId) {
    if (streamMap[fileId] == null) {
      streamMap[fileId] = StreamController<double>.broadcast();
    }
    return streamMap[fileId];
  }

  Future uploadFile(MsgModel msg, {CancelToken cancelToken}) async {
    Response response;

    var data = {
      "upuserid": msg.from,
      'channeltype': msg.channelType,
      "targetid": msg.sessionId,
      'fileid': msg.extraFile,
    };

    print('上传参数$data');
    var path = msg.localFile;
    var name = path.substring(path.lastIndexOf("/") + 1, path.length);
    String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);

    var fileType = getFileType(suffix);
    print('文件类型 : $fileType  $suffix');

    data['sign'] = getSign(data);
    data['uploadfile'] = await MultipartFile.fromFile(path,
        filename: name, contentType: MediaType(fileType, suffix));

    FormData formData = new FormData.fromMap(data);

    try {
      print('开始上传文件${msg.extraFile}  url:${uploadUrl + 'uploadchatfile'}');

      var streamController = getStream(msg.extraFile);

      response = await dio.post(uploadUrl + 'uploadchatfile', data: formData,
          onSendProgress: (int progress, int total) {
        streamController.sink.add(progress / total);
        streamLastPercentMap[msg.extraFile] = progress / total;
      }, cancelToken: cancelToken);

      streamController.close();
      streamMap.remove(msg.extraFile);
      print('uploadFile response  $response');
      print('###msgContent  ${msg.msgContent.length}');

      if (response.data['code'] == 0) {
        var data = response.data['data'];
        print('uploadFile 上传成功');
        msg.state = MsgState.Uploaded;
        MsgHandler.sendChatMsg(msg);
        MessageMgr().emit('Update LastMsg', msg.sessionId);
        return data;
      } else {
        print('uploadFile 上传失败');
        msg.state = MsgState.UploadFailed;
        MessageMgr().emit('Update LastMsg', msg.sessionId);
        return null;
      }
    } on DioError catch (e) {
      if (CancelToken.isCancel(e)) {
        print('post请求取消! ' + e.message);
      }
      showToast('上传文件失败');
      msg.state = MsgState.UploadFailed;
      print('post请求发生错误：$e');
    }

    MessageMgr().emit('Update LastMsg', msg.sessionId);
    return null;
  }

  void cancelRequests(CancelToken cancelToken) {
    print('取消请求$cancelToken');
    cancelToken.cancel("cancelled");
  }

  String getFullUrl(String imgData, int sessionid, int channelType) {
    var data = {
      "fileid": imgData,
      'targetid': sessionid,
      "userid": UserData().basicInfo.userId,
      'channeltype': channelType
    };

    var sign = getSign(data);

    return uploadUrl +
        'downloadchatfile?targetid=$sessionid&channeltype=$channelType&userid=${UserData().basicInfo.userId}&fileid=$imgData&sign=$sign';
  }

  copyFileUrl(MsgModel msg, BuildContext context) {
    var url = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
    ClipboardData clipboardData = new ClipboardData(text: url);
    Clipboard.setData(clipboardData);
    showToast(I18n.of(context).successful_copy);
  }

  Future downloadFile(MsgModel msg, {CancelToken cancelToken}) async {
    Response response;



    var fullUrl = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
    if(msg.extraFile.contains('http://')){
      fullUrl = msg.extraFile;
    }

    print('下载文件$fullUrl');

    var path = await FileCacheMgr().genFilePath(msg.extraFile);
    if (msg.msgType == ChatType.ShortVoiceChatType.value) {
      path = path + '.wav';
    } else if (msg.msgType == ChatType.FileChatType.value) {
      var fileMsg = FileChat.fromBuffer(msg.msgContent);
      var fileName = fileMsg.name;
      path = await FileCacheMgr().genFilePath(fileName);
    }

    print('文件保存路径$path');

    try {
      var streamController = getStream(msg.extraFile);

      response = await dio.download(fullUrl, path,
          onReceiveProgress: (int progress, int total) {
        streamController.sink.add(progress / total);
        streamLastPercentMap[msg.extraFile] = progress / total;
      }, cancelToken: cancelToken);

      streamController.close();
      streamMap.remove(msg.extraFile);
      if (response.statusCode == 200) {
        msg.state = MsgState.DownloadSuccess;
        msg.localFile = path;
        SqlUtil().updateLocalFile(msg.extraFile, path,
            isGroup: msg.channelType == ChatChannelType.Group.value);

        return path;
      } else {
        msg.state = MsgState.DownloadFailed;
        showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
        return null;
      }
    } catch (e) {
      msg.state = MsgState.DownloadFailed;
      showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
      print(response.toString());
    }
  }

  //机器人翻译，type 1文字，2语音
  Future<String> commitTranslateSource(
      int type, int sourceLang, int toLang, dynamic content) async {
    Response response;

    var myId = UserData().basicInfo.userId;

    Map<String, dynamic> data = {
      'userid': myId,
      'cType': type,
      'ulanguage': sourceLang,
      'tlanguage': toLang
    };

    data['sign'] = getSign(data);
    data['content'] = content;

    if (type == 2) {
      data['content'] = await MultipartFile.fromFile(content);
    }

    FormData formData = new FormData.fromMap(data);

    try {
      print('开始请求翻译$data ${uploadUrl + 'translate/transqt'}');
      response = await dio.post(rootUrl + '/translate/transqt', data: formData);
      Map resData = response.data;
      print('翻译结果$resData');
      if (resData['code'] == 0) {
        return resData['data'];
      }
    } catch (e) {
      print(e);
    }

    return null;
  }

  //获取群列表
  Future<List<GroupInfoModel>> getGroupList() async {
    Response response;

    var myId = UserData().basicInfo.userId;

    Map<String, dynamic> data = {
      'userid': myId,
    };
    data['sign'] = getSign(data);

    try {
      print('开始请求群列表$data}');
      response = await dio.post(rootUrl + '/chat/queryusergroups', data: data);
      Map resData = response.data;

      if (resData['code'] == 0) {
        List resList = resData['data'];
        List<GroupInfoModel> groupList = [];
        for (var i = 0; i < resList.length; i++) {
          var info = GroupInfoServerModel.fromJson(resList[i]);

          groupList.add(GroupInfoModel.fromServerGroupInfo(info));
        }

        return groupList;
      }
    } catch (e) {
      print(e);
    }

    return null;
  }

  //获取多个群信息
  Future<List<GroupInfoModel>> getMoreGroupInfo(List<int> groupIds) async {
    Response response;

    var myId = UserData().basicInfo.userId;

    Map<String, dynamic> data = {
      'userid': myId,
    };
    data['sign'] = getSign(data);
    data['groupids'] = groupIds;

    try {
      print('开始请求 getMoreGroupInfo $data');
      response = await dio.post(rootUrl + '/chat/querygroups', data: data);
      Map resData = response.data;

      if (resData['code'] == 0) {
        List data = resData['data'];
        print('群信息$data');
        if (data != null && data.length > 0) {
          List<GroupInfoModel> res = [];
          for (var each in data) {
            var info = GroupInfoServerModel.fromJson(each);
            res.add(GroupInfoModel.fromServerGroupInfo(info));
          }
          return res;
        }
      }
    } catch (e) {
      print(e);
    }

    return null;
  }

  //获取群信息
  Future<GroupInfoModel> getGroupInfo(int groupId) async {
    Response response;

    var myId = UserData().basicInfo.userId;

    Map<String, dynamic> data = {
      'userid': myId,
    };
    data['sign'] = getSign(data);
    data['groupids'] = [groupId];

    try {
      print('开始请求群信息$data');
      response = await dio.post(rootUrl + '/chat/querygroups', data: data);
      Map resData = response.data;

      if (resData['code'] == 0) {
        List data = resData['data'];
        print('群信息$data');
        if (data != null && data.length > 0) {
          var info = GroupInfoServerModel.fromJson(data.first);
          return GroupInfoModel.fromServerGroupInfo(info);
        }
      }
    } catch (e) {
      print(e);
    }

    return null;
  }

  //获取群校验信息
  Future<Map> getGroupCheckInfo() async {
    Response response;

    var myId = UserData().basicInfo.userId;

    Map<String, dynamic> data = {
      'userid': myId,
    };
    data['sign'] = getSign(data);

    try {
      print('请求获取群校验信息$data');
      response = await dio.post(rootUrl + '/chat/checkgroup', data: data);
      Map resData = response.data;

      if (resData['code'] == 0) {
        print('群校验信息${resData['data']}');
        return resData['data'];
      }
    } catch (e) {
      print(e);
    }

    return null;
  }
}
