import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';

import 'package:buffer/buffer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sport/db/step_db.dart';
import 'package:sport/services/api/inject_api.dart';
import 'package:sport/utils/toast.dart';

class Bluetooth with ChangeNotifier, InjectApi {
  static final String PREF_KEY = "SHOES";

  factory Bluetooth() => _getInstance();

  static Bluetooth _instance;

  // 获取对象
  static Bluetooth _getInstance() {
    if (_instance == null) {
      // 使用私有的构造方法来创建对象
      _instance = Bluetooth._internal();
    }
    return _instance;
  }

  Bluetooth._internal() {
    //初始化(设置一些默认的)...
  }

  FlutterBlue flutterBlue = FlutterBlue.instance;

  StreamSubscription listenSubscription, periodicSubscription, characteristicSubscription, deviceSubscription, connectedDevicesSubscription;
  String _mac;
  BluetoothDevice _device;
  BluetoothCharacteristic _writeCharacteristic;
  bool _founded = false;

  // ignore: close_sinks
  final StreamController<bool> _queryController = StreamController.broadcast();

  Stream<bool> get queryStream => _queryController.stream;

  // ignore: close_sinks
  final StreamController<bool> _stateController = StreamController.broadcast();

  Stream<bool> get stateStream => _stateController.stream;

  BluetoothDevice get device => _device;
  bool _online = false;
  final ValueNotifier<Map> infoNotifier = ValueNotifier<Map>({});
  final ValueNotifier<Map> dataNotifier = ValueNotifier<Map>({});
  final ValueNotifier<int> electricityNotifier = ValueNotifier<int>(0);
  final ValueNotifier<int> stepNotifier = ValueNotifier<int>(0);
  final ValueNotifier<int> stepTotalNotifier = ValueNotifier<int>(0);
  final ValueNotifier<int> actionNotifier = ValueNotifier<int>(0);
  final ValueNotifier<List<int>> byteNotifier = ValueNotifier<List<int>>([]);
  final ValueNotifier<int> vibrateNotifier = ValueNotifier<int>(400);
  final ValueNotifier<int> stepRealtimeNotifier = ValueNotifier<int>(0);
  final ValueNotifier<bool> stepRealtimeSwitchNotifier = ValueNotifier<bool>(false);

  PartInfo _partInfo;

  bool get isConnected => _online;

  resetData() {
    stepNotifier.value = 0;
    stepTotalNotifier.value = 0;
    actionNotifier.value = 0;
  }

  set device(BluetoothDevice device) {
    _device = device;
    notifyListeners();
  }

  Future<String> saveDevice(BluetoothDevice device) async {
    var id = device.id.toString();
    var prefs = await SharedPreferences.getInstance();
    prefs.setString(PREF_KEY, id);
    _mac = id;
    await connect(device);
    return id;
  }

  Future<String> getHistoryDevice() async {
    var prefs = await SharedPreferences.getInstance();
    return _mac = prefs.getString(PREF_KEY);
  }

  Future listen() async {
    var prefs = await SharedPreferences.getInstance();
    if (!prefs.containsKey("token")) {
      return;
    }
    FlutterBlue flutterBlue = FlutterBlue.instance;
    if (listenSubscription != null) return;
    print("bluetooth -- listen");
    listenSubscription = flutterBlue.state.listen((state) async {
      print("bluetooth -- state $state");
      if (state == BluetoothState.on) {
        // connectedDevicesSubscription = Stream.periodic(Duration(seconds: 10)).asyncMap((_) => FlutterBlue.instance.connectedDevices).listen((event) {
        //   BluetoothDevice d = event.firstWhere((element) => element.id.toString() == _mac, orElse: () => null);
        //   if (d != null) {
        //     device = d;
        //   }
        // });

        await connectDevice();
      } else {
        connectedDevicesSubscription?.cancel();
      }
    });
  }

  void timer() {
    // 定时读取设备信息
    periodicSubscription = Stream.periodic(Duration(seconds: 60)).listen((event) async {
      if (device == null) return;
      if (!_online) return;

      await queryDeviceData();
      await queryDeviceStep();
    });
  }

  void disposeDevice() {
    deviceSubscription?.cancel();
    characteristicSubscription?.cancel();
    _online = false;
    device?.disconnect();
    print("bluetooth -- disconnect $device");
    _writeCharacteristic = null;
  }

