// 自然写互动课堂平板端应用软件 V1.0
// service/ble_service.dart - BLE蓝牙点阵笔连接服务

import 'dart:async';
import 'dart:typed_data';

/// BLE服务UUID常量定义
/// 基于自然写点阵笔自定义GATT Service规范
class PadBleConstants {
  /// 点阵笔主服务UUID
  static const String penServiceUuid = '0000ffe0-0000-1000-8000-00805f9b34fb';

  /// 笔迹坐标数据特征值UUID（Notify）
  static const String strokeCharUuid = '0000ffe1-0000-1000-8000-00805f9b34fb';

  /// 笔控制指令特征值UUID（Write）
  static const String controlCharUuid = '0000ffe2-0000-1000-8000-00805f9b34fb';

  /// 电量信息特征值UUID（Read/Notify）
  static const String batteryCharUuid = '0000ffe3-0000-1000-8000-00805f9b34fb';

  /// 设备信息服务UUID
  static const String deviceInfoUuid = '0000180a-0000-1000-8000-00805f9b34fb';

  /// 扫描超时时间（秒）
  static const int scanTimeoutSeconds = 15;

  /// 自动重连延迟（秒）
  static const int reconnectDelaySeconds = 3;

  /// 最大重连次数
  static const int maxReconnectAttempts = 10;

  /// MTU协商大小
  static const int requestedMtu = 247;

  /// 笔迹数据缓冲批量回调阈值
  static const int strokeBatchSize = 8;

  /// 电量读取间隔（秒）
  static const int batteryReadInterval = 60;
}

/// 单个笔迹坐标点数据
class PadPenPoint {
  /// X坐标（0.01mm精度，16位无符号）
  final double x;

  /// Y坐标（0.01mm精度，16位无符号）
  final double y;

  /// 压力值（0-255，8位无符号）
  final int pressure;

  /// 时间戳（相对值，16位无符号，单位ms）
  final int timestamp;

  /// 是否为落笔点
  final bool isPenDown;

  PadPenPoint({
    required this.x,
    required this.y,
    required this.pressure,
    required this.timestamp,
    this.isPenDown = false,
  });

  @override
  String toString() =>
      'PadPenPoint(x: ${x.toStringAsFixed(2)}, y: ${y.toStringAsFixed(2)}, '
      'p: $pressure, t: $timestamp)';
}

/// 点阵笔设备信息
class PadPenDevice {
  /// 设备蓝牙MAC地址
  final String macAddress;

  /// 设备名称
  final String name;

  /// 信号强度（RSSI）
  int rssi;

  /// 当前连接状态
  PenConnectionState connectionState;

  /// 电量百分比（0-100）
  int batteryLevel;

  /// 固件版本号
  String? firmwareVersion;

  /// 当前所在点阵码页面ID
  String? currentPageId;

  PadPenDevice({
    required this.macAddress,
    required this.name,
    this.rssi = -100,
    this.connectionState = PenConnectionState.disconnected,
    this.batteryLevel = -1,
    this.firmwareVersion,
    this.currentPageId,
  });
}

/// 笔连接状态枚举
enum PenConnectionState {
  /// 未连接
  disconnected,

  /// 正在扫描
  scanning,

  /// 正在连接
  connecting,

  /// 已连接
  connected,

  /// 正在断开
  disconnecting,

  /// 自动重连中
  reconnecting,
}

/// 笔迹数据事件（批量坐标点回调）
class PenStrokeEvent {
  /// 来源笔的MAC地址
  final String penMac;

  /// 坐标点列表
  final List<PadPenPoint> points;

  /// 所在页面ID（点阵码识别）
  final String? pageId;

  PenStrokeEvent({
    required this.penMac,
    required this.points,
    this.pageId,
  });
}

/// BLE蓝牙点阵笔连接服务
/// 负责扫描、连接、数据接收、电量监控、自动重连等功能
/// 平板端支持同时连接1支笔（学生个人使用场景）
class PadBleService {
  /// 已发现的设备列表
  final List<PadPenDevice> _discoveredDevices = [];

  /// 当前已连接的笔
  PadPenDevice? _connectedPen;

  /// 笔迹数据缓冲区（累积到阈值后批量回调）
  final List<PadPenPoint> _strokeBuffer = [];

  /// 扫描结果流
  final StreamController<List<PadPenDevice>> _scanController =
      StreamController<List<PadPenDevice>>.broadcast();

  /// 笔迹数据事件流
  final StreamController<PenStrokeEvent> _strokeController =
      StreamController<PenStrokeEvent>.broadcast();

  /// 连接状态变化流
  final StreamController<PenConnectionState> _connectionController =
      StreamController<PenConnectionState>.broadcast();

  /// 电量变化流
  final StreamController<int> _batteryController =
      StreamController<int>.broadcast();

  /// 自动重连计数器
  int _reconnectAttempts = 0;

  /// 重连定时器
  Timer? _reconnectTimer;

