1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
use core::borrow::Borrow;

use super::ble_client::BLEClientState;
use super::ble_remote_service::BLERemoteServiceState;
use super::{BLEReader, BLEWriter};
use crate::BLEAttribute;
use crate::{
  ble,
  utilities::{as_void_ptr, voidp_to_ref, ArcUnsafeCell, BleUuid, WeakUnsafeCell},
  BLEError, BLERemoteDescriptor, Signal,
};
use alloc::{boxed::Box, vec::Vec};
use bitflags::bitflags;
use core::ffi::c_void;
use esp_idf_svc::sys as esp_idf_sys;

bitflags! {
  #[repr(transparent)]
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  pub struct GattCharacteristicProperties: u8 {
    const BROADCAST = esp_idf_sys::BLE_GATT_CHR_PROP_BROADCAST as _;
    const READ = esp_idf_sys::BLE_GATT_CHR_PROP_READ as _;
    const WRITE_NO_RSP = esp_idf_sys::BLE_GATT_CHR_PROP_WRITE_NO_RSP as _;
    const WRITE = esp_idf_sys::BLE_GATT_CHR_PROP_WRITE as _;
    const NOTIFY = esp_idf_sys::BLE_GATT_CHR_PROP_NOTIFY as _;
    const INDICATE = esp_idf_sys::BLE_GATT_CHR_PROP_INDICATE as _;
    const AUTH_SIGN_WRITE = esp_idf_sys::BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE as _;
    const EXTENDED =  esp_idf_sys::BLE_GATT_CHR_PROP_EXTENDED as _;
  }
}

#[allow(clippy::type_complexity)]
pub struct BLERemoteCharacteristicState {
  pub(crate) service: WeakUnsafeCell<BLERemoteServiceState>,
  pub(crate) uuid: BleUuid,
  pub handle: u16,
  end_handle: u16,
  properties: GattCharacteristicProperties,
  descriptors: Option<Vec<BLERemoteDescriptor>>,
  signal: Signal<u32>,
  on_notify: Option<Box<dyn FnMut(&[u8]) + Send + Sync>>,
}

impl BLEAttribute for BLERemoteCharacteristicState {
  fn get_client(&self) -> Option<ArcUnsafeCell<BLEClientState>> {
    match self.service.upgrade() {
      Some(x) => x.get_client(),
      None => None,
    }
  }
}

impl BLERemoteCharacteristicState {
  pub fn conn_handle(&self) -> u16 {
    match self.service.upgrade() {
      Some(x) => x.conn_handle(),
      None => esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _,
    }
  }
}

#[derive(Clone)]
pub struct BLERemoteCharacteristic {
  state: ArcUnsafeCell<BLERemoteCharacteristicState>,
}

impl BLERemoteCharacteristic {
  pub(crate) fn new(
    service: WeakUnsafeCell<BLERemoteServiceState>,
    chr: &esp_idf_sys::ble_gatt_chr,
  ) -> Self {
    Self {
      state: ArcUnsafeCell::new(BLERemoteCharacteristicState {
        service,
        uuid: BleUuid::from(chr.uuid),
        handle: chr.val_handle,
        end_handle: 0,
        properties: GattCharacteristicProperties::from_bits_truncate(chr.properties),
        descriptors: None,
        signal: Signal::new(),
        on_notify: None,
      }),
    }
  }

  pub(crate) fn state(&self) -> &BLERemoteCharacteristicState {
    &self.state
  }

  pub fn uuid(&self) -> BleUuid {
    self.state.uuid
  }

  pub fn properties(&self) -> GattCharacteristicProperties {
    self.state.properties
  }

  pub async fn get_descriptors(
    &mut self,
  ) -> Result<core::slice::IterMut<'_, BLERemoteDescriptor>, BLEError> {
    if self.state.descriptors.is_none() {
      self.state.descriptors = Some(Vec::new());

      if self.state.end_handle == 0 {
        unsafe {
          ble!(esp_idf_sys::ble_gattc_disc_all_chrs(
            self.state.conn_handle(),
            self.state.handle,
            self.state.service.upgrade().unwrap().end_handle,
            Some(Self::next_char_cb),
            as_void_ptr(self),
          ))?;
        }

        ble!(self.state.signal.wait().await)?;
      }

      if self.state.handle != self.state.end_handle {
        unsafe {
          ble!(esp_idf_sys::ble_gattc_disc_all_dscs(
            self.state.conn_handle(),
            self.state.handle,
            self.state.end_handle,
            Some(Self::descriptor_disc_cb),
            as_void_ptr(self),
          ))?;
        }
        ble!(self.state.signal.wait().await)?;
      }
    }

