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

import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:fluttertoast/fluttertoast.dart';

import '../objects/cloud_service_api.dart';

class BluetoothObject {
  late int _rssi = -1;
  late BluetoothDevice _device;
  String _address = "";
  String _name = "";

  String id = "";
  String _primaryThumbprint = "";
  final String _secondaryThumbprint = "";

  late BluetoothConnection? _connection;
  late Stream<Uint8List> _connectionStream;
  late StreamSubscription<Uint8List> _connectionStreamSubscription;
  late BuildContext _context;

  final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();

  //late Uint8List _messageBufferBits;
  late String _messageBufferChars = "";

  bool _isDisconnecting = false;

  String get primaryThumbprint {
    return _primaryThumbprint;
  }

  String get secondaryThumbprint {
    return _secondaryThumbprint;
  }

  int get rssi {
    return _rssi;
  }

  String get name {
    return _name;
  }

  String get address {
    return _address;
  }

  BluetoothDevice get device {
    return _device;
  }

  BluetoothConnection? get connection {
    return _connection;
  }

  bool get isConnected {
    return (_connection == null ? false : true);
  }

  BluetoothObject(BluetoothDiscoveryResult result) {
    _rssi = result.rssi;
    _device = result.device;
    _address = _device.address;
    _name = _device.name ?? "device.name.UNKNOWN";
    _connection = null;
  }

  Future<void> bondDevice(BuildContext context) async {
    try {
      bool bonded = false;
      if (_device.isBonded) {
        debugPrint('Unbonding from $_address...');
        await FlutterBluetoothSerial.instance.removeDeviceBondWithAddress(_address);
        debugPrint('Unbonding from $_address has succed');
      } else {
        debugPrint('Bonding with $_address...');
        bonded = (await FlutterBluetoothSerial.instance.bondDeviceAtAddress(_address))!;
        debugPrint('Bonding with $_address has ${bonded ? 'succed' : 'failed'}.');
      }
    } catch (ex) {
      showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text(
                'Error occured while bonding'),
            content: Text(ex.toString()),
            actions: <Widget>[
              TextButton(
                child: const Text("Close"),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        },
      );
    }
  }

  Future<void> disconnectDevice() async {
    _isDisconnecting = true;
    if (_connection != null) {
      await _connection!.finish();
      _connection = null;
    }
    _isDisconnecting = false;
  }

  Future<void> connectDevice() async {
    if (isConnected) {
      await _connection!.finish();
      _connection = null;
    }

    try {
      _connection = await BluetoothConnection.toAddress(_address);
      debugPrint("Connected to the device");
      _connectionStream = _connection!.input!;
      _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
      _connectionStreamSubscription.onDone(_connectionOnDone);
    } catch (ex) {
      debugPrint("$ex");
    }
  }

  void _connectionOnDone() {
    if (_isDisconnecting) {
      debugPrint("Disconnecting locally!");
    } else {
      debugPrint("Disconnected remotely!");
    }
  }

  Future<void> _connectionOnListen(Uint8List data) async {
    final String dataDecoded = const AsciiDecoder().convert(data);
    debugPrint("received decoded: $dataDecoded");
    _messageBufferChars += dataDecoded;

    if (!(data[data.length - 1] == 10)) {
      return;
    }
    if (!_messageBufferChars.contains(" ")) {
      _messageBufferChars = "";
      return;
    }

    List<String> input = _messageBufferChars.split(" ");
    if (input[0] == "fingerprint") {
      _primaryThumbprint = input[1].trim();
      debugPrint("_primaryThumbprint: ${const AsciiEncoder().convert(_primaryThumbprint)}");
      debugPrint("id: ${const AsciiEncoder().convert(id)}");
      debugPrint("_secondaryThumbprint: $_secondaryThumbprint");
      await _registerDevice();
    }
    _messageBufferChars = "";
  }

  Future<void> sendData(BuildContext context, String output) async {
    if (_connection == null) return;
    bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output);
    if (!nameAvailable) return;
    id = output;
    _connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
    await _connection!.output.allSent;
    _context = context;
    debugPrint("sent: $output");
  }

  Future<void> _registerDevice() async {
    bool registered = false;
    registered = await _cloudServiceAPI.createDevice(id, _primaryThumbprint, "");
    _confirmRegistration(registered);
  }

  void _confirmRegistration(bool registered) {
    showDialog<String>(
      context: _context,
      builder: (BuildContext context) => AlertDialog(
        title: const Text('Device Info'),
        content: Text(registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden"),
        backgroundColor: Colors.white,
        actions: <Widget>[
          TextButton(onPressed: () => Navigator.pop(context, 'Cancel'),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }
}