  /// 电量读取定时器
  Timer? _batteryTimer;

  /// 是否正在扫描
  bool _isScanning = false;

  /// 公开的流
  Stream<List<PadPenDevice>> get scanStream => _scanController.stream;
  Stream<PenStrokeEvent> get strokeStream => _strokeController.stream;
  Stream<PenConnectionState> get connectionStream =>
      _connectionController.stream;
  Stream<int> get batteryStream => _batteryController.stream;

  /// 获取当前连接的笔
  PadPenDevice? get connectedPen => _connectedPen;

  /// 开始扫描附近的点阵笔设备
  /// 按服务UUID过滤，仅发现自然写点阵笔
  Future<void> startScan() async {
    if (_isScanning) return;
    _isScanning = true;
    _discoveredDevices.clear();

    // 通知扫描状态
    _connectionController.add(PenConnectionState.scanning);

    // 模拟BLE扫描（实际使用flutter_blue_plus库）
    // 过滤条件：仅发现包含pen_service_uuid的设备
    // scanFilters: [ScanFilter(serviceUuid: PadBleConstants.penServiceUuid)]

    // 设置扫描超时
    Timer(Duration(seconds: PadBleConstants.scanTimeoutSeconds), () {
      stopScan();
    });
  }

  /// 停止扫描
  Future<void> stopScan() async {
    _isScanning = false;
    // 实际调用: FlutterBluePlus.stopScan()
  }

  /// 处理扫描结果回调
  void _onScanResult(String mac, String name, int rssi) {
    // 检查是否已发现过
    final existingIndex = _discoveredDevices.indexWhere(
      (d) => d.macAddress == mac,
    );

    if (existingIndex >= 0) {
      // 更新已有设备的RSSI
      _discoveredDevices[existingIndex].rssi = rssi;
    } else {
      // 添加新发现的设备
      _discoveredDevices.add(PadPenDevice(
        macAddress: mac,
        name: name,
        rssi: rssi,
      ));
    }

    // 按信号强度降序排列
    _discoveredDevices.sort((a, b) => b.rssi.compareTo(a.rssi));
    _scanController.add(List.from(_discoveredDevices));
  }

  /// 连接指定的点阵笔
  /// [device] 要连接的笔设备信息
  Future<bool> connectPen(PadPenDevice device) async {
    // 先断开已有连接
    if (_connectedPen != null) {
      await disconnectPen();
    }

    device.connectionState = PenConnectionState.connecting;
    _connectionController.add(PenConnectionState.connecting);

    try {
      // 停止扫描
      await stopScan();

      // 执行BLE连接
      // 实际调用: device.connect(timeout: Duration(seconds: 10))
      // 协商MTU
      // await device.requestMtu(PadBleConstants.requestedMtu);

      // 发现服务和特征值
      // final services = await device.discoverServices();
      // 查找笔迹数据特征值并订阅Notify

      // 设置连接成功状态
      device.connectionState = PenConnectionState.connected;
      _connectedPen = device;
      _reconnectAttempts = 0;
      _connectionController.add(PenConnectionState.connected);

      // 启动电量定时读取
      _startBatteryMonitor();

      // 订阅笔迹数据特征值
      _subscribeStrokeData();

      return true;
    } catch (e) {
      device.connectionState = PenConnectionState.disconnected;
      _connectionController.add(PenConnectionState.disconnected);
      return false;
    }
  }

  /// 订阅笔迹坐标数据Notify特征值
  void _subscribeStrokeData() {
    // 实际调用:
    // characteristic.setNotifyValue(true);
    // characteristic.onValueReceived.listen(_onStrokeDataReceived);
  }

  /// 处理接收到的笔迹原始数据（7字节紧凑编码）
  /// 数据格式：[X_H, X_L, Y_H, Y_L, Pressure, TS_H, TS_L]
  /// X: 16位无符号（0.01mm精度）
  /// Y: 16位无符号（0.01mm精度）
  /// Pressure: 8位无符号（0-255）
  /// Timestamp: 16位无符号（相对毫秒）
  void _onStrokeDataReceived(Uint8List rawData) {
    if (rawData.length < 7) return;

    // 可能包含多个坐标点（每7字节一个）
    int offset = 0;
    while (offset + 7 <= rawData.length) {
      // 解码X坐标（大端序16位）
      final int rawX = (rawData[offset] << 8) | rawData[offset + 1];
      final double x = rawX * 0.01; // 转换为毫米

      // 解码Y坐标
      final int rawY = (rawData[offset + 2] << 8) | rawData[offset + 3];
      final double y = rawY * 0.01;

      // 解码压力值
      final int pressure = rawData[offset + 4];

      // 解码时间戳
      final int timestamp =
          (rawData[offset + 5] << 8) | rawData[offset + 6];

      // 判断落笔/抬笔（压力值>0为落笔）
      final bool isPenDown = pressure > 0;

      final point = PadPenPoint(
        x: x,
        y: y,
        pressure: pressure,
        timestamp: timestamp,
        isPenDown: isPenDown,
      );

      _strokeBuffer.add(point);
      offset += 7;
    }

    // CRC-16 CCITT校验（如果数据包尾部有2字节CRC）
    if (rawData.length > offset + 1) {
      final int receivedCrc = (rawData[offset] << 8) | rawData[offset + 1];
      final int calculatedCrc = _calculateCrc16(
        rawData.sublist(0, offset),
      );
      if (receivedCrc != calculatedCrc) {
        // CRC校验失败，丢弃本批数据
        _strokeBuffer.clear();
        return;
      }
    }

    // 达到批量阈值后回调
    if (_strokeBuffer.length >= PadBleConstants.strokeBatchSize) {
      _flushStrokeBuffer();
    }
  }

