//
//  AgoraRtcEngine SDK
//
//  Copyright (c) 2019 Agora.io. All rights reserved.
//

#pragma once

#include <stdint.h>
#include "AgoraBase.h"
#include "AgoraMediaBase.h"
#include "AgoraRefPtr.h"
#include "IAgoraRtcEngineEx.h"

namespace agora {
namespace rtc {

/**
 * @brief The spatial position of the remote user or the media player.
 */
struct RemoteVoicePositionInfo {
  /**
   * The coordinates in the world coordinate system. This parameter is an array of length 3, and the
   * three values represent the front, right, and top coordinates in turn.
   */
  float position[3];
  /**
   * The unit vector of the x axis in the coordinate system. This parameter is an array of length 3,
   * and the three values represent the front, right, and top coordinates in turn.
   */
  float forward[3];
};

/**
 * @brief Sound insulation area settings.
 */
struct SpatialAudioZone {
  /**
   * The ID of the sound insulation area.
   */
  int zoneSetId;
  /**
   * The spatial center point of the sound insulation area. This parameter is an array of length 3,
   * and the three values represent the front, right, and top coordinates in turn.
   */
  float position[3];
  /**
   * Starting at `position`, the forward unit vector. This parameter is an array of length 3, and the
   * three values represent the front, right, and top coordinates in turn.
   */
  float forward[3];
  /**
   * Starting at `position`, the right unit vector. This parameter is an array of length 3, and the
   * three values represent the front, right, and top coordinates in turn.
   */
  float right[3];
  /**
   * Starting at `position`, the up unit vector. This parameter is an array of length 3, and the three
   * values represent the front, right, and top coordinates in turn.
   */
  float up[3];
  /**
   * The entire sound insulation area is regarded as a cube; this represents the length of the forward
   * side in the unit length of the game engine.
   */
  float forwardLength;
  /**
   * The entire sound insulation area is regarded as a cube; this represents the length of the right
   * side in the unit length of the game engine.
   */
  float rightLength;
  /**
   * The entire sound insulation area is regarded as a cube; this represents the length of the up side
   * in the unit length of the game engine.
   */
  float upLength;
  /**
   * The sound attenuation coefficient when users within the sound insulation area communicate with
   * external users. The value range is [0,1]. The values are as follows:
   * - 0: Broadcast mode, where the volume and timbre are not attenuated with distance, and the volume
   * and timbre heard by local users do not change regardless of distance.
   * - (0,0.5): Weak attenuation mode, that is, the volume and timbre are only weakly attenuated
   * during the propagation process, and the sound can travel farther than the real environment.
   * - 0.5: (Default) simulates the attenuation of the volume in the real environment; the effect is
   * equivalent to not setting the `audioAttenuation` parameter.
   * - (0.5,1]: Strong attenuation mode (default value is 1), that is, the volume and timbre attenuate
   * rapidly during propagation.
   */
  float audioAttenuation;
};

/**
 * @brief The configuration of `ILocalSpatialAudioEngine`.
 */
struct LocalSpatialAudioConfig {
  /**
   * `IRtcEngine`.
   */
  agora::rtc::IRtcEngine* rtcEngine;

  LocalSpatialAudioConfig() : rtcEngine(NULL) {}
};

/** The IBaseSpatialAudioEngine class provides the common methods of ICloudSpatialAudioEngine and ILocalSpatialAudioEngine.
 */
class ILocalSpatialAudioEngine: public RefCountInterface {
 protected:
  virtual ~ILocalSpatialAudioEngine() {}

 public:
  /**
   * @brief Destroys `IBaseSpatialAudioEngine`.
   *
   * @details
   * This method releases all resources under `IBaseSpatialAudioEngine`. When the user does not need
   * to use the spatial audio effect, you can call this method to release resources for other
   * operations.
   * After calling this method, you can no longer use any of the APIs under `IBaseSpatialAudioEngine`.
   *
   * @note Call this method before the `release` method under `IRtcEngine`.
   *
   */
  virtual void release() = 0;

