import 'dart:ffi'; // 引入 FFI 库
import 'package:ffi/ffi.dart'; // 导入 ffi 扩展方法
import 'dart:typed_data';
import '../tmg_type.dart';
import 'tmg_load_dynamic.dart';

typedef GetRoomIDNative = Pointer<Int8> Function(Pointer<Void>);
typedef GetRoomIDDart = Pointer<Int8> Function(Pointer<Void>);

typedef ChangeRoomTypeNative = Int32 Function(Pointer<Void>, Int32);
typedef ChangeRoomTypeDart = int Function(Pointer<Void>, int);

typedef GetRoomTypeNative = Int32 Function(Pointer<Void>);
typedef GetRoomTypeDart = int Function(Pointer<Void>);

typedef StartRoomSharingNative =
    Int32 Function(Pointer<Void>, Pointer<Int8>, Pointer<Int8>);
typedef StartRoomSharingDart =
    int Function(Pointer<Void>, Pointer<Int8>, Pointer<Int8>);

typedef StopRoomSharingNative = Int32 Function(Pointer<Void>);
typedef StopRoomSharingDart = int Function(Pointer<Void>);

typedef SwitchRoomNative =
    Int32 Function(Pointer<Void>, Pointer<Int8>, Pointer<Int8>);
typedef SwitchRoomDart =
    int Function(Pointer<Void>, Pointer<Int8>, Pointer<Int8>);

typedef SendCustomDataNative =
    Int32 Function(Pointer<Void>, Pointer<Uint8>, Int32, Int32);
typedef SendCustomDataDart =
    int Function(Pointer<Void>, Pointer<Uint8>, int, int);

typedef StopSendCustomDataNative = Int32 Function(Pointer<Void>);
typedef StopSendCustomDataDart = int Function(Pointer<Void>);

typedef UpdateAudioRecvRangeNative = Int32 Function(Pointer<Void>, Float);
typedef UpdateAudioRecvRangeDart = int Function(Pointer<Void>, double);

typedef UpdateSpatializerRecvRangeNative = Int32 Function(Pointer<Void>, Float);
typedef UpdateSpatializerRecvRangeDart = int Function(Pointer<Void>, double);

typedef UpdateSelfPositionNative =
    Int32 Function(
      Pointer<Void>,
      Pointer<Float> position,
      Pointer<Float> axis_forward,
      Pointer<Float> axis_right,
      Pointer<Float> axis_up,
      Int32 len,
    );
typedef UpdateSelfPositionDart =
    int Function(
      Pointer<Void>,
      Pointer<Float> position,
      Pointer<Float> axis_forward,
      Pointer<Float> axis_right,
      Pointer<Float> axis_up,
      int len,
    );

typedef UpdateOtherPositionNative =
    Int32 Function(Pointer<Void>, Pointer<Int8>, Pointer<Float>, Int32 len);
typedef UpdateOtherPositionDart =
    int Function(Pointer<Void>, Pointer<Int8>, Pointer<Float>, int len);

typedef SendSEIMsgNative = Int32 Function(Pointer<Void>, Pointer<Int8>, Int32);
typedef SendSEIMsgDart = int Function(Pointer<Void>, Pointer<Int8>, int);

class TMGRoomBindings {
  late final Pointer<Void> _gmeInstance;
  TMGLoadDynamic _tmgDynamicInstance = TMGLoadDynamic.GetInstance();
  DynamicLibrary? _gmeSdkLib = null;

  TMGRoomBindings() {
    _gmeSdkLib = _tmgDynamicInstance.GetGmeSdkLib();
    _gmeInstance = _tmgDynamicInstance.GetGmeSdkInstance();
  }

  late final GetRoomIDDart GetRoomIDPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<GetRoomIDNative>>('GMEUnity_GetRoomID')
          .asFunction<GetRoomIDDart>();

  String GetRoomID() {
    return GetRoomIDPtr(_gmeInstance).cast<Utf8>().toDartString();
  }

  late final ChangeRoomTypeDart ChangeRoomTypePtr =
      _gmeSdkLib!
          .lookup<NativeFunction<ChangeRoomTypeNative>>(
            'GMEUnity_ChangeRoomType',
          )
          .asFunction<ChangeRoomTypeDart>();

  int ChangeRoomType(ITMGRoomType roomType) {
    return ChangeRoomTypePtr(_gmeInstance, roomType.value);
  }

  late final GetRoomTypeDart GetRoomTypePtr =
      _gmeSdkLib!
          .lookup<NativeFunction<GetRoomTypeNative>>('GMEUnity_GetRoomType')
          .asFunction<GetRoomTypeDart>();

  int GetRoomType() {
    return GetRoomTypePtr(_gmeInstance);
  }

  late final StartRoomSharingDart StartRoomSharingPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<StartRoomSharingNative>>(
            'GMEUnity_StartRoomSharing',
          )
          .asFunction<StartRoomSharingDart>();

  int StartRoomSharing(String targetRoomID, String targetUserID) {
    return StartRoomSharingPtr(
      _gmeInstance,
      targetRoomID.toNativeUtf8().cast<Int8>(),
      targetUserID.toNativeUtf8().cast<Int8>(),
    );
  }

