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

import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:get/get.dart';
import 'package:nmea/nmea.dart';
import 'package:terepi_seged/gnss_sentences/gngga.dart';

class BluetoothTestViewController extends GetxController {
  Rx<BluetoothState> bluetoothState = BluetoothState.UNKNOWN.obs;
  RxList<BluetoothDevice> devices = RxList<BluetoothDevice>([]);
  Rx<BluetoothDevice>? selectedDevice;
  final NmeaDecoder nmeaDecoder = NmeaDecoder();

  BluetoothConnection? connection;
  StreamSubscription<BluetoothDiscoveryResult>? _bluetoothDiscoveryStream;
  RxList<BluetoothDiscoveryResult> bluetoothDiscoveryResults =
      RxList<BluetoothDiscoveryResult>.empty(growable: true);

  // bool get isConnected => connection != null && connection!.isConnected;
  bool isConnected = false;
  int count = 0;
  String _messageBuffer = '';
  bool hasGpsData = false;
  DateTime lastGpsRefreshTime = DateTime.now();
  RxString utcOfPositionFix = "".obs;

  @override
  void onInit() {
    super.onInit();

    print("Call BluetoothTestController.OnInit");

    FlutterBluetoothSerial.instance.state.then((state) {
      bluetoothState.value = state;
      if (bluetoothState.value.isEnabled) {
        //_listBoundedDevices();
        _startDiscovery();
      }
    });
    nmeaDecoder.registerTalkerSentence("GGA", (line) => Gngga(raw: line));

    //Loading, Success, Error handle with 1 line of code
  }

  @override
  void onClose() {
    super.onClose();
    FlutterBluetoothSerial.instance.setPairingRequestHandler(null);
    if (isConnected) {
      connection?.close();
      // connection = null;
      print("BluetoothTestController dispose ....");
    }
  }

  _listBoundedDevices() {
    FlutterBluetoothSerial.instance
        .getBondedDevices()
        .then((List<BluetoothDevice> boundedDevices) {
      devices.value = boundedDevices;
      devices.refresh();
    });
  }

  void setSelectedDevice(BluetoothDevice device) {
    selectedDevice = Rx<BluetoothDevice>(device);

    if (!isConnected) {
      BluetoothConnection.toAddress(selectedDevice?.value.address)
          .then((value) {
        connection = value;
        isConnected = true;
        print("Bleutooth device connected .... ${connection!.isConnected}");

        connection!.input!.listen(_onDataReceived);
      });

      // connection!.input!.listen((data) {
      //   print("Data -> $data");
      // });
    }
  }

  void _startDiscovery() {
    _bluetoothDiscoveryStream =
        FlutterBluetoothSerial.instance.startDiscovery().listen((event) {
      final existingIndex = bluetoothDiscoveryResults.indexWhere(
          (element) => element.device.address == event.device.address);
      if (existingIndex > 0) {
        bluetoothDiscoveryResults[existingIndex] = event;
        update();
      } else {
        bluetoothDiscoveryResults.add(event);
        update();
      }
    });

    _bluetoothDiscoveryStream!.onDone(() {});
  }

  void _onDataReceived(Uint8List data) {
    // Allocate buffer for parsed data
    int backspacesCounter = 0;
    String sentence = '';

    // print("Data: $data");

    for (var byte in data) {
      if (byte == 8 || byte == 127) {
        backspacesCounter++;
      }
    }
    Uint8List buffer = Uint8List(data.length - backspacesCounter);
    int bufferIndex = buffer.length;

    // Apply backspace control character
    backspacesCounter = 0;
    for (int i = data.length - 1; i >= 0; i--) {
      if (data[i] == 8 || data[i] == 127) {
        backspacesCounter++;
      } else {
        if (backspacesCounter > 0) {
          backspacesCounter--;
        } else {
          buffer[--bufferIndex] = data[i];
        }
      }
    }

    // Create message if there is new line character
    String dataString = String.fromCharCodes(buffer);
    int index = buffer.indexOf(13);
    if (~index != 0) {
      sentence = backspacesCounter > 0
          ? _messageBuffer.substring(
              0, _messageBuffer.length - backspacesCounter)
          : _messageBuffer + dataString.substring(0, index);
      _messageBuffer = dataString.substring(index);
    } else {
      _messageBuffer = (backspacesCounter > 0
          ? _messageBuffer.substring(
              0, _messageBuffer.length - backspacesCounter)
          : _messageBuffer + dataString);
    }

    count++;
    // print("Message ($count): $sentence");
    _processGnssMessage(sentence);
  }

  void _processGnssMessage(String message) {
    LineSplitter lineSplitter = const LineSplitter();
    List<String> lines = lineSplitter.convert(message);

    if (lines.isEmpty) {
      return;
    }

    for (String line in lines) {
      if (line.trim().isEmpty) {
        continue;
      }
      if (line.startsWith("\$GNGGA")) {
        final sentence = nmeaDecoder.decode(line);
        if (sentence!.valid && sentence is Gngga) {
          hasGpsData = sentence.gpsQualityIndicator > 0;
          if (hasGpsData) {
            if (DateTime.now().difference(lastGpsRefreshTime).inSeconds >= 4) {
              lastGpsRefreshTime = DateTime.now();
              utcOfPositionFix.value = sentence.utcOfPositionFix;
            }
            print("Last update: $utcOfPositionFix");
          }
        }
      }
    }
  }
}
