<commit_before>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Android.App;
using Android.Bluetooth;
using Cirrious.CrossCore;
using MvvmCross.Plugins.BLE.Bluetooth.LE;

namespace MvvmCross.Plugins.BLE.Droid.Bluetooth.LE
{
    public class Characteristic : ICharacteristic
    {
        protected IList<IDescriptor> _descriptors;

        /// <summary>
        ///     we have to keep a reference to this because Android's api is weird and requires
        ///     the GattServer in order to do nearly anything, including enumerating services
        /// </summary>
        protected BluetoothGatt _gatt;

        /// <summary>
        ///     we also track this because of gogole's weird API. the gatt callback is where
        ///     we'll get notified when services are enumerated
        /// </summary>
        protected IGattCallback _gattCallback;

        protected BluetoothGattCharacteristic _nativeCharacteristic;


        public Characteristic(BluetoothGattCharacteristic nativeCharacteristic, BluetoothGatt gatt,
            IGattCallback gattCallback)
        {
            _nativeCharacteristic = nativeCharacteristic;
            _gatt = gatt;
            _gattCallback = gattCallback;
        }

        public event EventHandler<CharacteristicReadEventArgs> ValueUpdated = delegate { };
        public event EventHandler<CharacteristicWriteEventArgs> ValueWritten = delegate { };

        public string Uuid
        {
            get { return _nativeCharacteristic.Uuid.ToString(); }
        }

        public Guid ID
        {
            get { return Guid.Parse(_nativeCharacteristic.Uuid.ToString()); }
        }

        public byte[] Value
        {
            get { return _nativeCharacteristic.GetValue(); }
        }

        public string StringValue
        {
            get
            {
                if (Value == null)
                    return string.Empty;
                return Encoding.UTF8.GetString(Value);
            }
        }

        public string Name
        {
            get { return KnownCharacteristics.Lookup(ID).Name; }
        }

        public CharacteristicPropertyType Properties
        {
            get { return (CharacteristicPropertyType) (int) _nativeCharacteristic.Properties; }
        }

        public IList<IDescriptor> Descriptors
        {
            get
            {
                // if we haven't converted them to our xplat objects
                if (_descriptors == null)
                {
                    _descriptors = new List<IDescriptor>();
                    // convert the internal list of them to the xplat ones
                    foreach (var item in _nativeCharacteristic.Descriptors)
                    {
                        _descriptors.Add(new Descriptor(item));
                    }
                }
                return _descriptors;
            }
        }

        public object NativeCharacteristic
        {
            get { return _nativeCharacteristic; }
        }

        public bool CanRead
        {
            get { return Properties.HasFlag(CharacteristicPropertyType.Read); }
        }

        public bool CanUpdate
        {
            get { return Properties.HasFlag(CharacteristicPropertyType.Notify); }
        }

        //NOTE: why this requires Apple, we have no idea. BLE stands for Mystery.
        public bool CanWrite
        {
            get
            {
                return Properties.HasFlag(CharacteristicPropertyType.WriteWithoutResponse) |
                       Properties.HasFlag(CharacteristicPropertyType.AppleWriteWithoutResponse);
            }
        }

        public Task<bool> WriteAsync(byte[] data)
        {
            if (!CanWrite)
            {
                throw new InvalidOperationException("Characteristic does not support WRITE");
            }

            var tcs = new TaskCompletionSource<bool>();

            EventHandler<CharacteristicWriteEventArgs> writeCallback = null;
            writeCallback = (s, a) =>
            {
                if (a.Characteristic.ID == ID)
                {
                    if (_gattCallback != null)
                    {
                        _gattCallback.CharacteristicValueWritten -= writeCallback;
                    }

                    tcs.SetResult(a.IsSuccessful);
                    //this.ValueWritten(s, a);
                }
            };

            if (_gattCallback != null)
            {
                _gattCallback.CharacteristicValueWritten += writeCallback;
            }
            else
            {
                tcs.SetResult(true);
            }

            //Make sure this is on the main thread or bad things happen
            Application.SynchronizationContext.Post(_ => Write(data), null);


            return tcs.Task;
        }


