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

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

import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_list_entry.dart';

import 'package:app_settings/app_settings.dart';
import 'package:permission_handler/permission_handler.dart';


class BluetoothScreen extends StatefulWidget {
  final bool start = true;

  const BluetoothScreen({super.key});

  @override
  State<BluetoothScreen> createState() => _BluetoothScreen();
}

// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}");
class _BluetoothScreen extends State<BluetoothScreen> {

  CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
  //BluetoothManager bluetoothManager = BluetoothManager();

  late Stream<BluetoothDiscoveryResult> _stream;
  late StreamSubscription<BluetoothDiscoveryResult> _streamSubscription;
  late Stream<Uint8List> _connectionStream;
  late StreamSubscription<Uint8List> _connectionStreamSubscription;
  BluetoothDevice? _bluetoothDevice;
  BluetoothConnection? _bluetoothConnection;
  final List<BluetoothDiscoveryResult> _discoveryResults =
  List<BluetoothDiscoveryResult>.empty(growable: true);
  String _messageBuffer = "";
  bool isDiscovering = false;
  bool isConnecting = false;
  bool isDisconnecting = false;
  bool isConnected = false;
  String textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  ButtonStyle buttonStyle = ElevatedButton.styleFrom(
    foregroundColor: Colors.black,
    backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
  );

  @override
  void initState() {
    super.initState();
    isDiscovering = widget.start;
    if (isDiscovering) {
      _startDiscovery();
    }
  }

  @override
  void dispose() {
    // Avoid memory leak (`setState` after dispose) and cancel discovery
    _streamSubscription.cancel();
    if (isConnected) {
      isDisconnecting = true;
      _bluetoothConnection?.dispose();
      _bluetoothConnection = null;
    }
    super.dispose();
  }

  Future<void> _disconnectDevice(BluetoothDevice device) async {
    if (_bluetoothConnection != null) {
      await _bluetoothConnection!.finish();
      _bluetoothConnection = null;
    }
  }

  Future<void> _connectDevice(BluetoothDevice device) async {
    String address = device.address;
    if (_bluetoothConnection != null) {
      await _bluetoothConnection!.finish();
      _bluetoothConnection = null;
    }
    _bluetoothConnection = await BluetoothConnection.toAddress(address);
    debugPrint("Connected to the device");
    setState(() {
      isConnecting = false;
      isDisconnecting = false;
    });

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

  void _connectionOnDone() {
    /*
    // Example: Detect which side closed the connection
    // There should be `isDisconnecting` flag to show are we are (locally)
    // in middle of disconnecting process, should be set before calling
    // `dispose`, `finish` or `close`, which all causes to disconnect.
    // If we except the disconnection, `onDone` should be fired as result.
    // If we didn't except this (no flag set), it means closing by remote.
    */
    if (isDisconnecting) {
      debugPrint("Disconnecting locally!");
    } else {
      debugPrint("Disconnected remotely!");
    }
    if (mounted) {
      setState(() {});
    }
  }

  void _connectionOnListen(Uint8List data) {
    debugPrint("received: $data");
    debugPrint("received decoded: ${const Utf8Decoder().convert(data)}");
    // 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) {
      setState(() {
        _messageBuffer = dataString.substring(index);
      });
    } else {
      _messageBuffer = (backspacesCounter > 0
          ? _messageBuffer.substring(
          0, _messageBuffer.length - backspacesCounter)
          : _messageBuffer + dataString);
    }
  }

  Future<void> _startDiscovery() async {
    _stream = FlutterBluetoothSerial.instance.startDiscovery();
    _streamSubscription = _stream.listen(_discoveryOnListen);
    _streamSubscription.onDone(_discoveryOnDone);
  }

  void _discoveryOnDone() {
    setState(() {
      isDiscovering = false;
      //debugPrint(isDiscovering as String?);
    });
  }

