// Copyright © 2022 BytePlusRTC All rights reserved.
// SPDX-License-Identifier: MIT

package com.volcengine.reactnative.vertc.events;

import com.ss.bytertc.engine.SysStats;
import com.ss.bytertc.engine.data.AudioMixingError;
import com.ss.bytertc.engine.data.AudioMixingState;
import com.ss.bytertc.engine.data.AudioPlaybackDevice;
import com.ss.bytertc.engine.data.AudioRoute;
import com.ss.bytertc.engine.data.DataMessageSourceType;
import com.ss.bytertc.engine.data.DeadLockMsg;
import com.ss.bytertc.engine.data.FrameUpdateInfo;
import com.ss.bytertc.engine.data.LocalAudioPropertiesInfo;
import com.ss.bytertc.engine.data.LocalAudioStreamError;
import com.ss.bytertc.engine.data.LocalAudioStreamState;
import com.ss.bytertc.engine.data.MuteState;
import com.ss.bytertc.engine.data.RecordingInfo;
import com.ss.bytertc.engine.data.RecordingProgress;
import com.ss.bytertc.engine.data.RemoteAudioPropertiesInfo;
import com.ss.bytertc.engine.data.RemoteAudioState;
import com.ss.bytertc.engine.data.RemoteAudioStateChangeReason;
import com.ss.bytertc.engine.data.RemoteStreamKey;
import com.ss.bytertc.engine.data.StreamIndex;
import com.ss.bytertc.engine.data.StreamSycnInfoConfig;
import com.ss.bytertc.engine.data.VideoDenoiseMode;
import com.ss.bytertc.engine.data.VideoDenoiseModeChangedReason;
import com.ss.bytertc.engine.data.VideoFrameInfo;
import com.ss.bytertc.engine.data.VideoSuperResolutionMode;
import com.ss.bytertc.engine.data.VideoSuperResolutionModeChangedReason;
import com.ss.bytertc.engine.handler.IRTCVideoEventHandler;
import com.ss.bytertc.engine.type.AudioDeviceType;
import com.ss.bytertc.engine.type.AudioDumpStatus;
import com.ss.bytertc.engine.type.AudioRecordingErrorCode;
import com.ss.bytertc.engine.type.AudioRecordingState;
import com.ss.bytertc.engine.type.EchoTestResult;
import com.ss.bytertc.engine.type.EffectErrorType;
import com.ss.bytertc.engine.type.FirstFramePlayState;
import com.ss.bytertc.engine.type.FirstFrameSendState;
import com.ss.bytertc.engine.type.HardwareEchoDetectionResult;
import com.ss.bytertc.engine.type.LocalProxyError;
import com.ss.bytertc.engine.type.LocalProxyState;
import com.ss.bytertc.engine.type.LocalProxyType;
import com.ss.bytertc.engine.type.LocalVideoStreamError;
import com.ss.bytertc.engine.type.LocalVideoStreamState;
import com.ss.bytertc.engine.type.NetworkDetectionLinkType;
import com.ss.bytertc.engine.type.NetworkDetectionStopReason;
import com.ss.bytertc.engine.type.PerformanceAlarmMode;
import com.ss.bytertc.engine.type.PerformanceAlarmReason;
import com.ss.bytertc.engine.type.PublicStreamErrorCode;
import com.ss.bytertc.engine.type.RecordingErrorCode;
import com.ss.bytertc.engine.type.RecordingState;
import com.ss.bytertc.engine.type.RemoteStreamSwitch;
import com.ss.bytertc.engine.type.RemoteVideoState;
import com.ss.bytertc.engine.type.RemoteVideoStateChangeReason;
import com.ss.bytertc.engine.type.RenderError;
import com.ss.bytertc.engine.type.RtcUser;
import com.ss.bytertc.engine.type.SEIStreamUpdateEvent;
import com.ss.bytertc.engine.type.SourceWantedData;
import com.ss.bytertc.engine.type.VideoDeviceType;
import com.ss.bytertc.engine.utils.LogUtil;
import com.volcengine.VolcApiEngine.BeanFactory;

