package com.reactnativezoom.videosdk;

import android.app.Service;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSDKCRCCallStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSDKErrors;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSDKRecordingConsentType;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkChatMessageDeleteType;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkChatPrivilegeType;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkLiveStreamStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkLiveTranscriptionOperationType;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkLiveTranscriptionStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkMultiCameraStreamStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkNetworkStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkPhoneFailedReason;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkPhoneStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkRawDataMemoryMode;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkRecordingStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkShareStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkTestMicStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkVideoSubscribeFailReason;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkCameraControlRequestType;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkFileTransferStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkUVCCameraStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSDKSessionLeaveReason;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkShareAction;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkSubSessionStatus;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkUserHelpRequestResult;
import com.reactnativezoom.videosdk.convert.RNZoomVideoSdkExportFormat;

import java.util.List;

import us.zoom.sdk.RealTimeMediaStreamsFailReason;
import us.zoom.sdk.RealTimeMediaStreamsStatus;
import us.zoom.sdk.ZoomVideoSDK;
import us.zoom.sdk.ZoomVideoSDKAnnotationHelper;
import us.zoom.sdk.ZoomVideoSDKAudioHelper;
import us.zoom.sdk.ZoomVideoSDKAudioOption;
import us.zoom.sdk.ZoomVideoSDKAudioRawData;
import us.zoom.sdk.ZoomVideoSDKBroadcastControlStatus;
import us.zoom.sdk.ZoomVideoSDKCRCCallStatus;
import us.zoom.sdk.ZoomVideoSDKChatHelper;
import us.zoom.sdk.ZoomVideoSDKChatMessage;
import us.zoom.sdk.ZoomVideoSDKChatMessageDeleteType;
import us.zoom.sdk.ZoomVideoSDKChatPrivilegeType;
import us.zoom.sdk.ZoomVideoSDKDataType;
import us.zoom.sdk.ZoomVideoSDKDelegate;
import us.zoom.sdk.ZoomVideoSDKErrors;
import us.zoom.sdk.ZoomVideoSDKExportFormat;
import us.zoom.sdk.ZoomVideoSDKExtendParams;
import us.zoom.sdk.ZoomVideoSDKInitParams;
import us.zoom.sdk.ZoomVideoSDKLiveStreamHelper;
import us.zoom.sdk.ZoomVideoSDKLiveStreamStatus;
import us.zoom.sdk.ZoomVideoSDKLiveTranscriptionHelper;
import us.zoom.sdk.ZoomVideoSDKLiveTranscriptionHelper.ILiveTranscriptionLanguage;
import us.zoom.sdk.ZoomVideoSDKLiveTranscriptionHelper.ZoomVideoSDKLiveTranscriptionOperationType;
import us.zoom.sdk.ZoomVideoSDKLiveTranscriptionHelper.ZoomVideoSDKLiveTranscriptionStatus;
import us.zoom.sdk.ZoomVideoSDKMultiCameraStreamStatus;
import us.zoom.sdk.ZoomVideoSDKNetworkStatus;
import us.zoom.sdk.ZoomVideoSDKPasswordHandler;
import us.zoom.sdk.ZoomVideoSDKPhoneFailedReason;
import us.zoom.sdk.ZoomVideoSDKPhoneStatus;
import us.zoom.sdk.ZoomVideoSDKProxySettingHandler;
import us.zoom.sdk.ZoomVideoSDKRawDataPipe;
import us.zoom.sdk.ZoomVideoSDKRecordingConsentHandler;
import us.zoom.sdk.ZoomVideoSDKRecordingConsentHandler.ConsentType;
import us.zoom.sdk.ZoomVideoSDKRecordingStatus;
import us.zoom.sdk.ZoomVideoSDKSSLCertificateInfo;
import us.zoom.sdk.ZoomVideoSDKSession;
import us.zoom.sdk.ZoomVideoSDKSessionContext;
import us.zoom.sdk.ZoomVideoSDKShareHelper;
import us.zoom.sdk.ZoomVideoSDKShareStatus;
import us.zoom.sdk.ZoomVideoSDKStreamingJoinStatus;
import us.zoom.sdk.ZoomVideoSDKTestMicStatus;
import us.zoom.sdk.ZoomVideoSDKUser;
import us.zoom.sdk.ZoomVideoSDKUserHelper;
import us.zoom.sdk.ZoomVideoSDKVideoCanvas;
import us.zoom.sdk.ZoomVideoSDKVideoHelper;
import us.zoom.sdk.ZoomVideoSDKVideoOption;
import us.zoom.sdk.ZoomVideoSDKVideoSubscribeFailReason;
import us.zoom.sdk.ZoomVideoSDKVideoView;
import us.zoom.sdk.ZoomVideoSDKCameraControlRequestType;
import us.zoom.sdk.ZoomVideoSDKCameraControlRequestHandler;
import us.zoom.sdk.ZoomVideoSDKSendFile;
import us.zoom.sdk.ZoomVideoSDKFileTransferStatus;
import us.zoom.sdk.ZoomVideoSDKReceiveFile;
import us.zoom.sdk.ZoomVideoSDKFileTransferStatus;
import us.zoom.sdk.UVCCameraStatus;
import us.zoom.sdk.ZoomVideoSDKSessionLeaveReason;
import us.zoom.sdk.ZoomVideoSDKShareAction;
import us.zoom.sdk.IncomingLiveStreamStatus;
import us.zoom.sdk.SubSessionKit;
import us.zoom.sdk.ZoomVideoSDKSubSessionManager;
import us.zoom.sdk.ZoomVideoSDKSubSessionParticipant;
import us.zoom.sdk.SubSessionUserHelpRequestHandler;
import us.zoom.sdk.ZoomVideoSDKUserHelpRequestResult;
import us.zoom.sdk.ZoomVideoSDKSubSessionStatus;
import us.zoom.sdk.ZoomVideoSDKShareSetting;
import us.zoom.sdk.ZoomVideoSDKWhiteboardHelper;
import us.zoom.sdk.ZoomVideoSDKAnnotationToolType;
import us.zoom.sdk.ZoomVideoSDKQOSStatistics;

