extern "C" {
#include "dittoffi.h"
}
#include "retainable.h"
#include <atomic>
#include <cassert>
#include <memory>
#include <string>

#ifdef SWIG
%feature("director", assumeoverride=1) WifiAwareRust;
%apply(char *STRING, size_t LENGTH) { (const char data[], size_t len) }; // byte[] instead of String
#endif
class WifiAwareRust : public Retainable {
public:
  virtual ~WifiAwareRust() {}

  virtual void clientStartSearching(const char *hashed_app_name,
                                    const char *local_announce) = 0;
  virtual void clientStopSearching() = 0;
  virtual void clientCreateNetwork(const char *announce) = 0;
  virtual void clientUpdatePeer(const char *announce, ConnectState_t state) = 0;

  virtual void serverStartAdvertising(const char *announce,
                                      const char *hashed_app_name,
                                      uint16_t service_port) = 0;
  virtual void serverStopAdvertising() = 0;
  virtual void serverUpdatePeer(const char *announce, ConnectState_t state) = 0;
  virtual void
  updateParameterU64(dittoffi_wifi_aware_system_parameter_u64 parameter,
                     uint64_t value) = 0;

  static void invokeClientStartSearching(void *ctx, const char *hashed_app_name,
                                         const char *local_announce) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->clientStartSearching(hashed_app_name, local_announce);
  }

  static void invokeClientStopSearching(void *ctx) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->clientStopSearching();
  }

  static void invokeClientCreateNetwork(void *ctx, const char *announce) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->clientCreateNetwork(announce);
  }

  static void invokeClientUpdatePeer(void *ctx, const char *announce,
                                     ConnectState_t state) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->clientUpdatePeer(announce, state);
  }

  static void invokeServerStartAdvertising(void *ctx, const char *announce,
                                           const char *hashed_app_name,
                                           uint16_t service_port) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->serverStartAdvertising(announce, hashed_app_name,
                                        service_port);
  }
  static void invokeServerStopAdvertising(void *ctx) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->serverStopAdvertising();
  }

  static void invokeServerUpdatePeer(void *ctx, const char *announce,
                                     ConnectState_t state) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->serverUpdatePeer(announce, state);
  }

  static void
  invokeUpdateParameterU64(void *ctx,
                           dittoffi_wifi_aware_system_parameter_u64 parameter,
                           uint64_t value) {
    auto inst = static_cast<WifiAwareRust *>(ctx);
    return inst->updateParameterU64(parameter, value);
  }

  virtual void addWifiAwareTransport(struct CDitto *ditto) {
    WifiAwareClientCallbacks_t client_cb = {
        .start_searching = WifiAwareRust::invokeClientStartSearching,
        .stop_searching = WifiAwareRust::invokeClientStopSearching,
        .create_network = WifiAwareRust::invokeClientCreateNetwork,
        .update_peer = WifiAwareRust::invokeClientUpdatePeer,
    };
    WifiAwareServerCallbacks_t server_cb = {
      .start_advertising = WifiAwareRust::invokeServerStartAdvertising,
      .stop_advertising = WifiAwareRust::invokeServerStopAdvertising,
      .update_peer = WifiAwareRust::invokeServerUpdatePeer,
      .update_parameter_u64 = WifiAwareRust::invokeUpdateParameterU64,
  };

  auto handle = ditto_add_wifi_aware_transport(
      ditto, client_cb, server_cb, this, WifiAwareRust::invokeRetain,
      WifiAwareRust::invokeRelease);
    atomic_store(
        &transportHandle,
        std::shared_ptr<TransportHandle_WifiAwarePlatformEvent_t>(
            handle, [](TransportHandle_WifiAwarePlatformEvent_t *h) {
              ditto_wifi_aware_transport_free_handle(h);
            }));
  }

  virtual void removeWifiAwareTransport() {
    atomic_store(
        &transportHandle,
        std::shared_ptr<TransportHandle_WifiAwarePlatformEvent_t>());
  }

  virtual void clientPeerAppeared(const char *announce) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_client_peer_appeared(handle.get(), announce);
    }
  }
  virtual void clientPeerDisappeared(const char *announce) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_client_peer_disappeared(handle.get(), announce);
    }
  }
  virtual void clientScanningStateChanged(OnlineState_t state,
                                          TransportCondition_t result) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_client_scanning_state_changed(handle.get(), state,
                                                     result);
    }
  }
  virtual void clientNetworkDidCreate(const char *announce,
                                      const char *hostname, uint16_t port) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_client_network_did_create(handle.get(), announce,
                                                 hostname, port);
    }
  }
  virtual void clientNetworkDidNotCreate(const char *announce) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_client_peer_did_not_connect(handle.get(), announce);
    }
  }

  virtual void serverAdvertisingStateChanged(OnlineState_t state,
                                             TransportCondition_t result) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_server_advertising_state_changed(handle.get(), state,
                                                        result);
    }
  }

  virtual void serverNetworkScopeIdAdded(uint32_t scope_id) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_server_network_scope_id_added(handle.get(), scope_id);
    }
  }

  virtual void serverNetworkScopeIdRemoved(uint32_t scope_id) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_server_network_scope_id_removed(handle.get(), scope_id);
    }
  }

  virtual void goOnlineRequest() {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_server_go_online_request(handle.get());
      ditto_wifi_aware_client_go_online_request(handle.get());
    }
  }

  virtual void goOfflineRequest() {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_wifi_aware_server_go_offline_request(handle.get());
      ditto_wifi_aware_client_go_offline_request(handle.get());
    }
  }

  std::shared_ptr<TransportHandle_WifiAwarePlatformEvent_t> transportHandle;
};