  /**
   * @brief Initializes `ILocalSpatialAudioEngine`.
   *
   * @note
   * - Call this method after calling `queryInterface` `(AGORA_IID_LOCAL_SPATIAL_AUDIO)`.
   * - Before calling other methods of the `ILocalSpatialAudioEngine` class, you need to call this
   * method to initialize `ILocalSpatialAudioEngine`.
   * - The SDK supports creating only one `ILocalSpatialAudioEngine` instance for an app.
   *
   * @param config The configuration of `ILocalSpatialAudioEngine`. See `LocalSpatialAudioConfig`.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int initialize(const LocalSpatialAudioConfig& config) = 0;
  /**
   * @brief Updates the spatial position of the specified remote user.
   *
   * @details
   * After successfully calling this method, the SDK calculates the spatial audio parameters based on
   * the relative position of the local and remote user.
   *
   * @note Call this method after the `joinChannel(const char* token, const char* channelId, const
   * char* info, uid_t uid)` or `joinChannel(const char* token, const char* channelId, uid_t uid,
   * const ChannelMediaOptions& options)` method.
   *
   * @param uid The user ID. This parameter must be the same as the user ID passed in when the user
   * joined the channel.
   * @param posInfo The spatial position of the remote user. See `RemoteVoicePositionInfo`.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int updateRemotePosition(uid_t uid, const RemoteVoicePositionInfo &posInfo) = 0;
  /**
   * Updates the position of remote user. It's supposed to use with IRtcEngineEx::joinChannelEx.
   *
   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
   * @param posInfo The position information of remote user. See #RemoteVoicePositionInfo.
   * @param connection The RTC connection whose spatial audio effect you want to update. See #RtcConnection.
   * @return
   * - 0: Success.
   * - <0: Failure.
   */
  virtual int updateRemotePositionEx(uid_t uid, const RemoteVoicePositionInfo &posInfo, const RtcConnection& connection) = 0;
  /**
   * @brief Removes the spatial position of the specified remote user.
   *
   * @details
   * After successfully calling this method, the local user no longer hears the specified remote user.
   * After leaving the channel, to avoid wasting computing resources, call this method to delete the
   * spatial position information of the specified remote user. Otherwise, the user's spatial position
   * information will be saved continuously. When the number of remote users exceeds the number of
   * audio streams that can be received as set in `setMaxAudioRecvCount`, the system automatically
   * unsubscribes from the audio stream of the user who is furthest away based on relative distance.
   *
   * @param uid The user ID. This parameter must be the same as the user ID passed in when the user
   * joined the channel.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int removeRemotePosition(uid_t uid) = 0;
  /**
   * Remove the position information of remote user. It's supposed to use with IRtcEngineEx::joinChannelEx.
   *
   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
   * @param connection The RTC connection whose spatial audio effect you want to update. See #RtcConnection.
   * @return
   * - 0: Success.
   * - <0: Failure.
   */
  virtual int removeRemotePositionEx(uid_t uid, const RtcConnection& connection) = 0;
  /**
   * Clear the position informations of remote users. It's supposed to use with IRtcEngineEx::joinChannelEx.
   *
   * @return
   * - 0: Success.
   * - <0: Failure.
   */
  virtual int clearRemotePositionsEx(const RtcConnection& connection) = 0;
  /**
   * Updates the position of local user. This method is used in scene with multi RtcConnection.
   *
   * @note
   * - This method is only effective in ILocalSpatialAudioEngine currently.
   *
   * @param position The sound position of the user. The coordinate order is forward, right, and up.
   * @param axisForward The vector in the direction of the forward axis in the coordinate system.
   * @param axisRight The vector in the direction of the right axis in the coordinate system.
   * @param axisUp The vector in the direction of the up axis in the coordinate system.
   * @param connection The RTC connection whose spatial audio effect you want to update.
   * @return
   * - 0: Success.
   * - <0: Failure.
   */
  virtual int updateSelfPositionEx(const float position[3], const float axisForward[3], const float axisRight[3], const float axisUp[3], const RtcConnection& connection) = 0;