  /// 将缓冲区中的笔迹数据批量回调
  void _flushStrokeBuffer() {
    if (_strokeBuffer.isEmpty || _connectedPen == null) return;

    final event = PenStrokeEvent(
      penMac: _connectedPen!.macAddress,
      points: List.from(_strokeBuffer),
      pageId: _connectedPen!.currentPageId,
    );

    _strokeController.add(event);
    _strokeBuffer.clear();
  }

  /// CRC-16 CCITT校验算法
  /// 多项式: 0x1021, 初始值: 0xFFFF
  int _calculateCrc16(Uint8List data) {
    int crc = 0xFFFF;
    for (int i = 0; i < data.length; i++) {
      crc ^= (data[i] << 8);
      for (int j = 0; j < 8; j++) {
        if ((crc & 0x8000) != 0) {
          crc = ((crc << 1) ^ 0x1021) & 0xFFFF;
        } else {
          crc = (crc << 1) & 0xFFFF;
        }
      }
    }
    return crc;
  }

  /// 启动电量定时读取
  void _startBatteryMonitor() {
    _batteryTimer?.cancel();
    _batteryTimer = Timer.periodic(
      Duration(seconds: PadBleConstants.batteryReadInterval),
      (_) => _readBatteryLevel(),
    );
    // 立即读取一次
    _readBatteryLevel();
  }

  /// 读取笔电量
  Future<void> _readBatteryLevel() async {
    if (_connectedPen == null) return;

    try {
      // 实际调用: 读取battery特征值
      // final value = await batteryCharacteristic.read();
      // _connectedPen!.batteryLevel = value[0];
      // _batteryController.add(_connectedPen!.batteryLevel);
    } catch (e) {
      // 读取失败，忽略
    }
  }

  /// 向笔发送控制指令
  /// [command] 指令类型（如：LED闪烁、蜂鸣提示、固件信息查询）
  Future<void> sendCommand(int command, [Uint8List? payload]) async {
    if (_connectedPen == null) return;

    // 构建指令包：[CMD, LEN, PAYLOAD..., CRC_H, CRC_L]
    final List<int> packet = [command];
    if (payload != null) {
      packet.add(payload.length);
      packet.addAll(payload);
    } else {
      packet.add(0);
    }

    // 追加CRC校验
    final crc = _calculateCrc16(Uint8List.fromList(packet));
    packet.add((crc >> 8) & 0xFF);
    packet.add(crc & 0xFF);

    // 实际调用: controlCharacteristic.write(Uint8List.fromList(packet));
  }

  /// 断开当前笔连接
  Future<void> disconnectPen() async {
    _batteryTimer?.cancel();
    _reconnectTimer?.cancel();

    if (_connectedPen != null) {
      _connectedPen!.connectionState = PenConnectionState.disconnecting;
      _connectionController.add(PenConnectionState.disconnecting);

      // 实际调用: device.disconnect();
      _connectedPen!.connectionState = PenConnectionState.disconnected;
      _connectedPen = null;
      _connectionController.add(PenConnectionState.disconnected);
    }

    // 清空缓冲区
    _flushStrokeBuffer();
  }

  /// 处理连接意外断开，启动自动重连
  void _onDisconnected(PadPenDevice device) {
    if (_reconnectAttempts >= PadBleConstants.maxReconnectAttempts) {
      // 超过最大重连次数，放弃重连
      _connectionController.add(PenConnectionState.disconnected);
      return;
    }

    _connectionController.add(PenConnectionState.reconnecting);
    _reconnectAttempts++;

    // 指数退避延迟重连
    final delay = PadBleConstants.reconnectDelaySeconds * _reconnectAttempts;
    final clampedDelay = delay > 30 ? 30 : delay;

    _reconnectTimer = Timer(Duration(seconds: clampedDelay), () async {
      final success = await connectPen(device);
      if (!success) {
        _onDisconnected(device);
      }
    });
  }

  /// 释放所有资源
  void dispose() {
    _batteryTimer?.cancel();
    _reconnectTimer?.cancel();
    _scanController.close();
    _strokeController.close();
    _connectionController.close();
    _batteryController.close();
    _strokeBuffer.clear();
  }
}