#ifdef SWIG
%feature("director", assumeoverride=1) BluetoothLeRadioRust;
%apply(char *STRING, size_t LENGTH) { (const char peripheralUuid[], size_t uuidLen) }; // byte[] instead of String
%apply(char *STRING, size_t LENGTH) { (const char centralUuid[], size_t uuidLen) }; // need one per param name for SWIG
%apply(char *STRING, size_t LENGTH) { (const char uuid[], size_t uuidLen) };
%apply(char *STRING, size_t LENGTH) { (const char serviceUuid[], size_t uuidLen) };
%apply(char *STRING, size_t LENGTH) { (const char characteristicUuid[], size_t uuidLen) };
%apply(char *STRING, size_t LENGTH) { (const char manufacturerData[], size_t manufacturerDataLen) };
// It is implied that UUID buffers are 16 bytes in length - insert an extra size_t for this Java array conversion
%apply(char *STRING, size_t LENGTH) { (const char data[], size_t len) }; // byte[] instead of String
#endif
class BluetoothLeRadioRust : public Retainable {
public:
  virtual ~BluetoothLeRadioRust() {}
  virtual void connectPeripheral(const char peripheralUuid[],
                                 size_t uuidLen) = 0;
  virtual void disconnectPeripheral(const char peripheralUuid[],
                                    size_t uuidLen) = 0;
  virtual OnlineState_t getAdvertisingState() { return ONLINE_STATE_OFFLINE; }
  virtual OnlineState_t getScanningState() { return ONLINE_STATE_OFFLINE; }
  virtual SendResult_t notifyToCentral(BleDataType_t sendType,
                                       const char centralUuid[], size_t uuidLen,
                                       const char data[], size_t len) = 0;
  virtual void startAdvertising(const char serviceUuid[], size_t uuidLen,
                                char *localName) = 0;
  virtual void startScanning(const char serviceUuid[], size_t uuidLen,
                             char *announce) = 0;
  virtual void stopAdvertising() = 0;
  virtual void stopScanning() = 0;
  virtual SendResult_t writeToPeripheral(BleDataType_t sendType,
                                         const char peripheralUuid[],
                                         size_t uuidLen, const char data[],
                                         size_t len) = 0;
  virtual bool appIsInForeground() = 0;
  virtual int readL2capFromPeripheral(const char peripheralUuid[],
                                      size_t uuidLen,
                                      slice_mut_uint8_t data) = 0;
  virtual int sendL2capToPeripheral(const char peripheralUuid[], size_t uuidLen,
                                    const char data[], size_t len) = 0;
  virtual int readL2capFromCentral(const char centralUuid[], size_t uuidLen,
                                   slice_mut_uint8_t data) = 0;
  virtual int sendL2capToCentral(const char centralUuid[], size_t uuidLen,
                                 const char data[], size_t len) = 0;