  void _discoveryOnListen(BluetoothDiscoveryResult event) {
    setState(() {
      final int existingIndex = _discoveryResults.indexWhere(
              (element) => element.device.address == event.device.address);
      if (existingIndex >= 0) {
        _discoveryResults[existingIndex] = event;
      } else {
        _discoveryResults.add(event);
      }
      debugPrint("event: ${event.device.address} ${event.device.name}");

      String deviceAddress = "64:BC:58:61:56:B0";
      if (event.device.address == deviceAddress) {
        _bluetoothDevice = event.device;
      }
    });
  }

  Future<void> _restartDiscovery() async {
    setState(() {
      _discoveryResults.clear();
      isDiscovering = true;
    });
    _startDiscovery();
  }

  Future<void> _sendData() async {
    //String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
    String output = textInput;
    _bluetoothConnection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
    await _bluetoothConnection!.output.allSent;
    debugPrint("sent: $output");
  }

  @override
  Widget build(BuildContext context) {
    //bool isDark = true;
    return Theme(
      data: ThemeData.dark(), //isDark ? ThemeData.dark() : ThemeData.light(),
      child: Scaffold(
        appBar: AppBar(
          title: isDiscovering
              ? const Text('Bluetooth Devices (searching...)')
              : const Text('Bluetooth Devices'),
          actions: <Widget>[
            isDiscovering
                ? FittedBox(
                    child: Container(
                      margin: const EdgeInsets.all(16.0),
                      child: const CircularProgressIndicator(
                        valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                      ),
                    ),
                  )
                : IconButton(
                    icon: const Icon(Icons.replay),
                    onPressed: _restartDiscovery,
                  )
          ],
        ),

        body: // Center(
            /*child:*/ SingleChildScrollView(
          physics: const ScrollPhysics(),
          child: Column(
            children: <Widget>[
              _SingleSection(
                title: "Setup",
                children: [
                  const _CustomListTile(
                    title: "Please Enable Bluetooth",
                    icon: Icons.info_outline_rounded,
                  ),
                  _CustomListTile(
                    title: "Bluetooth Settings",
                    icon: Icons.bluetooth_connected,
                    onTap: () async {
                      await AppSettings.openBluetoothSettings();
                    },
                  ),
                ],
              ),
              const Divider(),
              TextFormField(
                decoration: const InputDecoration(
                    labelText: "BluetoothText"
                ),
                initialValue: textInput,
                keyboardType: TextInputType.text,
                onChanged: (String newValue) {
                  textInput = newValue;
                },
              ),
              ElevatedButton(
                onPressed: () async {
                  await Permission.bluetoothConnect.request();
                  await Permission.bluetoothScan.request();
                  if (await Permission.bluetoothScan.request().isGranted) {
                    // Either the permission was already granted before or the user just granted it.
                    debugPrint("Location Permission is granted");
                  } else {
                    debugPrint("Location Permission is denied.");
                  }
                },
                style: buttonStyle,
                child: const Text("Request Permissions"),
              ),
              ElevatedButton(
                onPressed: () async {
                  _restartDiscovery();
                },
                style: buttonStyle,
                child: const Text("Restart Scan"),
              ),
              ElevatedButton(
                onPressed: () async {
                  _sendData();
                },
                style: buttonStyle,
                child: const Text("Send Data"),
              ),
              const Divider(),
              const _SingleSection(
                title: "Bluetooth Devices",
                children: [],
              ),
              const Divider(),
              ListView.builder(
                physics: const NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount: _discoveryResults.length,
                itemBuilder: (BuildContext context, index) {
                  BluetoothDiscoveryResult result = _discoveryResults[index];
                  final device = result.device;
                  final address = device.address;
                  return BluetoothDeviceListEntry(
                    device: device,
                    rssi: result.rssi,
                    onTap: () {
                      debugPrint(_bluetoothConnection?.isConnected.toString());
                      if (_bluetoothConnection?.isConnected ?? false) {
                        _disconnectDevice(device);
                      } else {
                        _connectDevice(device);
                      }

                      //Navigator.of(context).pop(result.device);
                    },
                    onLongPress: () async {
                      try {
                        bool bonded = false;
                        if (device.isBonded) {
                          debugPrint('Unbonding from ${device.address}...');
                          await FlutterBluetoothSerial.instance
                              .removeDeviceBondWithAddress(address);
                          debugPrint(
                              'Unbonding from ${device.address} has succed');
                        } else {
                          debugPrint('Bonding with ${device.address}...');
                          bonded = (await FlutterBluetoothSerial.instance
                              .bondDeviceAtAddress(address))!;
                          debugPrint(
                              'Bonding with ${device.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();
                                  },
                                ),
                              ],
                            );
                          },
                        );
                      }
                    },
                  );
                },
              ),
            ],
          ),
        ),
        //),
      ),
    );
  }