  /**
   * @brief Sets the maximum number of streams that a user can receive in a specified audio reception
   * range.
   *
   * @details
   * If the number of receivable streams exceeds the set value, the local user receives the `maxCount`
   * streams that are closest to the local user.
   *
   * @param maxCount The maximum number of streams that a user can receive within a specified audio
   * reception range. The value of this parameter should be ≤ 16, and the default value is 10.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setMaxAudioRecvCount(int maxCount) = 0;
  /**
   * @brief Sets the audio reception range of the local user.
   *
   * @details
   * After the setting is successful, the local user can only hear the remote users within the setting
   * range or belonging to the same team. You can call this method at any time to update the audio
   * reception range.
   *
   * @param range The maximum audio reception range. The unit is meters. The value of this parameter
   * must be greater than 0, and the default value is 20.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setAudioRecvRange(float range) = 0;

  /**
   * @brief Sets the length (in meters) of the game engine distance per unit.
   *
   * @details
   * In a game engine, the unit of distance is customized, while in the Agora spatial audio algorithm,
   * distance is measured in meters. By default, the SDK converts the game engine distance per unit to
   * one meter. You can call this method to convert the game engine distance per unit to a specified
   * number of meters.
   *
   * @param unit The number of meters that the game engine distance per unit is equal to. The value of
   * this parameter must be greater than 0.00, and the default value is 1.00. For example, setting
   * unit as 2.00 means the game engine distance per unit equals 2 meters.The larger the value is, the
   * faster the sound heard by the local user attenuates when the remote user moves far away from the
   * local user.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setDistanceUnit(float unit) = 0;
  /**
   * @brief Updates the spatial position of the local user.
   *
   * @details
   * - Under the `ILocalSpatialAudioEngine` class, this method needs to be used with
   * `updateRemotePosition`. The SDK calculates the relative position between the local and remote
   * users according to this method and the parameter settings in `updateRemotePosition`, and then
   * calculates the user's spatial audio effect parameters.
   *
   * @param position The coordinates in the world coordinate system. This parameter is an array of
   * length 3, and the three values represent the front, right, and top coordinates in turn.
   * @param axisForward The unit vector of the x axis in the coordinate system. This parameter is an
   * array of length 3, and the three values represent the front, right, and top coordinates in turn.
   * @param axisRight The unit vector of the y axis in the coordinate system. This parameter is an
   * array of length 3, and the three values represent the front, right, and top coordinates in turn.
   * @param axisUp The unit vector of the z axis in the coordinate system. This parameter is an array
   * of length 3, and the three values represent the front, right, and top coordinates in turn.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int updateSelfPosition(const float position[3], const float axisForward[3], const float axisRight[3], const float axisUp[3]) = 0;
  /**
   * @brief Updates the spatial position of the media player.
   *
   * @details
   * After a successful update, the local user can hear the change in the spatial position of the
   * media player.
   * Call timing: This method can be called either before or after joining the channel.
   *
   * @param playerId The ID of the media player. You can get the Device ID by calling
   * `getMediaPlayerId`.
   * @param positionInfo The spatial position of the media player. See `RemoteVoicePositionInfo`.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int updatePlayerPositionInfo(int playerId, const RemoteVoicePositionInfo& positionInfo) = 0;

  /**
   * Set parameters for spatial audio engine. It's deprecated for using.
   *
   * @param params The parameter string.
   * @return
   * - 0: Success.
   * - <0: Failure.
   */
  virtual int setParameters(const char* params) = 0;