  Future disposeBluetooth() async {

    // 实时计步中不主动断开
    if(stepRealtimeSwitchNotifier.value == true)
      return;

    connectedDevicesSubscription?.cancel();
    periodicSubscription?.cancel();
    listenSubscription?.cancel();
    listenSubscription = null;
    stepRealtimeSwitchNotifier?.value = false;
    disposeDevice();
    FlutterBlue.instance?.stopScan();
    _founded = false;
  }

  Future disconnectDevice() async {
    device?.disconnect();
    device = null;
  }

  Future connectDevice() async {
    var mac = await getHistoryDevice();
    print("bluetooth -- connect $mac $device");
    if (mac?.isEmpty == true) {
      return;
    }
    if (device != null) {
      connect(device);
      return;
    }
    // Start scanning
    flutterBlue.startScan(timeout: Duration(seconds: 10));
    flutterBlue.scanResults.listen((results) {
      for (ScanResult r in results) {
        if (r.device.id.toString() == mac) {
          if (_founded) return;
          _founded = true;
          flutterBlue.stopScan();
          connect(r.device);
          print('${r.device.name} found! connect: ${r.rssi} ${r.device.id}');
          break;
        }
        print('${r.device.name} found! rssi: ${r.rssi}');
      }
    });
  }

  Future connect(BluetoothDevice newDevice) async {
    if (device != null) {
      if (!(device == newDevice)) {
        _online = false;
        disposeDevice();
      }
    }
    device = newDevice;
    deviceSubscription?.cancel();

    deviceSubscription = device.state.listen((event) async {
      print("device: $_mac --> state $event");
      if (event == BluetoothDeviceState.disconnected) {
        if (_online == true) {
          ToastUtil.show("${device?.name} 已断开");
        }
        _online = false;
        characteristicSubscription?.cancel();
        _writeCharacteristic = null;
        periodicSubscription?.cancel();
      } else if (event == BluetoothDeviceState.connected) {
        _online = true;
        flutterBlue.stopScan();
        ToastUtil.show("${device?.name} 已连接");
        _stateController.add(true);

        await discoverServices(device);
        await Future.delayed(Duration(seconds: 2));
        await queryDeviceInfo();
        await Future.delayed(Duration(seconds: 1));
        await queryDeviceData();
        await Future.delayed(Duration(seconds: 1));
        await queryDeviceStep();
        timer();
      }
    });

    device.connect(autoConnect: false);
    print("device: $_mac --> connect $device");
  }

  Future discoverServices(BluetoothDevice device) async {
    characteristicSubscription?.cancel();

    var services = await device.discoverServices();
    for (var service in services) {
      print('${device.name} found! service: $service');
      for (var characteristic in service.characteristics) {
        print('${device.name} found! characteristic: $characteristic ${characteristic.uuid.toString()}');
        if (characteristic.properties.write && characteristic.properties.writeWithoutResponse) {
          _writeCharacteristic = characteristic;
        }
        if (characteristic.properties.notify) {
          characteristic.setNotifyValue(true).then((succ) {
            print("characteristic notify --> $succ");
            if (succ) {
              characteristicSubscription = characteristic.value.listen((event) {
                print("characteristic change --> $event");
                byteNotifier.value = event;
                parse(event);
              });
            }
          });
        }
      }
    }
  }

  void parse(List<int> event) {
    if (event?.isEmpty == true) return;
    ByteDataReader reader = ByteDataReader();
    reader.add(event);

    int flagAA = reader.readUint8();
    // int flagBB = reader.readUint8();
    // int flagCC = reader.readUint8();

    int len = reader.readUint8();
    int len1 = 0xFF - reader.readUint8();

    // int index = reader.readUint8();
    int cmd = reader.readUint8();

    print("                         change --> ${flagAA} ${len} ${~len} ${cmd}");
    // print("                         change --> ${len} ${~len} ${len1}");
    // print("                         change --> ${index} ${cmd}");

    if (!(flagAA == 0xAA)) return;
    if (len != len1) return;

    parseCmd(cmd, reader.read(event.length - 5));
  }

  void parseCmd(int cmd, Uint8List byteArray) {
    print("                        cmd $cmd data --> $byteArray");
    switch (cmd) {
      case 0x01:
        _parseAction(byteArray);
        break;
      case 0xA0:
        break;

      case 0xA1:
        _parseQuery(byteArray);
        break;

      case 0xA5:
        _parseStepRealtime(byteArray);
        break;
    }
  }