    Ok(self.state.descriptors.as_mut().unwrap().iter_mut())
  }

  pub async fn get_descriptor(
    &mut self,
    uuid: BleUuid,
  ) -> Result<&mut BLERemoteDescriptor, BLEError> {
    let mut iter = self.get_descriptors().await?;
    iter
      .find(|x| x.uuid() == uuid)
      .ok_or_else(|| BLEError::fail().unwrap_err())
  }

  extern "C" fn next_char_cb(
    conn_handle: u16,
    error: *const esp_idf_sys::ble_gatt_error,
    chr: *const esp_idf_sys::ble_gatt_chr,
    arg: *mut c_void,
  ) -> i32 {
    let characteristic = unsafe { voidp_to_ref::<Self>(arg) };
    if characteristic.state.conn_handle() != conn_handle {
      return 0;
    }

    let error = unsafe { &*error };
    if error.status == 0 {
      characteristic.state.end_handle = unsafe { (*chr).def_handle - 1 };
    } else if error.status == esp_idf_sys::BLE_HS_EDONE as _ {
      characteristic.state.end_handle = characteristic.state.service.upgrade().unwrap().end_handle;
    }

    characteristic.state.signal.signal(error.status as _);
    esp_idf_sys::BLE_HS_EDONE as _
  }

  extern "C" fn descriptor_disc_cb(
    conn_handle: u16,
    error: *const esp_idf_sys::ble_gatt_error,
    _chr_val_handle: u16,
    dsc: *const esp_idf_sys::ble_gatt_dsc,
    arg: *mut c_void,
  ) -> i32 {
    let characteristic = unsafe { voidp_to_ref::<Self>(arg) };
    if characteristic.state.conn_handle() != conn_handle {
      return 0;
    }

    let error = unsafe { &*error };
    let dsc = unsafe { &*dsc };

    if error.status == 0 {
      let descriptor =
        BLERemoteDescriptor::new(ArcUnsafeCell::downgrade(&characteristic.state), dsc);
      characteristic
        .state
        .descriptors
        .as_mut()
        .unwrap()
        .push(descriptor);
      return 0;
    }

    characteristic.state.signal.signal(error.status as _);
    esp_idf_sys::BLE_HS_EDONE as _
  }

  pub async fn read_value(&mut self) -> Result<Vec<u8>, BLEError> {
    let mut reader = BLEReader::new(self.state.conn_handle(), self.state.handle);
    reader.read_value().await
  }

  pub async fn write_value(&mut self, data: &[u8], response: bool) -> Result<(), BLEError> {
    let mut writer = BLEWriter::new(self.state.conn_handle(), self.state.handle);
    writer.write_value(data, response).await
  }

  pub async fn subscribe_notify(&mut self, response: bool) -> Result<(), BLEError> {
    self.set_notify(0x01, response).await
  }

  pub async fn subscribe_indicate(&mut self, response: bool) -> Result<(), BLEError> {
    self.set_notify(0x02, response).await
  }

  pub async fn unsubscribe(&mut self, response: bool) -> Result<(), BLEError> {
    self.set_notify(0x00, response).await
  }

  async fn set_notify(&mut self, val: u16, response: bool) -> Result<(), BLEError> {
    let desc = self.get_descriptor(BleUuid::from_uuid16(0x2902)).await?;
    desc.write_value(&val.to_ne_bytes(), response).await
  }

  pub fn on_notify(&mut self, callback: impl FnMut(&[u8]) + Send + Sync + 'static) -> &mut Self {
    self.state.on_notify = Some(Box::new(callback));
    self
  }

  pub fn can_notify(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::NOTIFY)
  }

  pub fn can_indicate(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::INDICATE)
  }

  pub fn can_read(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::READ)
  }

  pub fn can_write(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::WRITE)
  }

  pub fn can_write_no_response(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::WRITE_NO_RSP)
  }

  pub fn can_broadcast(&self) -> bool {
    self
      .properties()
      .contains(GattCharacteristicProperties::BROADCAST)
  }

  pub(crate) unsafe fn notify(&mut self, om: *mut esp_idf_sys::os_mbuf) {
    if let Some(no_notify) = self.state.on_notify.as_mut() {
      let data = unsafe { core::slice::from_raw_parts((*om).om_data, (*om).om_len as _) };
      no_notify(data);
    }
  }
}

impl core::fmt::Display for BLERemoteCharacteristic {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "BLERemoteCharacteristic[{}]", self.state.uuid)
  }
}

impl core::fmt::Debug for BLERemoteCharacteristic {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    f.debug_struct("BLERemoteCharacteristic")
      .field("uuid", &self.state.uuid)
      .field(
        "service",
        &self.state.service.upgrade().map(|svc| svc.borrow().uuid),
      )
      .field("handle", &self.state.handle)
      .field("end_handle", &self.state.end_handle)
      .field("properties", &self.state.properties)
      .finish()
  }
}