public class RNZoomVideoSdkModule extends ReactContextBaseJavaModule implements ZoomVideoSDKDelegate, LifecycleEventListener {

  private final String DEBUG_TAG = "ZoomVideoSdkDebug";
  private final ReactApplicationContext reactContext;
  private ZoomVideoSDKRecordingConsentHandler recordingConsentHandler;

  protected Display display;
  protected DisplayMetrics displayMetrics;
  private int lastRotation = -1;

  RNZoomVideoSdkModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;
    reactContext.addLifecycleEventListener(this);

    display = ((WindowManager) reactContext.getSystemService(Service.WINDOW_SERVICE)).getDefaultDisplay();
    displayMetrics = new DisplayMetrics();
    display.getMetrics(displayMetrics);
  }

  @Override
  public String getName() {
    return "RNZoomVideoSdk";
  }

  public String[] supportedEvents() {
    return new String[] {
      "onSessionJoin",
      "onSessionLeave",
      "onUserJoin",
      "onUserLeave",
      "onUserVideoStatusChanged",
      "onUserAudioStatusChanged",
      "onLiveStreamStatusChanged",
      "onUserShareStatusChanged",
      "onChatNewMessageNotify",
      "onUserNameChanged",
      "onUserHostChanged",
      "onUserManagerChanged",
      "onUserActiveAudioChanged",
      "onSessionNeedPassword",
      "onSessionPasswordWrong",
      "onError",
      "onCmdChannelConnectResult",
      "onCommandReceived",
      "onCloudRecordingStatus",
      "onHostAskUnmute",
      "onInviteByPhoneStatus",
      "onLiveTranscriptionStatus",
      "onLiveTranscriptionMsgError",
      "onChatDeleteMessageNotify",
      "onMultiCameraStreamStatusChanged",
      "onUserVideoNetworkStatusChanged",
      "onCameraControlRequestResult",
      "onUserRecordingConsent",
      "onLiveTranscriptionMsgInfoReceived",
      "onCallCRCDeviceStatusChanged",
      "onOriginalLanguageMsgReceived",
      "onVideoCanvasSubscribeFail",
      "onShareCanvasSubscribeFail",
      "onMicSpeakerVolumeChanged",
      "onTestMicStatusChanged",
      "onCalloutJoinSuccess",
      "onCameraControlRequestReceived",
      "onSendFileStatus",
      "onReceiveFileStatus",
      "onUVCCameraStatusChange",
      "onVideoAlphaChannelStatusChanged",
      "onSpotlightVideoChanged",
      "onShareContentSizeChanged",
      "onShareContentChanged",
      "onSubSessionStatusChanged",
      "onSubSessionManagerHandle",
      "onSubSessionParticipantHandle",
      "onSubSessionUsersUpdate",
      "onBroadcastMessageFromMainSession",
      "onSubSessionUserHelpRequest",
      "onSubSessionUserHelpRequestResult",
      "onUserWhiteboardShareStatusChanged",
      "onWhiteboardExported",
      "onStartBroadcastResponse",
      "onStopBroadcastResponse",
      "onGetBroadcastControlStatus",
      "onStreamingJoinStatusChanged"
    };
  }

  @ReactMethod
  public void initSdk(ReadableMap config, Promise promise) {
    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        ZoomVideoSDKInitParams params = new ZoomVideoSDKInitParams();
        params.domain = config.getString("domain");
        params.enableLog = config.getBoolean("enableLog");

        if (config.hasKey("logFilePrefix")) {
          params.logFilePrefix = config.getString("logFilePrefix");
        }

        if (config.hasKey("enableFullHD")) {
          params.enableFullHD = config.getBoolean("enableFullHD");
        }

        if (config.hasKey("videoRawDataMemoryMode")) {
          params.videoRawDataMemoryMode = RNZoomVideoSdkRawDataMemoryMode.valueOf(config.getString("videoRawDataMemoryMode"));
        }

        if (config.hasKey("audioRawDataMemoryMode")) {
          params.audioRawDataMemoryMode = RNZoomVideoSdkRawDataMemoryMode.valueOf(config.getString(""));
        }

        if (config.hasKey("shareRawDataMemoryMode")) {
          params.shareRawDataMemoryMode = RNZoomVideoSdkRawDataMemoryMode.valueOf(config.getString("shareRawDataMemoryMode"));
        }
        ZoomVideoSDKExtendParams extendParams = new ZoomVideoSDKExtendParams();

        if (config.hasKey("speakerFilePath")) {
          String speakerFilePath = config.getString("speakerFilePath");
          extendParams.speakerTestFilePath = speakerFilePath;
        }
        extendParams.wrapperType = 2;
        params.extendParam = extendParams;

        ZoomVideoSDK sdk = ZoomVideoSDK.getInstance();
        int initResult = sdk.initialize(reactContext.getCurrentActivity(), params);

        switch (initResult) {
          case ZoomVideoSDKErrors.Errors_Success:
            Log.d(DEBUG_TAG, "SDK initialized successfully");
            sdk.addListener(RNZoomVideoSdkModule.this);
            promise.resolve("SDK initialized successfully");
            break;
          default:
            Log.d(DEBUG_TAG, String.format("SDK failed to initialize with error code: %d", initResult));
            promise.reject("sdkinit_failed", RNZoomVideoSDKErrors.valueOf(initResult), (WritableMap) null);
            break;
        }

        refreshRotationIfNeeded();
      }
    });
  }

  @ReactMethod
  public void joinSession(ReadableMap config, Promise promise) {
    ReadableMap audioOptionConfig = config.getMap("audioOptions");
    ZoomVideoSDKAudioOption audioOption = new ZoomVideoSDKAudioOption();
    if (audioOptionConfig != null) {
      audioOption.connect = audioOptionConfig.getBoolean("connect");
      audioOption.mute = audioOptionConfig.getBoolean("mute");
      audioOption.autoAdjustSpeakerVolume = audioOptionConfig.getBoolean("autoAdjustSpeakerVolume");
    }
    ZoomVideoSDKVideoOption videoOption = new ZoomVideoSDKVideoOption();
    ReadableMap videoOptionConfig = config.getMap("videoOptions");
    if (videoOptionConfig != null) {
      videoOption.localVideoOn = videoOptionConfig.getBoolean("localVideoOn");
    }

    ZoomVideoSDKSessionContext sessionContext = new ZoomVideoSDKSessionContext();
    sessionContext.sessionName = config.getString("sessionName");
    sessionContext.userName = config.getString("userName");
    sessionContext.token = config.getString("token");
    sessionContext.sessionPassword = config.getString("sessionPassword");
    sessionContext.audioOption = audioOption;
    sessionContext.videoOption = videoOption;
    sessionContext.sessionIdleTimeoutMins = config.getInt("sessionIdleTimeoutMins");

    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        try {
          ZoomVideoSDKSession session = ZoomVideoSDK.getInstance().joinSession(sessionContext);
          promise.resolve(null);
        } catch(Exception e) {
          promise.reject("joinSession_failure", "Join Session failed", (WritableMap) null);
        }
      }
    });
  }

  @ReactMethod
  public void leaveSession(boolean shouldEndSession, Promise promise) {
    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        promise.resolve(RNZoomVideoSDKErrors.valueOf(ZoomVideoSDK.getInstance().leaveSession(shouldEndSession)));
      }
    });
  }

  @ReactMethod
  public void getSdkVersion(Promise promise) {
    promise.resolve(ZoomVideoSDK.getInstance().getSDKVersion());
  }

  @ReactMethod
  public void isInSession(Promise promise) {
    promise.resolve(ZoomVideoSDK.getInstance().isInSession());
  }

  @ReactMethod
  public void cleanup(Promise promise) {
    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        int ret = ZoomVideoSDK.getInstance().cleanup();

        switch (ret) {
          case ZoomVideoSDKErrors.Errors_Success:
            Log.d(DEBUG_TAG, "SDK cleanup successfully");
            break;
          default:
            Log.d(DEBUG_TAG, String.format("SDK failed to cleanup with error code: %d", ret));
            break;
        }
      }
    });
  }

  @ReactMethod
  public void acceptRecordingConsent(Promise promise) {
    if (recordingConsentHandler != null) {
      promise.resolve(recordingConsentHandler.accept());
    } else {
      promise.resolve(false);
    }
  }

  @ReactMethod
  public void declineRecordingConsent(Promise promise) {
    if (recordingConsentHandler != null) {
      promise.resolve(recordingConsentHandler.decline());
    } else {
      promise.resolve(false);
    }
  }

  @ReactMethod
  public void getRecordingConsentType(Promise promise) {
    if (recordingConsentHandler != null) {
      promise.resolve(RNZoomVideoSDKRecordingConsentType.valueOf(recordingConsentHandler.getConsentType()));
    } else {
      promise.resolve(RNZoomVideoSDKRecordingConsentType.valueOf(ConsentType.ConsentType_Invalid));
    }
  }

  @ReactMethod
  public void exportLog(Promise promise) {
    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        promise.resolve(ZoomVideoSDK.getInstance().exportLog());
      }
    });
  }

  @ReactMethod
  public void cleanAllExportedLogs(Promise promise) {
    if (Utils.checkRNActivity(reactContext, promise)) {
      return;
    }
    reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
      @Override
      public void run() {
        int result = ZoomVideoSDK.getInstance().cleanAllExportedLogs();
        promise.resolve(RNZoomVideoSDKErrors.valueOf(result));
    }
    });
  }

  // -----------------------------------------------------------------------------------------------
  // region ZoomVideoSDKDelegate
  // -----------------------------------------------------------------------------------------------

  @Override
  public void onProxySettingNotification(ZoomVideoSDKProxySettingHandler handler) {
    Log.d(DEBUG_TAG, "onProxySettingNotification");
    WritableMap params = Arguments.createMap();
    params.putString("proxyHost", handler.getProxyHost());
    params.putInt("proxyPort", handler.getProxyPort());
    params.putString("proxyDescription", handler.getProxyDescription());
    sendEvent(reactContext, "onProxySettingNotification", params);
  }

  @Override
  public void onSSLCertVerifiedFailNotification(ZoomVideoSDKSSLCertificateInfo info){
    Log.d(DEBUG_TAG, "onSSLCertVerifiedFailNotification");
    WritableMap params = Arguments.createMap();
    params.putString("certIssuedTo", info.getCertIssuedTo());
    params.putString("certIssuedBy", info.getCertIssuedBy());
    params.putString("certSerialNum", info.getCertSerialNum());
    params.putString("certFingerprint", info.getCertFingerprint());
    sendEvent(reactContext, "onSSLCertVerifiedFailNotification", params);
  }

  @Override
  public void onSessionJoin() {
    WritableMap params = Arguments.createMap();
    ReadableMap mySelf = RNZoomVideoSdkUserModule.mapUser(ZoomVideoSDK.getInstance().getSession().getMySelf());
    params.putMap("mySelf", mySelf);
    sendEvent(reactContext, "onSessionJoin", params);
  }

  @SuppressWarnings("deprecation")
  @Override
  public void onSessionLeave() {
  }

  @Override
  public void onSessionLeave(ZoomVideoSDKSessionLeaveReason reason) {
    WritableMap params = Arguments.createMap();
    params.putString("reason", RNZoomVideoSDKSessionLeaveReason.valueOf(reason));
    sendEvent(reactContext, "onSessionLeave", params);
  }

  @Override
  public void onError(int i) {
    Log.d(DEBUG_TAG, "onError: code=" + i);
    WritableMap params = Arguments.createMap();
    params.putString("errorType", RNZoomVideoSDKErrors.valueOf(i));
    sendEvent(reactContext, "onError", params);
  }

  @Override
  public void onUserJoin(ZoomVideoSDKUserHelper userHelper, List<ZoomVideoSDKUser> userList) {
    List<ZoomVideoSDKUser> remoteUsers = ZoomVideoSDK.getInstance().getSession().getRemoteUsers();
    WritableMap params = Arguments.createMap();
    params.putArray("joinedUsers", RNZoomVideoSdkUserModule.mapUserArray(userList));
    params.putArray("remoteUsers", RNZoomVideoSdkUserModule.mapUserArray(remoteUsers));
    sendEvent(reactContext, "onUserJoin", params);
  }

  @Override
  public void onUserLeave(ZoomVideoSDKUserHelper userHelper, List<ZoomVideoSDKUser> userList) {
    List<ZoomVideoSDKUser> remoteUsers = ZoomVideoSDK.getInstance().getSession().getRemoteUsers();
    WritableMap params = Arguments.createMap();
    params.putArray("leftUsers", RNZoomVideoSdkUserModule.mapUserArray(userList));
    params.putArray("remoteUsers", RNZoomVideoSdkUserModule.mapUserArray(remoteUsers));
    sendEvent(reactContext, "onUserLeave", params);
  }

  @Override
  public void onUserVideoStatusChanged(ZoomVideoSDKVideoHelper videoHelper, List<ZoomVideoSDKUser> userList) {
    WritableMap params = Arguments.createMap();
    params.putArray("changedUsers", RNZoomVideoSdkUserModule.mapUserArray(userList));
    sendEvent(reactContext, "onUserVideoStatusChanged", params);
  }

  @Override
  public void onUserAudioStatusChanged(ZoomVideoSDKAudioHelper audioHelper, List<ZoomVideoSDKUser> userList) {
    WritableMap params = Arguments.createMap();
    params.putArray("changedUsers", RNZoomVideoSdkUserModule.mapUserArray(userList));
    sendEvent(reactContext, "onUserAudioStatusChanged", params);
  }

  @Override
  public void onUserShareStatusChanged(ZoomVideoSDKShareHelper shareHelper, ZoomVideoSDKUser user, ZoomVideoSDKShareStatus shareStatus) {
  }

  @Override
  public void onUserShareStatusChanged(ZoomVideoSDKShareHelper shareHelper, ZoomVideoSDKUser userInfo, ZoomVideoSDKShareAction shareAction) {
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(userInfo));
    params.putMap("shareAction", RNZoomVideoSdkShareAction.mapShareAction(shareAction));
    sendEvent(reactContext, "onUserShareStatusChanged", params);
  }

  @Override
  public void onLiveStreamStatusChanged(ZoomVideoSDKLiveStreamHelper liveStreamHelper, ZoomVideoSDKLiveStreamStatus liveStreamStatus) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkLiveStreamStatus.valueOf(liveStreamStatus));
    sendEvent(reactContext, "onLiveStreamStatusChanged", params);
  }

  @Override
  public void onChatNewMessageNotify(ZoomVideoSDKChatHelper chatHelper, ZoomVideoSDKChatMessage chatMessage) {
    String content = chatMessage.getContent();
    ZoomVideoSDKUser sender = chatMessage.getSenderUser();
    sendEvent(reactContext, "onChatNewMessageNotify", (WritableMap) RNZoomVideoSdkChatMessageModule.mapChatMessage(chatMessage));
  }

  @Override
  public void onUserNameChanged(ZoomVideoSDKUser user) {
    WritableMap params = Arguments.createMap();
    params.putMap("changedUser", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onUserNameChanged", params);
  }

  @Override
  public void onUserHostChanged(ZoomVideoSDKUserHelper userHelper, ZoomVideoSDKUser user) {
    WritableMap params = Arguments.createMap();
    if (user != null) {
      params.putMap("changedUser", RNZoomVideoSdkUserModule.mapUser(user));
    }
    sendEvent(reactContext, "onUserHostChanged", params);
  }

  @Override
  public void onUserManagerChanged(ZoomVideoSDKUser user) {
    WritableMap params = Arguments.createMap();
    params.putMap("changedUser", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onUserManagerChanged", params);
  }

  @Override
  public void onUserActiveAudioChanged(ZoomVideoSDKAudioHelper audioHelper, List<ZoomVideoSDKUser> userList) {
    WritableMap params = Arguments.createMap();
    params.putArray("changedUsers", RNZoomVideoSdkUserModule.mapUserArray(userList));
    sendEvent(reactContext, "onUserActiveAudioChanged", params);
  }

  @Override
  public void onSessionNeedPassword(ZoomVideoSDKPasswordHandler passwordHandler) {
    passwordHandler.leaveSessionIgnorePassword();
    sendEvent(reactContext, "onSessionNeedPassword", null);
  }

  @Override
  public void onSessionPasswordWrong(ZoomVideoSDKPasswordHandler passwordHandler) {
    passwordHandler.leaveSessionIgnorePassword();
    sendEvent(reactContext, "onSessionPasswordWrong", null);
  }

  @Override
  public void onMixedAudioRawDataReceived(ZoomVideoSDKAudioRawData audioRawData) {
    Log.d(DEBUG_TAG, "onMixedAudioRawDataReceived");
  }

  @Override
  public void onOneWayAudioRawDataReceived(ZoomVideoSDKAudioRawData audioRawData, ZoomVideoSDKUser user) {
    Log.d(DEBUG_TAG, "onOneWayAudioRawDataReceived");
  }

  @Override
  public void onShareAudioRawDataReceived(ZoomVideoSDKAudioRawData audioRawData) {
    Log.d(DEBUG_TAG, "onShareAudioRawDataReceived");
  }

  @Override
  public void onCommandReceived(ZoomVideoSDKUser sender, String strCmd) {
    Log.d(DEBUG_TAG, "onCommandReceived");

    WritableMap params = Arguments.createMap();
    params.putString("sender", sender.getUserID());
    params.putString("command", strCmd);
    sendEvent(reactContext, "onCommandReceived", params);
  }

  @Override
  public void onCommandChannelConnectResult(boolean isSuccess) {
    Log.d(DEBUG_TAG, "onCommandChannelConnectResult");

    WritableMap params = Arguments.createMap();
    params.putBoolean("success", isSuccess);
    sendEvent(reactContext, "onCommandChannelConnectResult", params);
  }

  @Override
  public void onCloudRecordingStatus(ZoomVideoSDKRecordingStatus status, ZoomVideoSDKRecordingConsentHandler handler) {
    if (handler != null) {
      Log.d(DEBUG_TAG, "onCloudRecordingStatus handler != null");
      recordingConsentHandler = handler;
    }
    Log.d(DEBUG_TAG, "onCloudRecordingStatus= " + RNZoomVideoSdkRecordingStatus.valueOf(status));
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkRecordingStatus.valueOf(status));
    sendEvent(reactContext, "onCloudRecordingStatus", params);

  }

  @Override
  public void onHostAskUnmute() {
    Log.d(DEBUG_TAG, "onHostAskUnmute");
  }

  @Override
  public void onInviteByPhoneStatus(ZoomVideoSDKPhoneStatus status, ZoomVideoSDKPhoneFailedReason reason) {
    Log.d(DEBUG_TAG, "onInviteByPhoneStatus, status: " + status + ", reason: " + reason);

    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkPhoneStatus.valueOf(status));
    params.putString("reason", RNZoomVideoSdkPhoneFailedReason.valueOf(reason));
    sendEvent(reactContext, "onInviteByPhoneStatus", params);
  }

  @Override
  public void onMultiCameraStreamStatusChanged(ZoomVideoSDKMultiCameraStreamStatus status, ZoomVideoSDKUser user, ZoomVideoSDKVideoCanvas canvas) {
    Log.d(DEBUG_TAG, "onMultiCameraStreamStatusChanged, VideoCanvas");

    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkMultiCameraStreamStatus.valueOf(status));
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onMultiCameraStreamStatusChanged", params);
  }

  @Override
  public void onMultiCameraStreamStatusChanged(ZoomVideoSDKMultiCameraStreamStatus status, ZoomVideoSDKUser user, ZoomVideoSDKRawDataPipe videoPipe) {
    Log.d(DEBUG_TAG, "onMultiCameraStreamStatusChanged, RawDataPipe");
  }

  @Override
  public void onChatDeleteMessageNotify(ZoomVideoSDKChatHelper chatHelper, String msgID, ZoomVideoSDKChatMessageDeleteType deleteBy) {
    Log.d(DEBUG_TAG, "onChatDeleteMessageNotify, msgID: " + msgID + ", deleteBy: " + deleteBy);

    WritableMap params = Arguments.createMap();
    params.putString("msgID", msgID);
    params.putString("deleteBy", RNZoomVideoSdkChatMessageDeleteType.valueOf(deleteBy));
    sendEvent(reactContext, "onChatDeleteMessageNotify", params);
  }

  @Override
  public void onLiveTranscriptionStatus(ZoomVideoSDKLiveTranscriptionStatus status) {
    Log.d(DEBUG_TAG, "onLiveTranscriptionStatus, status: " + status);

    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkLiveTranscriptionStatus.valueOf(status));
    sendEvent(reactContext, "onLiveTranscriptionStatus", params);
  }

  @Override
  public void onOriginalLanguageMsgReceived(ZoomVideoSDKLiveTranscriptionHelper.ILiveTranscriptionMessageInfo messageInfo) {
    WritableMap params = Arguments.createMap();
    params.putMap("messageInfo", RNZoomVideoSdkILiveTranscriptionMessageInfoModule.mapMessageInfo(messageInfo));
    sendEvent(reactContext, "onOriginalLanguageMsgReceived", params);
  }

  @Override
  public void onLiveTranscriptionMsgInfoReceived(ZoomVideoSDKLiveTranscriptionHelper.ILiveTranscriptionMessageInfo messageInfo) {
    WritableMap params = Arguments.createMap();
    params.putMap("messageInfo", RNZoomVideoSdkILiveTranscriptionMessageInfoModule.mapMessageInfo(messageInfo));
    sendEvent(reactContext, "onLiveTranscriptionMsgInfoReceived", params);
  }

  @Override
  public void onLiveTranscriptionMsgError(ILiveTranscriptionLanguage spokenLanguage, ILiveTranscriptionLanguage transcriptLanguage) {
    Log.d(DEBUG_TAG, "onLiveTranscriptionMsgError, spokenLanguage: " + spokenLanguage + ", transcriptLanguage: " + transcriptLanguage);

    WritableMap params = Arguments.createMap();
    params.putString("spokenLanguage", spokenLanguage.getLTTLanguageName());
    params.putString("transcriptLanguage", transcriptLanguage.getLTTLanguageName());
    sendEvent(reactContext, "onLiveTranscriptionMsgError", params);
  }

  @Override
  public void onCameraControlRequestResult(ZoomVideoSDKUser user, boolean isApproved) {
    Log.d(DEBUG_TAG, "onCameraControlRequestResult, approved: " + isApproved +
            ", user: " + user.getUserName());

    WritableMap params = Arguments.createMap();
    params.putBoolean("approved", isApproved);
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onCameraControlRequestResult", params);
  }

  @Override
  public void onUserVideoNetworkStatusChanged(ZoomVideoSDKNetworkStatus status, ZoomVideoSDKUser user) {
    Log.d(DEBUG_TAG, "onUserVideoNetworkStatusChanged, status: " + RNZoomVideoSdkNetworkStatus.valueOf(status) +
            ", user: " + user.getUserName());

    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkNetworkStatus.valueOf(status));
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onUserVideoNetworkStatusChanged", params);
  }

  @Override
  public void onUserRecordingConsent(ZoomVideoSDKUser user) {
    Log.d(DEBUG_TAG, "onUserRecordingConsent, user: " + user.getUserName());
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onUserRecordingConsent", params);
  }

  @Override
  public void onCallCRCDeviceStatusChanged(ZoomVideoSDKCRCCallStatus status) {
    Log.d(DEBUG_TAG, "onCallCRCDeviceStatusChanged, status: " + status);
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSDKCRCCallStatus.valueOf(status));
    sendEvent(reactContext, "onCallCRCDeviceStatusChanged", params);
  }
  @Override
  public void onChatPrivilegeChanged(ZoomVideoSDKChatHelper chatHelper, ZoomVideoSDKChatPrivilegeType currentPrivilege){
    Log.d(DEBUG_TAG, "onChatPrivilegeChanged, privilege: " + currentPrivilege);
    WritableMap params = Arguments.createMap();
    params.putString("privilege", RNZoomVideoSdkChatPrivilegeType.valueOf(currentPrivilege));
    sendEvent(reactContext, "onChatPrivilegeChanged", params);
  }

  @Override
  public void onAnnotationPrivilegeChange(ZoomVideoSDKUser shareOwner, ZoomVideoSDKShareAction shareAction){
    WritableMap params = Arguments.createMap();
    params.putMap("shareOwner", RNZoomVideoSdkUserModule.mapUser(shareOwner));
    params.putMap("shareAction", RNZoomVideoSdkShareAction.mapShareAction(shareAction));
    sendEvent(reactContext, "onAnnotationPrivilegeChange", params);

  }


  @Override
  public void onAnnotationHelperCleanUp(ZoomVideoSDKAnnotationHelper helper){
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onAnnotationHelperCleanUp", params);

  }

  @Override
  public void onVideoCanvasSubscribeFail(ZoomVideoSDKVideoSubscribeFailReason fail_reason, ZoomVideoSDKUser pUser, ZoomVideoSDKVideoView view) {
    WritableMap params = Arguments.createMap();
    params.putString("failReason", RNZoomVideoSdkVideoSubscribeFailReason.valueOf(fail_reason));
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(pUser));
    sendEvent(reactContext, "onVideoCanvasSubscribeFail", params);
  }

  @Override
  public void onShareCanvasSubscribeFail(ZoomVideoSDKVideoSubscribeFailReason fail_reason, ZoomVideoSDKUser pUser, ZoomVideoSDKVideoView view) {
  }

  @Override
  public void onShareCanvasSubscribeFail(ZoomVideoSDKUser pUser, ZoomVideoSDKVideoView view, ZoomVideoSDKShareAction shareAction) {
    WritableMap params = Arguments.createMap();
    params.putMap("shareAction", RNZoomVideoSdkShareAction.mapShareAction(shareAction));
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(pUser));
    sendEvent(reactContext, "onShareCanvasSubscribeFail", params);
  }

  @Override
  public void onMicSpeakerVolumeChanged(int micVolume, int speakerVolume) {
    WritableMap params = Arguments.createMap();
    params.putInt("micVolume", micVolume);
    params.putInt("speakerVolume", speakerVolume);
    sendEvent(reactContext, "onMicSpeakerVolumeChanged", params);
  }

  @Override
  public void onTestMicStatusChanged(ZoomVideoSDKTestMicStatus status) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkTestMicStatus.valueOf(status));
    sendEvent(reactContext, "onTestMicStatusChanged", params);
  }

  @Override
  public void onCalloutJoinSuccess(ZoomVideoSDKUser user, String phoneNumber) {
    WritableMap params = Arguments.createMap();
    params.putString("phoneNumber", phoneNumber);
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onCalloutJoinSuccess", params);
  }

  @Override
  public void onCameraControlRequestReceived(ZoomVideoSDKUser user, ZoomVideoSDKCameraControlRequestType requestType, ZoomVideoSDKCameraControlRequestHandler requestHandler) {
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    params.putString("requestType", RNZoomVideoSdkCameraControlRequestType.valueOf(requestType));
    //TODO: Add camera control handler in the next release
    sendEvent(reactContext, "onCameraControlRequestReceived", params);
  }

  @Override
  public void onSendFileStatus(ZoomVideoSDKSendFile file, ZoomVideoSDKFileTransferStatus status) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkFileTransferStatus.valueOf(status));
    //TODO: Add file handler in the next release
    sendEvent(reactContext, "onSendFileStatus", params);
  }

  @Override
  public void onReceiveFileStatus(ZoomVideoSDKReceiveFile file, ZoomVideoSDKFileTransferStatus status) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkFileTransferStatus.valueOf(status));
    //TODO: Add file handler in the next release
    sendEvent(reactContext, "onReceiveFileStatus", params);
  }

  @Override
  public void onUVCCameraStatusChange(String cameraId, UVCCameraStatus status) {
    WritableMap params = Arguments.createMap();
    params.putString("cameraId", cameraId);
    params.putString("status", RNZoomVideoSdkUVCCameraStatus.valueOf(status));
    sendEvent(reactContext, "onUVCCameraStatusChange", params);
  }

  @Override
  public void onVideoAlphaChannelStatusChanged(boolean isAlphaModeOn) {
    WritableMap params = Arguments.createMap();
    params.putBoolean("isAlphaModeOn", isAlphaModeOn);
    sendEvent(reactContext, "onVideoAlphaChannelStatusChanged", params);
  }

  @Override
  public void onSpotlightVideoChanged(ZoomVideoSDKVideoHelper videoHelper, List<ZoomVideoSDKUser> userList) {
    WritableMap params = Arguments.createMap();
    params.putArray("userList", RNZoomVideoSdkUserModule.mapUserArray(userList));
    sendEvent(reactContext, "onSpotlightVideoChanged", params);
  }

  @Override
  public void onFailedToStartShare(ZoomVideoSDKShareHelper shareHelper,  ZoomVideoSDKUser user) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onFailedToStartShare", params);
  }

  @Override
  public void onBindIncomingLiveStreamResponse(boolean bSuccess, String streamKeyID) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onBindIncomingLiveStreamResponse", params);
  }

  @Override
  public void onUnbindIncomingLiveStreamResponse(boolean bSuccess, String streamKeyID) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onUnbindIncomingLiveStreamResponse", params);
  }
  @Override
  public void onIncomingLiveStreamStatusResponse(boolean bSuccess, List<IncomingLiveStreamStatus> streamsStatusList) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onIncomingLiveStreamStatusResponse", params);
  }
  @Override
  public void onStartIncomingLiveStreamResponse(boolean bSuccess, String streamKeyID) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onStartIncomingLiveStreamResponse", params);
  }
  @Override
  public void onStopIncomingLiveStreamResponse(boolean bSuccess, String streamKeyID) {
    WritableMap params = Arguments.createMap();
    sendEvent(reactContext, "onStopIncomingLiveStreamResponse", params);
  }

  @Override
  public void onShareContentSizeChanged(ZoomVideoSDKShareHelper helper, ZoomVideoSDKUser user, ZoomVideoSDKShareAction action) {
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    params.putMap("shareAction", RNZoomVideoSdkShareAction.mapShareAction(action));
    sendEvent(reactContext, "onShareContentSizeChanged", params);
  }

  @Override
  public void onShareContentChanged(ZoomVideoSDKShareHelper helper, ZoomVideoSDKUser user, ZoomVideoSDKShareAction action) {
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    params.putMap("shareAction", RNZoomVideoSdkShareAction.mapShareAction(action));
    sendEvent(reactContext, "onShareContentChanged", params);
  }

  @Override
  public void onSubSessionStatusChanged(ZoomVideoSDKSubSessionStatus status, List<SubSessionKit> subSessionKitList) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkSubSessionStatus.valueOf(status));
    params.putArray("subSessionKitList", RNZoomVideoSdkSubSessionModule.mapSubSessionKitArray(subSessionKitList));
    sendEvent(reactContext, "onSubSessionStatusChanged", params);
  }
  @Override
  public void onSubSessionManagerHandle(ZoomVideoSDKSubSessionManager manager) {
    RNZoomVideoSdkSubSessionModule.storeSubSessionManager(manager);
    sendEvent(reactContext, "onSubSessionManagerHandle", null);
  }

  @Override
  public void onSubSessionParticipantHandle(ZoomVideoSDKSubSessionParticipant participant) {
    RNZoomVideoSdkSubSessionModule.storeSubSessionParticipant(participant);
    sendEvent(reactContext, "onSubSessionParticipantHandle", null);
  }

  @Override
  public void onSubSessionUsersUpdate(SubSessionKit subSessionKit) {
    WritableMap params = Arguments.createMap();
    params.putMap("subSessionKit", RNZoomVideoSdkSubSessionModule.mapSubSessionKit(subSessionKit));
    sendEvent(reactContext, "onSubSessionUsersUpdate", params);
  }

  @Override
  public void onBroadcastMessageFromMainSession(String message, String userName) {
    WritableMap params = Arguments.createMap();
    params.putString("message", message);
    params.putString("userName", userName);
    sendEvent(reactContext, "onBroadcastMessageFromMainSession", params);
  }

  @Override
  public void onSubSessionUserHelpRequest(SubSessionUserHelpRequestHandler handler) {
    RNZoomVideoSdkSubSessionModule.storeSubSessionUserHelpRequestHandler(handler);
    sendEvent(reactContext, "onSubSessionUserHelpRequest", null);
  }

  @Override
  public void onSubSessionUserHelpRequestResult(ZoomVideoSDKUserHelpRequestResult eResult) {
    WritableMap params = Arguments.createMap();
    params.putString("status", RNZoomVideoSdkUserHelpRequestResult.valueOf(eResult));
    sendEvent(reactContext, "onSubSessionUserHelpRequestResult", params);
  }

  @Override
  public void onShareSettingChanged(ZoomVideoSDKShareSetting shareSetting) {
    // Handle share setting change if needed
  }

  @Override
  public void onStartBroadcastResponse(boolean bSuccess, String channelID) {
    Log.d(DEBUG_TAG, "onStartBroadcastResponse: isSuccess=" + bSuccess + " channelID=" + channelID);
    WritableMap params = Arguments.createMap();
    params.putBoolean("isSuccess", bSuccess);
    params.putString("channelID", channelID);
    sendEvent(reactContext, "onStartBroadcastResponse", params);
  }

  @Override
  public void onStopBroadcastResponse(boolean bSuccess) {
    Log.d(DEBUG_TAG, "onStopBroadcastResponse: isSuccess=" + bSuccess);
    WritableMap params = Arguments.createMap();
    params.putBoolean("isSuccess", bSuccess);
    sendEvent(reactContext, "onStopBroadcastResponse", params);
  }

  @Override
  public void onGetBroadcastControlStatus(boolean bSuccess, ZoomVideoSDKBroadcastControlStatus status) {
    Log.d(DEBUG_TAG, "onGetBroadcastControlStatus: isSuccess=" + bSuccess + " status=" + status);
    WritableMap params = Arguments.createMap();
    params.putBoolean("isSuccess", bSuccess);
    params.putString("status", com.reactnativezoom.videosdk.convert.RNZoomVideoSdkBroadcastControlStatus.valueOf(status));
    sendEvent(reactContext, "onGetBroadcastControlStatus", params);
  }

  @Override
  public void onStreamingJoinStatusChanged(ZoomVideoSDKStreamingJoinStatus status) {
    Log.d(DEBUG_TAG, "onStreamingJoinStatusChanged: status=" + status);
    WritableMap params = Arguments.createMap();
    params.putString("status", com.reactnativezoom.videosdk.convert.RNZoomVideoSdkStreamingJoinStatus.valueOf(status));
    sendEvent(reactContext, "onStreamingJoinStatusChanged", params);
  }

  @Override
  public void onUserWhiteboardShareStatusChanged(ZoomVideoSDKUser user, ZoomVideoSDKWhiteboardHelper helper) {
    WritableMap params = Arguments.createMap();
    params.putMap("user", RNZoomVideoSdkUserModule.mapUser(user));
    sendEvent(reactContext, "onUserWhiteboardShareStatusChanged", params);
  }

  @Override
  public void onWhiteboardExported(ZoomVideoSDKExportFormat format, byte[] data) {
    if (data == null || data.length == 0) {
      Log.e(DEBUG_TAG, "onWhiteboardExported: data is null or empty");
      WritableMap params = Arguments.createMap();
      params.putString("format", RNZoomVideoSdkExportFormat.valueOf(format));
      params.putString("filePath", null);
      params.putString("error", "No data to save");
      sendEvent(reactContext, "onWhiteboardExported", params);
      return;
    }

    try {
      String fileExtension = ".pdf"; // Default to PDF
      if (format == ZoomVideoSDKExportFormat.EXPORT_FORMAT_PDF) {
        fileExtension = ".pdf";
      }

      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
      String timestamp = sdf.format(new Date());
      String fileName = "whiteboard_export_" + timestamp + fileExtension;

      File outputDir = reactContext.getExternalFilesDir(null);
      if (outputDir == null) {
        outputDir = reactContext.getCacheDir();
      }

      File outputFile = new File(outputDir, fileName);

      FileOutputStream fos = new FileOutputStream(outputFile);
      fos.write(data);
      fos.flush();
      fos.close();

      String filePath = outputFile.getAbsolutePath();
      Log.d(DEBUG_TAG, "Whiteboard exported to: " + filePath);

      WritableMap params = Arguments.createMap();
      params.putString("format", RNZoomVideoSdkExportFormat.valueOf(format));
      params.putString("filePath", filePath);
      sendEvent(reactContext, "onWhiteboardExported", params);

    } catch (IOException e) {
      Log.e(DEBUG_TAG, "Failed to save whiteboard export: " + e.getMessage(), e);
      WritableMap params = Arguments.createMap();
      params.putString("format", RNZoomVideoSdkExportFormat.valueOf(format));
      params.putString("filePath", null);
      params.putString("error", "Failed to save file: " + e.getMessage());
      sendEvent(reactContext, "onWhiteboardExported", params);
    } catch (Exception e) {
      Log.e(DEBUG_TAG, "Unexpected error saving whiteboard export: " + e.getMessage(), e);
      WritableMap params = Arguments.createMap();
      params.putString("format", RNZoomVideoSdkExportFormat.valueOf(format));
      params.putString("filePath", null);
      params.putString("error", "Unexpected error: " + e.getMessage());
      sendEvent(reactContext, "onWhiteboardExported", params);
    }
  }

  @Override
  public void onMyAudioSourceTypeChanged(ZoomVideoSDKAudioHelper.ZoomVideoSDKAudioDevice device) {

  }

  @Override
  public void onUserNetworkStatusChanged(ZoomVideoSDKDataType type, ZoomVideoSDKNetworkStatus level, ZoomVideoSDKUser user) {

  }

  @Override
  public void onUserOverallNetworkStatusChanged(ZoomVideoSDKNetworkStatus level, ZoomVideoSDKUser user) {

  }

  @Override
  public void onAudioLevelChanged(int level, boolean audioSharing, ZoomVideoSDKUser user) {

  }

  @Override
  public void onRealTimeMediaStreamsStatus(RealTimeMediaStreamsStatus status) {

  }

  @Override
  public void onRealTimeMediaStreamsFail(RealTimeMediaStreamsFailReason failReason) {

  }

  @Override
  public void onSpokenLanguageChanged(ILiveTranscriptionLanguage language) {
    // Handle spoken language change if needed
  }

  @Override
  public void onShareNetworkStatusChanged(ZoomVideoSDKNetworkStatus shareNetworkStatus, boolean isSendingShare) {

  }

  @Override
  public void onCanvasSnapshotIncompatible(ZoomVideoSDKUser user) {

  }

  @Override
  public void onCanvasSnapshotTaken(ZoomVideoSDKUser user, boolean isShare) {

  }

  @Override
  public void onAnnotationToolTypeChanged(ZoomVideoSDKAnnotationHelper helper, ZoomVideoSDKVideoView view, ZoomVideoSDKAnnotationToolType toolType) {

  }

  @Override
  public void onQOSStatisticsReceived(ZoomVideoSDKQOSStatistics statistics, ZoomVideoSDKUser user) {

  }

  @Override
  public void onVoiceInterpretationReady() {

  }


  // -----------------------------------------------------------------------------------------------
  // endregion
  // -----------------------------------------------------------------------------------------------

  // -----------------------------------------------------------------------------------------------
  // region LifecycleEventListener
  // -----------------------------------------------------------------------------------------------

  @Override
  public void onHostResume() {
    refreshRotationIfNeeded();
    Log.d(DEBUG_TAG, "onHostResume");
  }

  @Override
  public void onHostPause() {
    Log.d(DEBUG_TAG, "onHostPause");
  }

  @Override
  public void onHostDestroy() {
    Log.d(DEBUG_TAG, "onHostDestroy");
  }



  // -----------------------------------------------------------------------------------------------
  // endregion
  // -----------------------------------------------------------------------------------------------

  // -----------------------------------------------------------------------------------------------
  // region Helper Methods
  // -----------------------------------------------------------------------------------------------

  private void refreshRotationIfNeeded() {
    int rotation = display.getRotation();
    if (rotation != lastRotation) {
      lastRotation = rotation;
      if (ZoomVideoSDK.getInstance().getVideoHelper() != null) {
        ZoomVideoSDK.getInstance().getVideoHelper().rotateMyVideo(lastRotation);
      }
    }
  }

  private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
    reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, params);
  }

  // -----------------------------------------------------------------------------------------------
  // endregion
  // -----------------------------------------------------------------------------------------------

}