  void _parseAction(Uint8List byteArray) {
    ByteDataReader reader = ByteDataReader();
    reader.add(byteArray);
    int action = reader.readUint8();
    int t = reader.readUint16();
    print("action: $action ");
    actionNotifier.value = action;
  }

  void _parseQuery(Uint8List byteArray) {
    ByteDataReader reader = ByteDataReader();
    reader.add(byteArray);
    int cmd = reader.readUint8();
    switch (cmd) {
      case 0x00: // 设备基本信息
        Map<String, dynamic> info = {};
        List<int> name = [];
        for (var i = 0; i < 64; i++) {
          name.add(reader.readUint8());
        }
        info['name'] = String.fromCharCodes(name);
        info['softwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
        info['hardwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
        infoNotifier.value = info;
        break;
      case 0x01: // 设备数据(左鞋,右鞋)
        Map<String, dynamic> info = {};
        for (var i = 0; i < 2; i++) {
          info['${i}_electricity'] = reader.readUint8();
          info['${i}_endurance'] = reader.readUint8();
          info['${i}_temperature'] = reader.readUint8();
          info['${i}_pressure'] = reader.readUint(4);
        }
        // [170, 187, 204, 23, 232, 0, 161, 1, 50, 0, 33, 0, 0, 156, 41, 51, 0, 34, 0, 0, 150, 19, 232]
        dataNotifier.value = info;
        electricityNotifier.value = min(info['0_electricity'] ?? 0, info['1_electricity'] ?? 0);

        // _testElectricity(info);

        break;
      case 0x02: // 查询步数
        if (reader.remainingLength == 1) {
          print("step end ... ");
          print(_partInfo?.items);
          _partInfo = null;
          return;
        }
        int serial = reader.readUint8();
        if (serial == 0) {
          int start = reader.readUint64();
          int serialCount = reader.readUint8();
          print("step start ... $start $serial $serialCount");
          if (serialCount > 0) {
            if (_partInfo == null) _partInfo = new PartInfo(start ~/ 1000, serialCount);
          } else {
            _partInfo = null;
          }
        } else {
          if (_partInfo != null) {
            if (_partInfo.serial < serial) {
              _partInfo.serial = serial;
              while (reader.remainingLength > 0) {
                int step = reader.readUint16();
                int distance = reader.readUint16();
                _partInfo.add(step, distance);
                print("step read $serial --> ${reader.offsetInBytes} ... ${reader.remainingLength}   = info: $step $distance");
              }

              if (serial > 0 && _partInfo.serialCount == serial) {
                print("step end ... ");
                _saveInfo(_partInfo);
                stepTotalNotifier.value = _partInfo.items.map((e) => e.step).reduce((value, element) => value + element);
                _partInfo = null;
              }
            }
          }

          ByteDataWriter writer = ByteDataWriter();
          writer.writeUint8(0x02);
          writer.writeUint8(serial);
          write(0xA1, writer.toBytes());
        }
        break;
      case 0x03: // 步数回调
        int step = reader.readUint8();
        stepNotifier.value = stepNotifier.value + step;
        break;
    }
  }

  void _parseStepRealtime(Uint8List byteArray) {
    ByteDataReader reader = ByteDataReader();
    reader.add(byteArray);
    int cmd = reader.readUint8();
    if (cmd == 0) {
      stepRealtimeSwitchNotifier.value = false;
    } else if (cmd == 1) {
      stepRealtimeSwitchNotifier.value = true;
    } else {
      stepRealtimeNotifier.value = reader.readUint16();
    }
  }

  Future _saveInfo(PartInfo partInfo) async {
    if (partInfo?.items?.isEmpty == true) return;

    List<PartItem> _items = [];
    for (var item in partInfo.items) {
      if (item.step == 0 && item.distance == 0) continue;
      _items.add(item);
    }
    await StepDB().insertAll(_items);
    await uploadInfo();
    _queryController.add(true);
  }

  Future uploadInfo() async {
    for (var i = 0; i < 100; i++) {
      var list = await StepDB().find();
      if (list.isEmpty) break;

      List<List<int>> data = List();
      int last = 0;
      int step = 0;
      for (var item in list) {
        last = item['time'];
        data.add([item['st'], item['di'], item['time']]);
        step += item['st'];
      }
      if (step > 5) {
        await api.addDaily(data: json.encode(data));
        await StepDB().delete(last);
      }
    }

    // Directory appDocDir = await getApplicationDocumentsDirectory();
    // String appDocPath = appDocDir.path;
    // Directory saveDir = Directory("$appDocPath/step");
    // var files = saveDir.listSync();
    // for(var file in files){
    //   var f= new File(file.path);
    //   var content = f.readAsStringSync();
    //   await api.addDaily(data: content);
    //   file.delete();
    // }
  }

  Future syncDeviceInfo() async {
    DateTime d = DateTime.now();
    int now = d.millisecondsSinceEpoch;
    ByteDataWriter writer = ByteDataWriter();
    writer.writeUint8(0x00);
    writer.writeUint64(now);
    await write(0xA0, writer.toBytes());
  }

  // 查询 设备基本信息
  Future queryDeviceInfo() async {
    await write(0xA1, Uint8List.fromList([0x00]));
  }

  // 查询 设备数据
  Future queryDeviceData() async {
    await write(0xA1, Uint8List.fromList([0x01]));
  }

  // 查询 查询步数
  Future queryDeviceStep() async {
    // await Future.delayed(Duration(seconds: 3));
    // _queryController.add(true);
    if (_partInfo != null) {
      if ((_partInfo.now?.difference(DateTime.now())?.inSeconds ?? 0) < 60) {
        return;
      }
    }

    await write(0xA1, createTime(0x02));
  }

  Future setupGameMode(bool mode) async {
    await write(0xA2, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00]));
  }