        public void Write(byte[] data)
        {
            if (!CanWrite)
            {
                throw new InvalidOperationException("Characteristic does not support WRITE");
            }

            if (_gattCallback != null)
            {
                _gattCallback.CharacteristicValueWritten += OnCharacteristicValueWritten;
            }

            var c = _nativeCharacteristic;
            c.SetValue(data);
            Mvx.Trace(".....Write");
            _gatt.WriteCharacteristic(c);
        }


        // HACK: UNTESTED - this API has only been tested on iOS
        public Task<ICharacteristic> ReadAsync()
        {
            var tcs = new TaskCompletionSource<ICharacteristic>();

            if (!CanRead)
            {
                throw new InvalidOperationException("Characteristic does not support READ");
            }
            EventHandler<CharacteristicReadEventArgs> updated = null;
            updated = (object sender, CharacteristicReadEventArgs e) =>
            {
                // it may be other characteristics, so we need to test
                var c = e.Characteristic;
                tcs.SetResult(c);
                if (_gattCallback != null)
                {
                    _gattCallback.CharacteristicValueUpdated -= updated;
                }
            };


            if (_gattCallback != null)
            {
                // wire up the characteristic value updating on the gattcallback
                _gattCallback.CharacteristicValueUpdated += updated;
            }

            Console.WriteLine(".....ReadAsync");
            _gatt.ReadCharacteristic(_nativeCharacteristic);

            return tcs.Task;
        }