import org.json.JSONObject;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class IRTCVideoEventHandlerImpl extends IRTCVideoEventHandler implements BeanFactory.EventReceiver {
  public BeanFactory.EventEmitter ee;
  
  public IRTCVideoEventHandlerImpl(BeanFactory.EventEmitter ee) {
    this.ee = ee;
  }

  @Override
  public void onError(int err) {
    this.ee.sendEvent("onError", err);
  }

  @Override
  public void onDeadLockError(DeadLockMsg deadLockMsg) {
    this.ee.sendEvent("onDeadLockError", deadLockMsg);
  }

  @Override
  public void onExtensionAccessError(String extensionName, String msg) {
    this.ee.sendEvent("onExtensionAccessError", extensionName, msg);
  }

  @Override
  public void onLoggerMessage(LogUtil.LogLevel level, String msg, Throwable throwable) {
    this.ee.sendEvent("onLoggerMessage", level, msg, throwable);
  }

  @Override
  public void onWarning(int warn) {
    this.ee.sendEvent("onWarning", warn);
  }

  @Override
  public void onActiveSpeaker(String roomId, String uid) {
    this.ee.sendEvent("onActiveSpeaker", roomId, uid);
  }

  @Override
  public void onStreamSyncInfoReceived(RemoteStreamKey streamKey, StreamSycnInfoConfig.SyncInfoStreamType streamType, ByteBuffer data) {
    this.ee.sendEvent("onStreamSyncInfoReceived", streamKey, streamType, data);
  }

  @Override
  public void onPushPublicStreamResult(String roomId, String publicStreamId, PublicStreamErrorCode error) {
    this.ee.sendEvent("onPushPublicStreamResult", roomId, publicStreamId, error);
  }

  @Override
  public void onPlayPublicStreamResult(String publicStreamId, PublicStreamErrorCode error) {
    this.ee.sendEvent("onPlayPublicStreamResult", publicStreamId, error);
  }

  @Override
  public void onPublicStreamSEIMessageReceived(String publicStreamId, ByteBuffer message, DataMessageSourceType sourceType) {
    this.ee.sendEvent("onPublicStreamSEIMessageReceived", publicStreamId, message, sourceType);
  }

  @Override
  public void onPublicStreamSEIMessageReceivedWithChannel(String publicStreamId, int channelId, ByteBuffer message) {
    this.ee.sendEvent("onPublicStreamSEIMessageReceivedWithChannel", publicStreamId, channelId, message);
  }

  @Override
  public void onPublicStreamDataMessageReceived(String publicStreamId, ByteBuffer message, DataMessageSourceType sourceType) {
    this.ee.sendEvent("onPublicStreamDataMessageReceived", publicStreamId, message, sourceType);
  }

  @Override
  public void onUserStartAudioCapture(String roomId, String uid) {
    this.ee.sendEvent("onUserStartAudioCapture", roomId, uid);
  }

  @Override
  public void onUserStopAudioCapture(String roomId, String uid) {
    this.ee.sendEvent("onUserStopAudioCapture", roomId, uid);
  }

  @Override
  public void onLocalAudioStateChanged(LocalAudioStreamState state, LocalAudioStreamError error) {
    this.ee.sendEvent("onLocalAudioStateChanged", state, error);
  }

  @Override
  public void onRemoteAudioStateChanged(RemoteStreamKey key, RemoteAudioState state, RemoteAudioStateChangeReason reason) {
    this.ee.sendEvent("onRemoteAudioStateChanged", key, state, reason);
  }

  @Override
  public void onLocalVideoStateChanged(StreamIndex streamIndex, LocalVideoStreamState state, LocalVideoStreamError error) {
    this.ee.sendEvent("onLocalVideoStateChanged", streamIndex, state, error);
  }

  @Override
  public void onRemoteVideoStateChanged(RemoteStreamKey streamKey, RemoteVideoState videoState, RemoteVideoStateChangeReason videoStateReason) {
    this.ee.sendEvent("onRemoteVideoStateChanged", streamKey, videoState, videoStateReason);
  }

  @Override
  public void onRemoteVideoSuperResolutionModeChanged(RemoteStreamKey streamKey, VideoSuperResolutionMode mode, VideoSuperResolutionModeChangedReason reason) {
    this.ee.sendEvent("onRemoteVideoSuperResolutionModeChanged", streamKey, mode, reason);
  }

  @Override
  public void onVideoDenoiseModeChanged(VideoDenoiseMode mode, VideoDenoiseModeChangedReason reason) {
    this.ee.sendEvent("onVideoDenoiseModeChanged", mode, reason);
  }

  @Override
  public void onFirstRemoteVideoFrameRendered(RemoteStreamKey remoteStreamKey, VideoFrameInfo frameInfo) {
    this.ee.sendEvent("onFirstRemoteVideoFrameRendered", remoteStreamKey, frameInfo);
  }

  @Override
  public void onFirstRemoteVideoFrameDecoded(RemoteStreamKey remoteStreamKey, VideoFrameInfo frameInfo) {
    this.ee.sendEvent("onFirstRemoteVideoFrameDecoded", remoteStreamKey, frameInfo);
  }

  @Override
  public void onLogout(int reason) {
    this.ee.sendEvent("onLogout", reason);
  }

  @Override
  public void onServerParamsSetResult(int error) {
    this.ee.sendEvent("onServerParamsSetResult", error);
  }

  @Override
  public void onGetPeerOnlineStatus(String peerUserId, int status) {
    this.ee.sendEvent("onGetPeerOnlineStatus", peerUserId, status);
  }

  @Override
  public void onUserMessageReceivedOutsideRoom(String uid, String message) {
    this.ee.sendEvent("onUserMessageReceivedOutsideRoom", uid, message);
  }

  @Override
  public void onUserBinaryMessageReceivedOutsideRoom(String uid, ByteBuffer message) {
    this.ee.sendEvent("onUserBinaryMessageReceivedOutsideRoom", uid, message);
  }

  @Override
  public void onUserMessageSendResultOutsideRoom(long msgid, int error) {
    this.ee.sendEvent("onUserMessageSendResultOutsideRoom", msgid, error);
  }

  @Override
  public void onServerMessageSendResult(long msgid, int error, ByteBuffer message) {
    this.ee.sendEvent("onServerMessageSendResult", msgid, error, message);
  }

  @Override
  public void onNetworkDetectionResult(NetworkDetectionLinkType type, int quality, int rtt, double lostRate, int bitrate, int jitter) {
    this.ee.sendEvent("onNetworkDetectionResult", type, quality, rtt, lostRate, bitrate, jitter);
  }

  @Override
  public void onNetworkDetectionStopped(NetworkDetectionStopReason reason) {
    this.ee.sendEvent("onNetworkDetectionStopped", reason);
  }

  @Override
  public void onSimulcastSubscribeFallback(RemoteStreamSwitch event) {
    this.ee.sendEvent("onSimulcastSubscribeFallback", event);
  }

  @Override
  public void onPerformanceAlarms(PerformanceAlarmMode mode, String roomId, PerformanceAlarmReason reason, SourceWantedData data) {
    this.ee.sendEvent("onPerformanceAlarms", mode, roomId, reason, data);
  }

  @Override
  public void onAudioFrameSendStateChanged(String roomId, RtcUser user, FirstFrameSendState state) {
    this.ee.sendEvent("onAudioFrameSendStateChanged", roomId, user, state);
  }

  @Override
  public void onVideoFrameSendStateChanged(String roomId, RtcUser user, FirstFrameSendState state) {
    this.ee.sendEvent("onVideoFrameSendStateChanged", roomId, user, state);
  }

  @Override
  public void onScreenVideoFrameSendStateChanged(String roomId, RtcUser user, FirstFrameSendState state) {
    this.ee.sendEvent("onScreenVideoFrameSendStateChanged", roomId, user, state);
  }

  @Override
  public void onAudioFramePlayStateChanged(String roomId, RtcUser user, FirstFramePlayState state) {
    this.ee.sendEvent("onAudioFramePlayStateChanged", roomId, user, state);
  }

  @Override
  public void onVideoFramePlayStateChanged(String roomId, RtcUser user, FirstFramePlayState state) {
    this.ee.sendEvent("onVideoFramePlayStateChanged", roomId, user, state);
  }

  @Override
  public void onScreenVideoFramePlayStateChanged(String roomId, RtcUser user, FirstFramePlayState state) {
    this.ee.sendEvent("onScreenVideoFramePlayStateChanged", roomId, user, state);
  }

  @Override
  public void onMediaDeviceStateChanged(String deviceID, int deviceType, int deviceState, int deviceError) {
    this.ee.sendEvent("onMediaDeviceStateChanged", deviceID, deviceType, deviceState, deviceError);
  }

  @Override
  public void onAudioDeviceStateChanged(String deviceID, AudioDeviceType deviceType, int deviceState, int deviceError) {
    int deviceTypeInt = deviceType.value();
    this.ee.sendEvent("onAudioDeviceStateChanged", deviceID, deviceTypeInt, deviceState, deviceError);
  }

  @Override
  public void onVideoDeviceStateChanged(String deviceID, VideoDeviceType deviceType, int deviceState, int deviceError) {
    int deviceTypeInt = deviceType.value();
    this.ee.sendEvent("onVideoDeviceStateChanged", deviceID, deviceTypeInt, deviceState, deviceError);
  }

  @Override
  public void onMediaDeviceWarning(String deviceID, int deviceType, int deviceWarning) {
    this.ee.sendEvent("onMediaDeviceWarning", deviceID, deviceType, deviceWarning);
  }

  @Override
  public void onAudioDeviceWarning(String deviceID, AudioDeviceType deviceType, int deviceWarning) {
    this.ee.sendEvent("onAudioDeviceWarning", deviceID, deviceType, deviceWarning);
  }

  @Override
  public void onVideoDeviceWarning(String deviceID, VideoDeviceType deviceType, int deviceWarning) {
    this.ee.sendEvent("onVideoDeviceWarning", deviceID, deviceType, deviceWarning);
  }

  @Override
  public void onHttpProxyState(int state) {
    this.ee.sendEvent("onHttpProxyState", state);
  }

  @Override
  public void onHttpsProxyState(int state) {
    this.ee.sendEvent("onHttpsProxyState", state);
  }

  @Override
  public void onSocks5ProxyState(int state, String cmd, String proxyAddress, String localAddress, String remoteAddress) {
    this.ee.sendEvent("onSocks5ProxyState", state, cmd, proxyAddress, localAddress, remoteAddress);
  }

  @Override
  public void onRecordingStateUpdate(StreamIndex type, RecordingState state, RecordingErrorCode errorCode, RecordingInfo info) {
    this.ee.sendEvent("onRecordingStateUpdate", type, state, errorCode, info);
  }

  @Override
  public void onRecordingProgressUpdate(StreamIndex type, RecordingProgress progress, RecordingInfo info) {
    this.ee.sendEvent("onRecordingProgressUpdate", type, progress, info);
  }

  @Override
  public void onAudioRecordingStateUpdate(AudioRecordingState state, AudioRecordingErrorCode errorCode) {
    this.ee.sendEvent("onAudioRecordingStateUpdate", state, errorCode);
  }

  @Override
  public void onAudioMixingStateChanged(int mixId, AudioMixingState state, AudioMixingError error) {
    this.ee.sendEvent("onAudioMixingStateChanged", mixId, state, error);
  }

  @Override
  public void onAudioMixingPlayingProgress(int mixId, long progress) {
    this.ee.sendEvent("onAudioMixingPlayingProgress", mixId, progress);
  }

  @Override
  public void onLocalAudioPropertiesReport(LocalAudioPropertiesInfo[] audioPropertiesInfos) {
    android.os.Handler mainHandler =
      new android.os.Handler(android.os.Looper.getMainLooper());
    mainHandler.post(() -> {
      try {
        ArrayList<Object> res = new ArrayList<>();
        for (LocalAudioPropertiesInfo info : audioPropertiesInfos) {
          Map<String, Object> data = new HashMap<>();
          Map<String, Object> props = new HashMap<>();
          ArrayList<Number> spectrum = new ArrayList<>(info.audioPropertiesInfo.spectrum.length);
          for (int i = 0; i < info.audioPropertiesInfo.spectrum.length; i++) {
            spectrum.add(info.audioPropertiesInfo.spectrum[i]);
          }
          data.put("streamIndex", info.streamIndex);
          props.put("linearVolume", info.audioPropertiesInfo.linearVolume);
          props.put("nonlinearVolume", info.audioPropertiesInfo.nonlinearVolume);
          props.put("spectrum", spectrum);
          props.put("vad", info.audioPropertiesInfo.vad);
          props.put("voicePitch", info.audioPropertiesInfo.voicePitch);
          props.put("deviceLoopDelay", info.audioPropertiesInfo.deviceLoopDelay);
          data.put("audioPropertiesInfo", props);
          res.add(data);
        }
        this.ee.sendEvent("onLocalAudioPropertiesReport", res);
      } catch (Exception e) {
        e.printStackTrace();
      }
    });
  }

  @Override
  public void onAudioPlaybackDeviceTestVolume(int volume) {
    this.ee.sendEvent("onAudioPlaybackDeviceTestVolume", volume);
  }

  @Override
  public void onRemoteAudioPropertiesReport(RemoteAudioPropertiesInfo[] audioPropertiesInfos, int totalRemoteVolume) {
    android.os.Handler mainHandler =
      new android.os.Handler(android.os.Looper.getMainLooper());
    mainHandler.post(() -> {
      try {
        ArrayList<Object> res = new ArrayList<>();
        for (RemoteAudioPropertiesInfo info : audioPropertiesInfos) {
          Map<String, Object> data = new HashMap<>();
          Map<String, Object> streamKey = new HashMap<>();
          Map<String, Object> props = new HashMap<>();
          ArrayList<Number> spectrum = new ArrayList<>(info.audioPropertiesInfo.spectrum.length);
          for (int i = 0; i < info.audioPropertiesInfo.spectrum.length; i++) {
            spectrum.add(info.audioPropertiesInfo.spectrum[i]);
          }
          streamKey.put("roomId", info.streamKey.roomId);
          streamKey.put("userId", info.streamKey.userId);
          streamKey.put("streamIndex", info.streamKey.streamIndex);
          data.put("streamKey", streamKey);
          props.put("linearVolume", info.audioPropertiesInfo.linearVolume);
          props.put("nonlinearVolume", info.audioPropertiesInfo.nonlinearVolume);
          props.put("spectrum", spectrum);
          props.put("vad", info.audioPropertiesInfo.vad);
          props.put("voicePitch", info.audioPropertiesInfo.voicePitch);
          props.put("deviceLoopDelay", info.audioPropertiesInfo.deviceLoopDelay);
          data.put("audioPropertiesInfo", props);
          res.add(data);
        }
        this.ee.sendEvent("onRemoteAudioPropertiesReport", res, totalRemoteVolume);
      } catch (Exception e) {
        e.printStackTrace();
      }
    });
  }

  @Override
  public void onLoginResult(String uid, int errorCode, int elapsed) {
    this.ee.sendEvent("onLoginResult", uid, errorCode, elapsed);
  }

  @Override
  public void onFirstLocalVideoFrameCaptured(StreamIndex streamIndex, VideoFrameInfo frameInfo) {
    this.ee.sendEvent("onFirstLocalVideoFrameCaptured", streamIndex, frameInfo);
  }

  @Override
  public void onLocalVideoSizeChanged(StreamIndex streamIndex, VideoFrameInfo frameInfo) {
    this.ee.sendEvent("onLocalVideoSizeChanged", streamIndex, frameInfo);
  }

  @Override
  public void onRemoteVideoSizeChanged(RemoteStreamKey remoteStreamKey, VideoFrameInfo frameInfo) {
    this.ee.sendEvent("onRemoteVideoSizeChanged", remoteStreamKey, frameInfo);
  }

  @Override
  public void onConnectionStateChanged(int state, int reason) {
    this.ee.sendEvent("onConnectionStateChanged", state, reason);
  }

  @Override
  public void onAudioPlaybackDeviceChanged(AudioPlaybackDevice device) {
    this.ee.sendEvent("onAudioPlaybackDeviceChanged", device);
  }

  @Override
  public void onAudioRouteChanged(AudioRoute route) {
    this.ee.sendEvent("onAudioRouteChanged", route);
  }

  @Override
  public void onFirstLocalAudioFrame(StreamIndex streamIndex) {
    this.ee.sendEvent("onFirstLocalAudioFrame", streamIndex);
  }

  @Override
  public void onFirstRemoteAudioFrame(RemoteStreamKey remoteStreamKey) {
    this.ee.sendEvent("onFirstRemoteAudioFrame", remoteStreamKey);
  }

  @Override
  public void onLogReport(String logType, JSONObject logContent) {
    this.ee.sendEvent("onLogReport", logType, logContent);
  }

  @Override
  public void onSEIMessageReceived(RemoteStreamKey remoteStreamKey, ByteBuffer message) {
    this.ee.sendEvent("onSEIMessageReceived", remoteStreamKey, message);
  }

  @Override
  public void onSEIStreamUpdate(RemoteStreamKey remoteStreamKey, SEIStreamUpdateEvent event) {
    this.ee.sendEvent("onSEIStreamUpdate", remoteStreamKey, event);
  }

  @Override
  public void onFirstPublicStreamVideoFrameDecoded(String publicStreamId, VideoFrameInfo videoFrameInfo) {
    this.ee.sendEvent("onFirstPublicStreamVideoFrameDecoded", publicStreamId, videoFrameInfo);
  }

  @Override
  public void onFirstPublicStreamAudioFrame(String publicStreamId) {
    this.ee.sendEvent("onFirstPublicStreamAudioFrame", publicStreamId);
  }

  @Override
  public void onEchoTestResult(EchoTestResult result) {
    this.ee.sendEvent("onEchoTestResult", result);
  }

  @Override
  public void onCloudProxyConnected(int interval) {
    this.ee.sendEvent("onCloudProxyConnected", interval);
  }

  @Override
  public void onAudioDumpStateChanged(AudioDumpStatus status) {
    this.ee.sendEvent("onAudioDumpStateChanged", status);
  }

  @Override
  public void onNetworkTimeSynchronized() {
    super.onNetworkTimeSynchronized();
  }

  @Override
  public void onLicenseWillExpire(int days) {
    this.ee.sendEvent("onLicenseWillExpire", days);
  }

  @Override
  public void onInvokeExperimentalAPI(String param) {
    this.ee.sendEvent("onInvokeExperimentalAPI", param);
  }

  @Override
  public void onHardwareEchoDetectionResult(HardwareEchoDetectionResult hardwareEchoDetectionResult) {
    this.ee.sendEvent("onHardwareEchoDetectionResult", hardwareEchoDetectionResult);
  }

  @Override
  public void onLocalProxyStateChanged(LocalProxyType localProxyType, LocalProxyState localProxyState, LocalProxyError localProxyError) {
    this.ee.sendEvent("onLocalProxyStateChanged", localProxyType, localProxyState, localProxyError);
  }

  @Override
  public void onEffectError(EffectErrorType error, String msg) {
    this.ee.sendEvent("onEffectError", error, msg);
  }

  @Override
  public void onRemoteRenderError(RemoteStreamKey key, RenderError error, String message) {
    this.ee.sendEvent("onRemoteRenderError", key, error, message);
  }

  @Override
  public void onExternalScreenFrameUpdate(FrameUpdateInfo info) {
    this.ee.sendEvent("onExternalScreenFrameUpdate", info);
  }

  @Override
  public void onSysStats(SysStats stats) {
    this.ee.sendEvent("onSysStats", stats);
  }

  @Override
  public void onNetworkTypeChanged(int type) {
    this.ee.sendEvent("onNetworkTypeChanged", type);
  }

  @Override
  public void onUserMuteAudio(String roomId, String uid, MuteState muteState) {
    this.ee.sendEvent("onUserMuteAudio", roomId, uid, muteState);
  }

  @Override
  public void onUserMuteVideo(String roomId, String uid, MuteState muteState) {
    this.ee.sendEvent("onUserMuteVideo", roomId, uid, muteState);
  }

  @Override
  public void onUserStartVideoCapture(String roomId, String uid) {
    this.ee.sendEvent("onUserStartVideoCapture", roomId, uid);
  }

  @Override
  public void onUserStopVideoCapture(String roomId, String uid) {
    this.ee.sendEvent("onUserStopVideoCapture", roomId, uid);
  }

  @Override
  public void onCreateRoomStateChanged(String roomId, int errorCode) {
    this.ee.sendEvent("onCreateRoomStateChanged", roomId, errorCode);
  }
}