  Future vibrate(int vibrate) async {
    ByteDataWriter writer = ByteDataWriter();
    writer.writeUint16(vibrate.round());
    await write(0xA4, writer.toBytes());
  }

  Future stepRealTime(bool mode) async {
    stepRealtimeNotifier.value = 0;
    await write(0xA5, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00]));
  }

  Uint8List createTime(int cmd) {
    DateTime now = DateTime.now();
    DateTime offset = DateTime(now.year, now.month, now.day, now.hour);
    print("create now .. $now - $offset");
    ByteDataWriter writer = ByteDataWriter();
    writer.writeUint8(cmd);
    writer.writeUint8(0);
    writer.writeUint64(offset.millisecondsSinceEpoch);
    writer.writeUint8(max(0, min(60, now.minute - offset.minute)));
    return writer.toBytes();
  }

  Future write(int cmd, Uint8List data) async {
    BluetoothDevice _device = device;
    if (_device == null) return;
    if (_writeCharacteristic == null) {
      await discoverServices(_device);
    }
    if (_writeCharacteristic == null) return;

    int length = data.length + 5;
    ByteDataWriter writer = ByteDataWriter();
    writer.writeUint8(0xAA);
    // writer.writeUint8(0xBB);
    // writer.writeUint8(0xCC);
    writer.writeUint8(length);
    writer.writeUint8(0xFF - length);
    // writer.writeUint8(0x00);
    writer.writeUint8(cmd);
    if (data.isNotEmpty) writer.write(data);
    int ver = writer.toBytes().reduce((value, element) => value + element);
    writer.writeUint8(ver);
    Uint8List out = writer.toBytes();
    print("out $out");
    for(var i =0; i< 10;i++){
      try {
        await _writeCharacteristic?.write(out, withoutResponse: true);
        break;
      } catch (e) {
        print(e);
        await Future.delayed(Duration(milliseconds: 10));
      }
    }

  }

  void _testElectricity(Map<String, dynamic> info) async {
    var now = DateTime.now();
    Directory dir = await getExternalStorageDirectory();
    File file = File("${dir.path}/temp/${now.year}_${now.month}_${now.day}.cvs");
    print("log file: $file");
    if (!file.parent.existsSync()) {
      file.parent.createSync();
    }
    file.writeAsStringSync("${now.hour},${now.minute},${now.second},${info['0_electricity']},${info['1_electricity']}\n", mode: FileMode.append, flush: true);
  }
}

class PartItem {
  int step;
  int distance;
  int time;

  PartItem(this.step, this.distance, this.time);

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['step'] = this.step;
    data['distance'] = this.distance;
    data['time'] = this.time;
    return data;
  }
}

class PartInfo {
  int start;
  int serial = 0;
  int serialCount;
  List<PartItem> items = [];
  DateTime now;

  PartInfo(this.start, this.serialCount) {
    now = DateTime.now();
  }

  void add(int step, int distance) {
    items.add(PartItem(step, distance, start + items.length * 60 * 60));
  }
}