/*
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Bluetooth Devices"),
      ),
      body: RefreshIndicator(
        onRefresh: () {
          debugPrint("refreshed");
          return Future(() => null);
        },
        child: SingleChildScrollView(
          physics: const AlwaysScrollableScrollPhysics(),
          //child: Padding(
          //padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
                decoration: const BoxDecoration(
                  border: Border(
                    bottom: BorderSide(width: 1.5, color: Colors.grey),
                  ),
                ),
                child: Column(
                  children: [
                    const Text(
                        "General",
                        textAlign: TextAlign.start,
                    ),
                    Row(
                      children: [
                        _CustomListTile(
                            title: "Dark Mode",
                            icon: CupertinoIcons.moon,
                            trailing:
                            CupertinoSwitch(value: false, onChanged: (value) {})),
                      ]
                    ),
                    TextField(
                      controller: textFieldValueHolder,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'Enter the name of your new device',
                      ),
                    ),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        onPressed: () {
                          var textFieldValue = textFieldValueHolder.value.text;
                          checkNameAvailability(textFieldValue);
                          setState(() {
                            inputName = textFieldValue;
                          });
                        },
                        style: buttonStyle,
                        child: const Text("check name"),
                      ),
                    ),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        onPressed: () {
                          //_startDiscovery();
                          initScan();
                        },
                        style: buttonStyle,
                        child: const Text("discover devices"),
                      ),
                    ),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        onPressed: () {
                          //_startDiscovery();
                          _sendData();
                        },
                        style: buttonStyle,
                        child: const Text("send data"),
                      ),
                    ),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        onPressed: () {
                          //_startDiscovery();
                          dispose();
                        },
                        style: buttonStyle,
                        child: const Text("dispose"),
                      ),
                    ),
                  ],
                ),
              ),
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
                decoration: const BoxDecoration(
                  border: Border(
                    bottom: BorderSide(width: 1.5, color: Colors.grey),
                  ),
                ),
                child: Row(children: [
                  const Text(
                    "Toggle Scan",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  const Expanded(child: Text("")),
                  Switch(
                      value: widgetScanState,
                      onChanged: (bool toggleState) {
                        scanState = toggleState;
                        log("widget state: $scanState");
                        setState(() {
                          widgetScanState = toggleState;
                        });
                      })
                ]),
              ),
              // BluetoothDiscovery(start: initScan, deviceID: inputName),
            ],
          ),
          //),
        ),
      ),
    );
  }*/
}

class _CustomListTile extends StatelessWidget {
  final String title;
  final IconData icon;
  final VoidCallback? onTap;
  //final Widget? trailing;

  const _CustomListTile({
    Key? key,
    required this.title,
    required this.icon,
    this.onTap,
    //this.trailing,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      leading: Icon(icon),
      onTap: onTap,
      //trailing: trailing,
    );
  }
}

class _SingleSection extends StatelessWidget {
  final String? title;
  final List<Widget> children;

  const _SingleSection({
    Key? key,
    this.title,
    required this.children,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (title != null)
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              title!,
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
            ),
          ),
        Column(
          children: children,
        ),
      ],
    );
  }
}
