import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'dart:convert';
import '../../tmg_sdk.dart';
import '../../tmg_type.dart';
import '../../tmg_audio_ctrl.dart';
import '../../tmg_audio_effect_ctrl.dart';
import '../../tmg_room.dart';
import '../../tmg_ptt.dart';
import '../../bindings/v2/tmg_sdk_bindings.dart' as TmgSdkBindings;
import '../../bindings/v2/tmg_audio_effect_ctrl_bindings.dart';
import 'tmg_audio_ctrl_impl.dart';
import 'tmg_audio_effect_ctrl_impl.dart';
import 'tmg_room_impl.dart';
import 'tmg_ptt_impl.dart';
import '../../tmg_error.dart';

typedef AVEnterRoomCompleteCallback_CSharp = Void Function(
    Int32 result, Pointer<Int8> error_info);

typedef AVExitRoomCompleteCallback_CSharp = Void Function();

typedef AVRoomDisconnect_CSharp = Void Function(
    Int32 result, Pointer<Int8> error_info);

typedef AVEndpointsUpdateInfo_CSharp = Void Function(
    Int32 eventID, Int32 count, Pointer<Pointer<Int8>> identifier);

typedef AVRoomChangeQualityCallback_CSharp = Void Function(
    Int32 nQualityEVA, Float fLostRate, Int32 nDealy);

typedef AVOnRoomEvent_CSharp = Void Function(Int32 roomType);

typedef AVOnEventCallback_CSharp = Void Function(
    Int32 type, Int32 subtype, Pointer<Int8> data);

typedef AVAccompnyCompleteCallBack_CSharp = Void Function(
    Int32 code, Int32 is_finished, Pointer<Int8> filepath);

typedef AVAccompnyCallBack_CSharp = Void Function(Int32 soundId);

class TMGContextImplV2 extends ITMGContext {
  late final TmgSdkBindings.TmgSdkBindings _instance;
  static final int EVENT_TYPE_NUMBER_OF_USERS_UPDATE = 7;
  static final int EVENT_TYPE_NUMBER_OF_AUDIOSTREAMS_UPDATE = 8;
  static final int EVENT_TYPE_CHANGE_ROOM_QUALITY = 1022;

  static ITMGDelegate? _onEventCallback;
  late TMGAudioCtrlImpl _audioCtrlInstance = new TMGAudioCtrlImpl();
  late TMGAudioEffectCtrlImpl _audioEffectCtrlInstance =
      new TMGAudioEffectCtrlImpl();
  late TMGRoomImpl _roomInstance = new TMGRoomImpl();
  late TMGPTTImpl _pttInstance = new TMGPTTImpl();

  TMGContextImplV2() {
    _instance = new TmgSdkBindings.TmgSdkBindings();
  }