  /**
   * @brief Stops or resumes publishing the local audio stream.
   *
   * @note
   * - This method does not affect any ongoing audio recording, because it does not disable the audio
   * capture device.
   * - Call this method after the `joinChannel(const char* token, const char* channelId, const char*
   * info, uid_t uid)` or `joinChannel(const char* token, const char* channelId, uid_t uid, const
   * ChannelMediaOptions& options)` method.
   * - When using the spatial audio effect, if you need to set whether to stop subscribing to the
   * audio stream of a specified user, Agora recommends calling this method instead of the
   * `muteLocalAudioStream` method in `IRtcEngine`.
   * - A successful call of this method triggers the `onUserMuteAudio` and `onRemoteAudioStateChanged`
   * callbacks on the remote client.
   *
   * @param mute Whether to stop publishing the local audio stream:
   * - `true`: Stop publishing the local audio stream.
   * - `false`: Publish the local audio stream.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int muteLocalAudioStream(bool mute) = 0;
  /**
   * @brief Stops or resumes subscribing to the audio streams of all remote users.
   *
   * @details
   * After successfully calling this method, the local user stops or resumes subscribing to the audio
   * streams of all remote users, including all subsequent users.
   *
   * @note
   * - Call this method after the `joinChannel(const char* token, const char* channelId, const char*
   * info, uid_t uid)` or `joinChannel(const char* token, const char* channelId, uid_t uid, const
   * ChannelMediaOptions& options)` method.
   * - When using the spatial audio effect, if you need to set whether to stop subscribing to the
   * audio streams of all remote users, Agora recommends calling this method instead of the
   * `muteAllRemoteAudioStreams` method in `IRtcEngine`.
   * - After calling this method, you need to call `updateSelfPosition` and `updateRemotePosition` to
   * update the spatial location of the local user and the remote user; otherwise, the settings in
   * this method do not take effect.
   *
   * @param mute Whether to stop subscribing to the audio streams of all remote users:
   * - `true`: Stop subscribing to the audio streams of all remote users.
   * - `false`: Subscribe to the audio streams of all remote users.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int muteAllRemoteAudioStreams(bool mute) = 0;
  
  /**
   * @brief Stops or resumes subscribing to the audio stream of a specified user.
   *
   * @note
   * - Call this method after the `joinChannel(const char* token, const char* channelId, const char*
   * info, uid_t uid)` or `joinChannel(const char* token, const char* channelId, uid_t uid, const
   * ChannelMediaOptions& options)` method.
   * - When using the spatial audio effect, if you need to set whether to stop subscribing to the
   * audio stream of a specified user, Agora recommends calling this method instead of the
   * `muteRemoteAudioStream` method in `IRtcEngine`.
   *
   * @param uid The user ID. This parameter must be the same as the user ID passed in when the user
   * joined the channel.
   * @param mute Whether to subscribe to the specified remote user's audio stream.
   * - `true`: Stop subscribing to the audio stream of the specified user.
   * - `false`: (Default) Subscribe to the audio stream of the specified user. The SDK decides whether
   * to subscribe according to the distance between the local user and the remote user.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int muteRemoteAudioStream(uid_t uid, bool mute) = 0;
  
  /**
   * @brief Sets the sound attenuation effect for the specified user.
   *
   * @param uid The user ID. This parameter must be the same as the user ID passed in when the user
   * joined the channel.
   * @param attenuation For the user's sound attenuation coefficient, the value range is [0,1]. The
   * values are as follows:
   * - 0: Broadcast mode, where the volume and timbre are not attenuated with distance, and the volume
   * and timbre heard by local users do not change regardless of distance.
   * - (0,0.5): Weak attenuation mode, that is, the volume and timbre are only weakly attenuated
   * during the propagation process, and the sound can travel farther than the real environment.
   * - 0.5: (Default) simulates the attenuation of the volume in the real environment; the effect is
   * equivalent to not setting the `speaker_attenuation` parameter.
   * - (0.5,1]: Strong attenuation mode, that is, the volume and timbre attenuate rapidly during the
   * propagation process.
   * @param forceSet Whether to force the user's sound attenuation effect:
   * - `true`: Force `attenuation` to set the sound attenuation of the user. At this time, the
   * `attenuation` coefficient of the sound insulation area set in the `audioAttenuation` of the
   * `SpatialAudioZone` does not take effect for the user.
   * - `false`: Do not force `attenuation` to set the user's sound attenuation effect, as shown in the
   * following two cases.
   *   - If the sound source and listener are inside and outside the sound isolation area, the sound
   * attenuation effect is determined by the `audioAttenuation` in `SpatialAudioZone`.
   *   - If the sound source and the listener are in the same sound insulation area or outside the
   * same sound insulation area, the sound attenuation effect is determined by `attenuation` in this
   * method.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setRemoteAudioAttenuation(uid_t uid, double attenuation, bool forceSet) = 0;
    
  /**
   * @brief Sets the sound insulation area.
   *
   * @details
   * In virtual interactive scenarios, you can use this method to set the sound insulation area and
   * sound attenuation coefficient. When the sound source (which can be the user or the media player)
   * and the listener belong to the inside and outside of the sound insulation area, they can
   * experience the attenuation effect of sound similar to the real environment when it encounters a
   * building partition.
   * - When the sound source and the listener belong to the inside and outside of the sound insulation
   * area, the sound attenuation effect is determined by the sound attenuation coefficient in
   * `SpatialAudioZone`.
   * - If the user or media player is in the same sound insulation area, it is not affected by
   * `SpatialAudioZone`, and the sound attenuation effect is determined by the `attenuation` parameter
   * in `setPlayerAttenuation` or `setRemoteAudioAttenuation`. If you do not call
   * `setPlayerAttenuation` or `setRemoteAudioAttenuation`, the default sound attenuation coefficient
   * of the SDK is 0.5, which simulates the attenuation of the sound in the real environment.
   * - If the sound source and the receiver belong to two sound insulation areas, the receiver cannot
   * hear the sound source.
   *
   * @note If this method is called multiple times, the last sound insulation area set takes effect.
   *
   * @param zones Sound insulation area settings. See `SpatialAudioZone`. When you set this parameter
   * to `NULL`, it means clearing all sound insulation zones.Attention: On the Windows platform, it is
   * necessary to ensure that the number of members in the `zones` array is equal to the value of
   * `zoneCount`; otherwise, it may cause a crash.
   * @param zoneCount The number of sound insulation areas.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setZones(const SpatialAudioZone *zones, unsigned int zoneCount) = 0;
  /**
   * @brief Sets the sound attenuation properties of the media player.
   *
   * @param playerId The ID of the media player. You can get the Device ID by calling
   * `getMediaPlayerId`.
   * @param attenuation The sound attenuation coefficient of the remote user or media player. The
   * value range is [0,1]. The values are as follows:
   * - 0: Broadcast mode, where the volume and timbre are not attenuated with distance, and the volume
   * and timbre heard by local users do not change regardless of distance.
   * - (0,0.5): Weak attenuation mode, that is, the volume and timbre are only weakly attenuated
   * during the propagation process, and the sound can travel farther than the real environment.
   * - 0.5: (Default) simulates the attenuation of the volume in the real environment; the effect is
   * equivalent to not setting the `speaker_attenuation` parameter.
   * - (0.5,1]: Strong attenuation mode, that is, the volume and timbre attenuate rapidly during the
   * propagation process.
   * @param forceSet Whether to force the sound attenuation effect of the media player:
   * - `true`: Force `attenuation` to set the attenuation of the media player. At this time, the
   * attenuation coefficient of the sound insulation are set in the `audioAttenuation` in the
   * `SpatialAudioZone` does not take effect for the media player.
   * - `false`: Do not force `attenuation` to set the sound attenuation effect of the media player, as
   * shown in the following two cases.
   *   - If the sound source and listener are inside and outside the sound isolation area, the sound
   * attenuation effect is determined by the `audioAttenuation` in `SpatialAudioZone`.
   *   - If the sound source and the listener are in the same sound insulation area or outside the
   * same sound insulation area, the sound attenuation effect is determined by `attenuation` in this
   * method.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int setPlayerAttenuation(int playerId, double attenuation, bool forceSet) = 0;
  /**
   * @brief Removes the spatial positions of all remote users.
   *
   * @details
   * After successfully calling this method, the local user no longer hears any remote users.
   * After leaving the channel, to avoid wasting resources, you can also call this method to delete
   * the spatial positions of all remote users.
   *
   * @return
   * - 0: Success.
   * - < 0: Failure.
   */
  virtual int clearRemotePositions() = 0;  
};

}  // namespace rtc
}  // namespace agora