        public void StartUpdates()
        {
            // TODO: should be bool RequestValue? compare iOS API for commonality
            var successful = false;<commit_msg>When reading make sure the correct characteristic receives the response.<commit_after>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Android.App;
using Android.Bluetooth;
using Cirrious.CrossCore;
using MvvmCross.Plugins.BLE.Bluetooth.LE;

namespace MvvmCross.Plugins.BLE.Droid.Bluetooth.LE
{
    public class Characteristic : ICharacteristic
    {
        protected IList<IDescriptor> _descriptors;

        /// <summary>
        ///     we have to keep a reference to this because Android's api is weird and requires
        ///     the GattServer in order to do nearly anything, including enumerating services
        /// </summary>
        protected BluetoothGatt _gatt;

        /// <summary>
        ///     we also track this because of gogole's weird API. the gatt callback is where
        ///     we'll get notified when services are enumerated
        /// </summary>
        protected IGattCallback _gattCallback;

        protected BluetoothGattCharacteristic _nativeCharacteristic;


        public Characteristic(BluetoothGattCharacteristic nativeCharacteristic, BluetoothGatt gatt,
            IGattCallback gattCallback)
        {
            _nativeCharacteristic = nativeCharacteristic;
            _gatt = gatt;
            _gattCallback = gattCallback;
        }

        public event EventHandler<CharacteristicReadEventArgs> ValueUpdated = delegate { };
        public event EventHandler<CharacteristicWriteEventArgs> ValueWritten = delegate { };

        public string Uuid
        {
            get { return _nativeCharacteristic.Uuid.ToString(); }
        }

        public Guid ID
        {
            get { return Guid.Parse(_nativeCharacteristic.Uuid.ToString()); }
        }

        public byte[] Value
        {
            get { return _nativeCharacteristic.GetValue(); }
        }

        public string StringValue
        {
            get
            {
                if (Value == null)
                    return string.Empty;
                return Encoding.UTF8.GetString(Value);
            }
        }

        public string Name
        {
            get { return KnownCharacteristics.Lookup(ID).Name; }
        }

        public CharacteristicPropertyType Properties
        {
            get { return (CharacteristicPropertyType) (int) _nativeCharacteristic.Properties; }
        }

        public IList<IDescriptor> Descriptors
        {
            get
            {
                // if we haven't converted them to our xplat objects
                if (_descriptors == null)
                {
                    _descriptors = new List<IDescriptor>();
                    // convert the internal list of them to the xplat ones
                    foreach (var item in _nativeCharacteristic.Descriptors)
                    {
                        _descriptors.Add(new Descriptor(item));
                    }
                }
                return _descriptors;
            }
        }

        public object NativeCharacteristic
        {
            get { return _nativeCharacteristic; }
        }

        public bool CanRead
        {
            get { return Properties.HasFlag(CharacteristicPropertyType.Read); }
        }

        public bool CanUpdate
        {
            get { return Properties.HasFlag(CharacteristicPropertyType.Notify); }
        }

        //NOTE: why this requires Apple, we have no idea. BLE stands for Mystery.
        public bool CanWrite
        {
            get
            {
                return Properties.HasFlag(CharacteristicPropertyType.WriteWithoutResponse) |
                       Properties.HasFlag(CharacteristicPropertyType.AppleWriteWithoutResponse);
            }
        }

        public Task<bool> WriteAsync(byte[] data)
        {
            if (!CanWrite)
            {
                throw new InvalidOperationException("Characteristic does not support WRITE");
            }

            var tcs = new TaskCompletionSource<bool>();

            EventHandler<CharacteristicWriteEventArgs> writeCallback = null;
            writeCallback = (s, a) =>
            {
                if (a.Characteristic.ID == ID)
                {
                    if (_gattCallback != null)
                    {
                        _gattCallback.CharacteristicValueWritten -= writeCallback;
                    }

                    tcs.SetResult(a.IsSuccessful);
                    //this.ValueWritten(s, a);
                }
            };

            if (_gattCallback != null)
            {
                _gattCallback.CharacteristicValueWritten += writeCallback;
            }
            else
            {
                tcs.SetResult(true);
            }

            //Make sure this is on the main thread or bad things happen
            Application.SynchronizationContext.Post(_ => Write(data), null);


            return tcs.Task;
        }


        public void Write(byte[] data)
        {
            if (!CanWrite)
            {
                throw new InvalidOperationException("Characteristic does not support WRITE");
            }

            if (_gattCallback != null)
            {
                _gattCallback.CharacteristicValueWritten += OnCharacteristicValueWritten;
            }

            var c = _nativeCharacteristic;
            c.SetValue(data);
            Mvx.Trace(".....Write");
            _gatt.WriteCharacteristic(c);
        }


        // HACK: UNTESTED - this API has only been tested on iOS
        public Task<ICharacteristic> ReadAsync()
        {
            var tcs = new TaskCompletionSource<ICharacteristic>();

            if (!CanRead)
            {
                throw new InvalidOperationException("Characteristic does not support READ");
            }
            EventHandler<CharacteristicReadEventArgs> updated = null;
            updated = (object sender, CharacteristicReadEventArgs e) =>
            {
                // it may be other characteristics, so we need to test
                if (e.Characteristic.ID == ID)
                {
                    tcs.SetResult(e.Characteristic);
                    if (_gattCallback != null)
                    {
                        _gattCallback.CharacteristicValueUpdated -= updated;
                    }
                }
            };


            if (_gattCallback != null)
            {
                // wire up the characteristic value updating on the gattcallback
                _gattCallback.CharacteristicValueUpdated += updated;
            }

            Console.WriteLine(".....ReadAsync");
            _gatt.ReadCharacteristic(_nativeCharacteristic);

            return tcs.Task;
        }

        public void StartUpdates()
        {
            // TODO: should be bool RequestValue? compare iOS API for commonality
            var successful = false;
{'fraction_non_alphanumeric': 0.07185147284555223, 'fraction_numerical': 0.00014618814414151012, 'mean_word_length': 1.500822518735149, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3227589', 'n_tokens_mistral': 3274, 'n_tokens_neox': 3084, 'n_words': 808}