  @override
  int SetTMGDelegate(ITMGDelegate onEventCallback) {
    _onEventCallback = onEventCallback;
    final onEnterRoomCompleteCallbackPointer =
        Pointer.fromFunction<AVEnterRoomCompleteCallback_CSharp>(
      OnEnterRoomCompleteCallback,
    );
    final onExitRoomCompleteCallbackPointer =
        Pointer.fromFunction<AVExitRoomCompleteCallback_CSharp>(
      OnExitRoomCompleteCallback,
    );
    final onRoomDisconnectCallbackPointer =
        Pointer.fromFunction<AVRoomDisconnect_CSharp>(
      OnRoomDisconnectCallback,
    );
    final onEndpointsUpdateInfoCallbackPointer =
        Pointer.fromFunction<AVEndpointsUpdateInfo_CSharp>(
      OnEndpointsUpdateInfoCallback,
    );
    final onRoomEventCallbackPointer =
        Pointer.fromFunction<AVOnRoomEvent_CSharp>(
      OnRoomEventCallback,
    );
    final onRoomChangeQualityCallbackPointer =
        Pointer.fromFunction<AVRoomChangeQualityCallback_CSharp>(
      OnRoomChangeQualityCallback,
    );
    final onEventCallbackPointer =
        Pointer.fromFunction<AVOnEventCallback_CSharp>(
      OnEventCallback,
    );

    final onAccompnyCompleteCallBackPointer =
        Pointer.fromFunction<AVAccompnyCompleteCallBack_CSharp>(
      OnAccompnyCompleteCallBack,
    );

    final onAccompnyStartCallBackPointer =
        Pointer.fromFunction<AVAccompnyCallBack_CSharp>(
      OnAccompnyStartCallBack,
    );

    final onAccompnyPauseCallBackPointer =
        Pointer.fromFunction<AVAccompnyCallBack_CSharp>(
      OnAccompnyPauseCallBack,
    );

    final onAccompnyResumeCallBackPointer =
        Pointer.fromFunction<AVAccompnyCallBack_CSharp>(
      OnAccompnyResumeCallBack,
    );

    TMGAudioEffectCtrlBindings.SetAccompanyFinishCallback(
        onAccompnyCompleteCallBackPointer,
        onAccompnyStartCallBackPointer,
        onAccompnyPauseCallBackPointer,
        onAccompnyResumeCallBackPointer);

    return _instance.SetDelegate(
        onEnterRoomCompleteCallbackPointer,
        onExitRoomCompleteCallbackPointer,
        onRoomDisconnectCallbackPointer,
        onEndpointsUpdateInfoCallbackPointer,
        onRoomEventCallbackPointer,
        onRoomChangeQualityCallbackPointer,
        onEventCallbackPointer);
  }

  @override
  void SetRegion(String region) {}

  @override
  int Init(String appId, String userID) {
    return _instance.Init(appId, userID);
  }

  @override
  void Poll() {
    _instance.Poll();
  }

  @override
  int Pause() {
    return _instance.Pause();
  }

  @override
  int Resume() {
    return _instance.Resume();
  }

  @override
  int Uninit() {
    return _instance.Uninit();
  }

  @override
  int SetScene(ITMG_APP_SCENE scene) {
    return TMGError.AV_ERR_NOT_IMPLEMENTED;
  }

  @override
  int SetAudioRole(ITMG_AUDIO_MEMBER_ROLE role) {
    return TMGError.AV_ERR_NOT_IMPLEMENTED;
  }

  @override
  int SetRangeAudioMode(ITMG_RANGE_AUDIO_MODE rangeAudioMode) {
    return TMGError.AV_ERR_NOT_IMPLEMENTED;
  }

  @override
  int SetRangeAudioTeamID(int teamID) {
    return TMGError.AV_ERR_NOT_IMPLEMENTED;
  }

  @override
  String GenAuthBuffer(int appId, String roomId, String userId, String key) {
    String result = _instance.GenAuthBuffer(appId, roomId, userId, key);
    return result;
  }

  @override
  int EnterRoom(String room_id, ITMGRoomType room_type, String user_sig) {
    return _instance.EnterRoom(room_id, room_type.value, user_sig);
  }

  @override
  int ExitRoom() {
    return _instance.ExitRoom();
  }

  @override
  bool IsRoomEntered() {
    return _instance.IsRoomEntered();
  }

  @override
  int SetLogLevel(int levelWrite, int levelPrint) {
    return _instance.SetLogLevel(levelWrite, levelPrint);
  }

  @override
  int SetLogPath(String logDir) {
    return _instance.SetLogPath(logDir);
  }

  @override
  String GetLogPath() {
    return "";
  }

  @override
  String GetSDKVersion() {
    return "";
  }

  @override
  void SetAppVersion(String appVersion) {}

  @override
  int ShowDebugView(bool show) {
    return TMGError.AV_ERR_NOT_IMPLEMENTED;
  }

  @override
  ITMGRoom GetRoom() {
    return _roomInstance;
  }