  late final StopRoomSharingDart StopRoomSharingPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<StopRoomSharingNative>>(
            'GMEUnity_StopRoomSharing',
          )
          .asFunction<StopRoomSharingDart>();

  int StopRoomSharing() {
    return StopRoomSharingPtr(_gmeInstance);
  }

  late final SwitchRoomDart SwitchRoomPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<SwitchRoomNative>>('GMEUnity_SwitchRoom')
          .asFunction<SwitchRoomDart>();

  int SwitchRoom(String targetRoomID, String userSig) {
    return SwitchRoomPtr(
      _gmeInstance,
      targetRoomID.toNativeUtf8().cast<Int8>(),
      userSig.toNativeUtf8().cast<Int8>(),
    );
  }

  Pointer<Uint8> uint8ListToPointer(Uint8List list) {
    final pointer = malloc<Uint8>(list.length + 1);
    final nativeList = pointer.asTypedList(list.length + 1);
    nativeList.setAll(0, list);
    nativeList[list.length] = 0;
    return pointer;
  }

  late final SendCustomDataDart SendCustomDataPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<SendCustomDataNative>>(
            'GMEUnity_SendCustomData',
          )
          .asFunction<SendCustomDataDart>();

  int SendCustomData(Uint8List data, int repeatCount) {
    if (data.length == 0) {
      return 0;
    }

    final dataPointer = uint8ListToPointer(data);
    int ret = SendCustomDataPtr(
      _gmeInstance,
      dataPointer,
      data.length,
      repeatCount,
    );
    if (dataPointer != null) {
      malloc.free(dataPointer);
    }
    return ret;
  }

  late final StopSendCustomDataDart StopSendCustomDataPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<StopSendCustomDataNative>>(
            'GMEUnity_StopSendCustomData',
          )
          .asFunction<StopSendCustomDataDart>();

  int StopSendCustomData() {
    return StopSendCustomDataPtr(_gmeInstance);
  }

  late final UpdateAudioRecvRangeDart UpdateAudioRecvRangePtr =
      _gmeSdkLib!
          .lookup<NativeFunction<UpdateAudioRecvRangeNative>>(
            'GMEUnity_UpdateAudioRecvRange',
          )
          .asFunction<UpdateAudioRecvRangeDart>();

  int UpdateAudioRecvRange(double range) {
    return UpdateAudioRecvRangePtr(_gmeInstance, range);
  }

  late final UpdateSpatializerRecvRangeDart UpdateSpatializerRecvRangePtr =
      _gmeSdkLib!
          .lookup<NativeFunction<UpdateSpatializerRecvRangeNative>>(
            'GMEUnity_UpdateSpatializerRecvRange',
          )
          .asFunction<UpdateSpatializerRecvRangeDart>();

  int UpdateSpatializerRecvRange(double range) {
    return UpdateSpatializerRecvRangePtr(_gmeInstance, range);
  }

  Pointer<Float> toPointer(Float32List list) {
    final ptr = malloc<Float>(list.length);
    for (var i = 0; i < list.length; i++) {
      ptr[i] = list[i];
    }
    return ptr;
  }

  late final UpdateSelfPositionDart UpdateSelfPositionPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<UpdateSelfPositionNative>>(
            'GMEUnity_UpdateSelfPosition',
          )
          .asFunction<UpdateSelfPositionDart>();

  int UpdateSelfPosition(
    Float32List position,
    Float32List axisForward,
    Float32List axisRight,
    Float32List axisUp,
  ) {
    Pointer<Float> positionPtr = toPointer(position);
    Pointer<Float> axisForwardPtr = toPointer(axisForward);
    Pointer<Float> axisRightPtr = toPointer(axisRight);
    Pointer<Float> axisUpPtr = toPointer(axisUp);

    int ret = UpdateSelfPositionPtr(
      _gmeInstance,
      positionPtr,
      axisForwardPtr,
      axisRightPtr,
      axisUpPtr,
      position.length,
    );

    malloc.free(positionPtr);
    malloc.free(axisForwardPtr);
    malloc.free(axisRightPtr);
    malloc.free(axisUpPtr);

    return ret;
  }

  late final UpdateOtherPositionDart UpdateOtherPositionPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<UpdateOtherPositionNative>>(
            'GMEUnity_UpdateOtherPosition',
          )
          .asFunction<UpdateOtherPositionDart>();

  int UpdateOtherPosition(String userID, Float32List position) {
    Pointer<Float> positionPtr = toPointer(position);
    int ret = UpdateOtherPositionPtr(
      _gmeInstance,
      userID.toNativeUtf8().cast<Int8>(),
      positionPtr,
      0,
    );

    malloc.free(positionPtr);
    return ret;
  }

  late final SendSEIMsgDart SendSEIMsgPtr =
      _gmeSdkLib!
          .lookup<NativeFunction<SendSEIMsgNative>>('GMEUnity_SendSEIMsg')
          .asFunction<SendSEIMsgDart>();

  int SendSEIMsg(String message, int repeatCount) {
    return SendSEIMsgPtr(
      _gmeInstance,
      message.toNativeUtf8().cast<Int8>(),
      repeatCount,
    );
  }
}