  static void invokeConnectPeripheral(void *ctx,
                                      uint8_16_array_t const *peripheral_uuid) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->connectPeripheral((const char *)peripheral_uuid, (size_t)16);
  }
  static void
  invokeDisconnectPeripheral(void *ctx,
                             uint8_16_array_t const *peripheral_uuid) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->disconnectPeripheral((const char *)peripheral_uuid,
                                      (size_t)16);
  }
  static OnlineState_t invokeGetAdvertisingState(void *ctx) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->getAdvertisingState();
  }
  static OnlineState_t invokeGetScanningState(void *ctx) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->getScanningState();
  }
  static SendResult_t
  invokeNotifyToCentral(void *ctx, BleDataType_t sendType,
                        uint8_16_array_t const *central_uuid,
                        slice_ref_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->notifyToCentral(sendType, (char const *)central_uuid, 16,
                                 (char const *)data.ptr, data.len);
  }
  static void invokeStartAdvertising(void *ctx,
                                     uint8_16_array_t const *service_uuid,
                                     slice_ref_uint8_t local_name) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    char *local_name_str =
        strndup((char const *)local_name.ptr, local_name.len);
    inst->startAdvertising((const char *)service_uuid, 16, local_name_str);
    free(local_name_str);
  }
  static void invokeStartScanning(void *ctx,
                                  uint8_16_array_t const *service_uuid,
                                  slice_ref_uint8_t announce) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    char *announce_str = strndup((char const *)announce.ptr, announce.len);
    inst->startScanning((const char *)service_uuid, 16, announce_str);
    free(announce_str);
  }
  static void invokeStopAdvertising(void *ctx) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    inst->stopAdvertising();
  }
  static void invokeStopScanning(void *ctx) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    inst->stopScanning();
  }
  static SendResult_t
  invokeWriteToPeripheral(void *ctx, BleDataType_t sendType,
                          uint8_16_array_t const *peripheral_uuid,
                          slice_ref_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->writeToPeripheral(sendType, (const char *)peripheral_uuid, 16,
                                   (const char *)data.ptr, data.len);
  }
  static bool invokeAppIsInForeground(void *ctx) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->appIsInForeground();
  }
  static int
  invokeReadL2capFromPeripheral(void *ctx,
                                uint8_16_array_t const *peripheral_uuid,
                                slice_mut_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->readL2capFromPeripheral((const char *)peripheral_uuid, 16,
                                         data);
  }
  static int
  invokeSendL2capToPeripheral(void *ctx,
                              uint8_16_array_t const *peripheral_uuid,
                              slice_ref_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->sendL2capToPeripheral((const char *)peripheral_uuid, 16,
                                       (char const *)data.ptr, data.len);
  }
  static int invokeReadL2capFromCentral(void *ctx,
                                        uint8_16_array_t const *central_uuid,
                                        slice_mut_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->readL2capFromCentral((const char *)central_uuid, 16, data);
  }
  static int invokeSendL2capToCentral(void *ctx,
                                      uint8_16_array_t const *central_uuid,
                                      slice_ref_uint8_t data) {
    auto inst = static_cast<BluetoothLeRadioRust *>(ctx);
    return inst->sendL2capToCentral((const char *)central_uuid, 16,
                                    (char const *)data.ptr, data.len);
  }
  virtual void addBleTransport(struct CDitto *ditto) {
    BleClientCallbacks_t client_cb = {
        .start_scanning = BluetoothLeRadioRust::invokeStartScanning,
        .stop_scanning = BluetoothLeRadioRust::invokeStopScanning,
        .scanning_state = BluetoothLeRadioRust::invokeGetScanningState,
        .connect_peripheral = BluetoothLeRadioRust::invokeConnectPeripheral,
        .disconnect_peripheral =
            BluetoothLeRadioRust::invokeDisconnectPeripheral,
        .write_to_peripheral = BluetoothLeRadioRust::invokeWriteToPeripheral,
        .read_l2cap_from_peripheral =
            BluetoothLeRadioRust::invokeReadL2capFromPeripheral,
        .send_l2cap_to_peripheral =
            BluetoothLeRadioRust::invokeSendL2capToPeripheral};
    BleServerCallbacks_t server_cb = {
        .start_advertising = BluetoothLeRadioRust::invokeStartAdvertising,
        .stop_advertising = BluetoothLeRadioRust::invokeStopAdvertising,
        .advertising_state = BluetoothLeRadioRust::invokeGetAdvertisingState,
        .notify_to_central = BluetoothLeRadioRust::invokeNotifyToCentral,
        .read_l2cap_from_central =
            BluetoothLeRadioRust::invokeReadL2capFromCentral,
        .send_l2cap_to_central =
            BluetoothLeRadioRust::invokeSendL2capToCentral};
    auto handle = ditto_add_ble_transport(
        ditto, client_cb, this, server_cb, this, BluetoothLeRadioRust::invokeRetain,
        BluetoothLeRadioRust::invokeRelease);
    atomic_store(&transportHandle,
                 std::shared_ptr<TransportHandle_BlePlatformEvent_t>(
                     handle, [](TransportHandle_BlePlatformEvent_t *h) {
                       ditto_ble_transport_free_handle(h);
                     }));
  }

  virtual void dropBleTransport() {
    // This is how we break the circular ownership when it is time to shut down
    // the transport
    atomic_store(&transportHandle,
                 std::shared_ptr<TransportHandle_BlePlatformEvent_t>());
  }
  virtual void advertisementHeard(const char peripheralUuid[], size_t uuidLen,
                                  const char manufacturerData[],
                                  size_t manufacturerDataLen,
                                  bool manufacturerDataIncludesId, char *name,
                                  float rssi) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ditto_transports_ble_advertisement_heard(
          handle.get(), (uint8_16_array_t const *)peripheralUuid,
          slice_ref_uint8_t{
              .ptr = (uint8_t const *)manufacturerData,
              .len = manufacturerDataLen,
          },
          manufacturerDataIncludesId,
          slice_ref_uint8_t{
              .ptr = (uint8_t const *)name,
              .len = name ? strlen(name) : 0,
          },
          rssi);
    }
    (void)uuidLen;
  }
  virtual void connectionStateChanged(const char peripheralUuid[],
                                      size_t uuidLen, ConnectState_t state,
                                      bool l2cap_available, int mtu) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_connection_state_changed(handle.get(),
                                   (uint8_16_array_t const *)peripheralUuid,
                                   state, l2cap_available ? 1 : 0, mtu);
    }
    (void)uuidLen;
  }
  virtual void peripheralMtuUpdated(const char uuid[], size_t uuidLen,
                                    int mtu) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_peripheral_mtu_updated(handle.get(), (uint8_16_array_t const *)uuid,
                                 mtu);
    }
    (void)uuidLen;
  }
  virtual void receivedFromPeripheral(const char peripheralUuid[],
                                      size_t uuidLen, BleDataType_t dataType,
                                      const char data[], size_t len) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_received_from_peripheral(
          handle.get(), (uint8_16_array_t const *)peripheralUuid, dataType,
          (slice_ref_uint8_t){
              .ptr = (uint8_t const *)data,
              .len = len,
          });
    }
    (void)uuidLen;
  }
  virtual void scanningStateChanged(OnlineState_t state,
                                    TransportCondition_t result) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_scanning_state_changed(handle.get(), state, result);
    }
  }
  virtual void peripheralReadyToSend(const char uuid[], size_t uuidLen) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_peripheral_ready_to_send(handle.get(),
                                   (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  virtual void peripheralL2capReadyToSend(const char uuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_peripheral_l2cap_ready_to_send(handle.get(),
                                         (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  virtual void peripheralL2capDataAvailable(const char uuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_peripheral_l2cap_data_available(handle.get(),
                                          (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  virtual void advertisingStateChanged(OnlineState_t state,
                                       TransportCondition_t result) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_advertising_state_changed(handle.get(), state, result);
    }
  }
  virtual void centralMtuUpdated(const char uuid[], size_t uuidLen, int mtu) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_central_mtu_updated(handle.get(), (uint8_16_array_t const *)uuid,
                              mtu);
    }
    (void)uuidLen;
  }
  virtual void receivedFromCentral(const char centralUuid[], size_t uuidLen,
                                   BleDataType_t dataType, const char data[],
                                   size_t len) {
    auto handle = atomic_load(&transportHandle);
    assert(uuidLen == 16);
    if (handle) {
      ble_received_from_central(handle.get(),
                                (uint8_16_array_t const *)centralUuid, dataType,
                                (slice_ref_uint8_t){
                                    .ptr = (uint8_t const *)data,
                                    .len = len,
                                });
    }
    (void)uuidLen;
  }
  virtual void centralFinishedConnecting(const char centralUuid[],
                                         size_t uuidLen, const char data[],
                                         size_t len, bool l2capAvailable,
                                         int mtu) {
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_central_finished_connecting(handle.get(),
                                      (uint8_16_array_t const *)centralUuid,
                                      (slice_ref_uint8_t){
                                          .ptr = (uint8_t const *)data,
                                          .len = len,
                                      },
                                      l2capAvailable ? 1 : 0, mtu);
    }
  }
  virtual void centralUnsubscribed(const char centralUuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_central_unsubscribed(handle.get(),
                               (uint8_16_array_t const *)centralUuid);
    }
  }
  virtual void centralReadyToSend(const char uuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_central_ready_to_send(handle.get(), (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  virtual void centralL2capReadyToSend(const char uuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_central_l2cap_ready_to_send(handle.get(),
                                      (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  virtual void centralL2capDataAvailable(const char uuid[], size_t uuidLen) {
    assert(uuidLen == 16);
    auto handle = atomic_load(&transportHandle);
    if (handle) {
      ble_central_l2cap_data_available(handle.get(),
                                       (uint8_16_array_t const *)uuid);
    }
    (void)uuidLen;
  }
  std::shared_ptr<TransportHandle_BlePlatformEvent_t> transportHandle;
};

#ifdef SWIG
%feature("director", assumeoverride=1) StatusRust;
#endif
class StatusRust : public Retainable {
public:
  virtual ~StatusRust() {}
  virtual void transportConditionDidChange(ConditionSource source,
                                           TransportCondition_t condition) = 0;

  static void
  invokeTransportConditionDidChange(void *ctx, ConditionSource source,
                                    TransportCondition_t condition) {
    auto inst = static_cast<StatusRust *>(ctx);
    return inst->transportConditionDidChange(source, condition);
  }

  virtual void submit(struct CDitto *ditto) {
    ditto_register_transport_condition_changed_callback(
        ditto, this, StatusRust::invokeRetain, StatusRust::invokeRelease,
        StatusRust::invokeTransportConditionDidChange);
  }

  virtual void shutdown(struct CDitto *ditto) {
    ditto_register_transport_condition_changed_callback(ditto, nullptr, nullptr,
                                                        nullptr, nullptr);
  }
};

#ifdef SWIG
%feature("director", assumeoverride=1) LoginProviderRust;
#endif
class LoginProviderRust : public Retainable {
public:
  virtual ~LoginProviderRust() {}
  virtual void authenticationExpiring(unsigned int time_remaining) = 0;

  static void invokeAuthenticationExpiring(void *ctx,
                                           unsigned int time_remaining) {
    auto inst = static_cast<LoginProviderRust *>(ctx);
    return inst->authenticationExpiring(time_remaining);
  }

  virtual void submit() {
    this->loginProviderPtr = ditto_auth_client_make_login_provider(
        this, LoginProviderRust::invokeRetain, LoginProviderRust::invokeRelease,
        LoginProviderRust::invokeAuthenticationExpiring);
  }

  CLoginProvider *loginProviderPtr;
};

#ifdef SWIG
%feature("director", assumeoverride=1) AuthenticationStatusHandlerRust;
#endif
class AuthenticationStatusHandlerRust : public Retainable {
public:
  virtual ~AuthenticationStatusHandlerRust() {}
  virtual void authenticationStatusUpdated(dittoffi_authentication_status_t *ffi_auth_status) = 0;

  static void invokeAuthenticationStatusUpdated(void *ctx, dittoffi_authentication_status_t *ffi_auth_status) {
    auto inst = static_cast<AuthenticationStatusHandlerRust *>(ctx);
    return inst->authenticationStatusUpdated(ffi_auth_status);
  }

  virtual void submit(struct CDitto *ditto) {
    AuthenticationStatusHandlerRust *ctx = this;
    ctx->java_retain();

    dittoffi_authentication_status_handler_t ffi_auth_status_handler = {
      .env_ptr = ctx,
      .call = AuthenticationStatusHandlerRust::invokeAuthenticationStatusUpdated,
      .free = AuthenticationStatusHandlerRust::invokeRelease,
    };

    dittoffi_ditto_set_authentication_status_handler(ditto, ffi_auth_status_handler);
  }
};

#ifdef SWIG
%feature("director", assumeoverride=1) DiskUsageObserverRust;
#endif
class DiskUsageObserverRust : public Retainable {
private:
  DiskUsageObserver *obplatformHandle;

public:
  virtual ~DiskUsageObserverRust() {}
  virtual void diskUsageUpdated(slice_ref_uint8_t diskUsageBytes) = 0;

  static void invokeDiskUsageUpdated(void *ctx,
                                     slice_ref_uint8_t diskUsageBytes) {
    auto inst = static_cast<DiskUsageObserverRust *>(ctx);
    return inst->diskUsageUpdated(diskUsageBytes);
  }

  virtual void subscribe(struct CDitto *ditto) {
    obplatformHandle = ditto_register_disk_usage_callback(
        ditto, FsComponent::FS_COMPONENT_ROOT, this,
        DiskUsageObserverRust::invokeRetain,
        DiskUsageObserverRust::invokeRelease,
        DiskUsageObserverRust::invokeDiskUsageUpdated);
  }

  virtual void stop() { ditto_release_disk_usage_callback(obplatformHandle); }
};

#ifdef SWIG
%apply(char *STRING, size_t LENGTH) { (const char value[], size_t valueLen) };
%extend slice_mut_uint8 {
	// The maximum number of bytes that can be written to this slice. Any larger values will be truncated.
	size_t getCapacity() {
		return $self->len;
	}

	// Write a byte array to the mutable memory represented by this slice. Returns the actual number of bytes written.
	size_t setValue(const char value[], size_t valueLen) {
		size_t len = (valueLen < $self->len ? valueLen : $self->len);
		memcpy($self->ptr, value, len);
		return len;
	}
}
#endif
