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 = "";
  String _secondaryThumbprint = "";

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

  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() 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'}.');
      }
      /*setState(() {
        _discoveryResults[_discoveryResults.indexOf(
            result)] =
            BluetoothDiscoveryResult(
                device: BluetoothDevice(
                  name: device.name ?? '',
                  address: address,
                  type: device.type,
                  bondState: bonded
                      ? BluetoothBondState.bonded
                      : BluetoothBondState.none,
                ),
                rssi: result.rssi);
      });*/
    } 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;
    }

    _connection = await BluetoothConnection.toAddress(_address);
    debugPrint("Connected to the device");

    _connectionStream = _connection!.input!;
    _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
    _connectionStreamSubscription.onDone(_connectionOnDone);
  }

  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: $data");
    debugPrint("received decoded: $dataDecoded");

    _messageBufferChars += dataDecoded;

    if (data[data.length - 1] == 10) {
      debugPrint("received buffer: $_messageBufferChars");
      int spaceIndex = _messageBufferChars.indexOf(" ");
      String firstParameter = "";
      String secondParameter = "";
      try {
        firstParameter = _messageBufferChars.substring(0, spaceIndex);
        secondParameter = _messageBufferChars.substring(spaceIndex + 1, _messageBufferChars.length - 2);
      } catch (ex) {
        debugPrint(ex.toString());
      }
      debugPrint("we still go on!");
      _messageBufferChars = "";
      debugPrint("first: $firstParameter");
      debugPrint("second: $secondParameter");
      if (firstParameter == "fingerprint") {
        debugPrint("received final buffer: $secondParameter");
        _primaryThumbprint = secondParameter;
        debugPrint("{ _secondaryThumbprint: $_secondaryThumbprint }");
        debugPrint("{ id: $id, _primaryThumbprint: $_primaryThumbprint, _secondaryThumbprint: $_secondaryThumbprint }");
        await _registerDevice();
      }
    }

    /*
    // Allocate buffer for parsed data
    int backspacesCounter = 0;
    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) {
      _messageBuffer = dataString.substring(index);
    } else {
      _messageBuffer = (backspacesCounter > 0
          ? _messageBuffer.substring(0, _messageBuffer.length - backspacesCounter)
          : _messageBuffer + dataString);
    }
    */
  }

  Future<void> sendData(String output) async {
    if (_connection == null) return;
    bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output);
    // String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
    // String output = "myDevice123";// !§%&()=?#!?";
    if (!nameAvailable) return;
    id = output;
    _connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
    await _connection!.output.allSent;
    debugPrint("sent: $output");
  }

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

    String statusText =
        registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden";
    Fluttertoast.showToast(
        msg: statusText,
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.BOTTOM,
        timeInSecForIosWeb: 2,
        backgroundColor: Colors.grey[200],
        textColor: Colors.black,
        fontSize: 16.0);
  }
}

/*setState(() {
      _discoveryResults[_discoveryResults.indexOf(
          result)] =
          BluetoothDiscoveryResult(
              device: BluetoothDevice(
                name: device.name ?? '',
                address: address,
                type: device.type,
                isConnected: connected,
              ),
              rssi: result.rssi);
    });
     */