  @override
  ITMGPTT GetPTT() {
    return _pttInstance;
  }

  @override
  ITMGAudioCtrl GetAudioCtrl() {
    return _audioCtrlInstance;
  }

  @override
  ITMGAudioEffectCtrl GetAudioEffectCtrl() {
    return _audioEffectCtrlInstance;
  }

  @override
  int SetRecvMixStreamCount(int count) {
    return _instance.SetRecvMixStreamCount(count);
  }

  @override
  int SetAdvanceParams(String key, String value) {
    return _instance.SetAdvanceParams(key, value);
  }

  @override
  String GetAdvanceParams(String key) {
    return "";
  }

  @override
  ITMG_MIC_PERMISSION CheckMicPermission() {
    return ITMG_MIC_PERMISSION.ITMG_PERMISSION_GRANTED;
  }

  static void OnEnterRoomCompleteCallback(
      int result, Pointer<Int8> error_info) {
    if (_onEventCallback != null) {
      print("OnEnterRoomCompleteCallback: $result");
      Map<String, dynamic> jsonMap = {
        "result": result,
        "error_info": error_info.cast<Utf8>().toDartString()
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_ENTER_ROOM, jsonString);
    }
  }

  static void OnExitRoomCompleteCallback() {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {"result": 0, "error_info": ""};
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_EXIT_ROOM, jsonString);
    }
  }

  static void OnRoomDisconnectCallback(int result, Pointer<Int8> error_info) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "result": result,
        "error_info": error_info.cast<Utf8>().toDartString()
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_ROOM_DISCONNECT,
          jsonString);
    }
  }

  static void OnEndpointsUpdateInfoCallback(
      int eventID, int count, Pointer<Pointer<Int8>> identifier) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "event_id": eventID,
        "user_list": [],
      };

      for (int i = 0; i < count; i++) {
        jsonMap["user_list"].add(identifier[i].cast<Utf8>().toDartString());
      }
      String jsonString = jsonEncode(jsonMap);
      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_USER_UPDATE, jsonString);
    }
  }

  static void OnRoomEventCallback(int roomType) {
    return;
  }

  static void OnRoomChangeQualityCallback(
      int nQualityEVA, double fLostRate, int nDealy) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "weight": nQualityEVA,
        "loss": fLostRate,
        "delay": nDealy
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_NETWORK_QUALITY,
          jsonString);
    }
  }

  static void OnEventCallback(int type, int subtype, Pointer<Int8> data) {
    if (type == EVENT_TYPE_NUMBER_OF_USERS_UPDATE ||
        type == EVENT_TYPE_NUMBER_OF_AUDIOSTREAMS_UPDATE) {
      return;
    }
    if (_onEventCallback != null) {
      _onEventCallback?.onEvent(type, data.cast<Utf8>().toDartString());
    }
  }

  static void OnAccompnyCompleteCallBack(
      int code, int is_finished, Pointer<Int8> filepath) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "result": code,
        "sound_id": 0,
        "file_path": filepath.cast<Utf8>().toDartString(),
        "is_finished": (is_finished == 1) ? true : false
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_MUSIC_PLAY_FINISH,
          jsonString);
    }

    return;
  }

  static void OnAccompnyStartCallBack(int soundId) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "sound_id": soundId,
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_MUSIC_PLAY_START,
          jsonString);
    }

    return;
  }

  static void OnAccompnyPauseCallBack(int soundId) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "sound_id": soundId,
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_MUSIC_PLAY_PAUSE,
          jsonString);
    }

    return;
  }

  static void OnAccompnyResumeCallBack(int soundId) {
    if (_onEventCallback != null) {
      Map<String, dynamic> jsonMap = {
        "sound_id": soundId,
      };
      String jsonString = jsonEncode(jsonMap);

      _onEventCallback?.onEvent(
          ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_MUSIC_PLAY_RESUME,
          jsonString);
    }

    return;
  }
}
