/*
 *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_
#define EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_

#include <jni.h>

#include <memory>
#include <string>
#include <vector>

#include "api/audio_codecs/audio_format.h"
#include "api/call/transport.h"
#include "api/voip/voip_base.h"
#include "api/voip/voip_engine.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/async_udp_socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/thread.h"
#include "sdk/android/native_api/jni/scoped_java_ref.h"

namespace webrtc_examples {

// AndroidVoipClient facilitates the use of the VoIP API defined in
// api/voip/voip_engine.h. One instance of AndroidVoipClient should
// suffice for most VoIP applications. AndroidVoipClient implements
// webrtc::Transport to send RTP/RTCP packets to the remote endpoint.
// It also creates methods (slots) for sockets to connect to in
// order to receive RTP/RTCP packets. AndroidVoipClient does all
// operations with rtc::Thread (voip_thread_), this is to comply
// with consistent thread usage requirement with ProcessThread used
// within VoipEngine, as well as providing asynchronicity to the
// caller. AndroidVoipClient is meant to be used by Java through JNI.
class AndroidVoipClient : public webrtc::Transport,
                          public sigslot::has_slots<> {
 public:
  // Returns a pointer to an AndroidVoipClient object. Clients should
  // use this factory method to create AndroidVoipClient objects. The
  // method will return a nullptr in case of initialization errors.
  // It is the client's responsibility to delete the pointer when
  // they are done with it (this class provides a Delete() method).
  static AndroidVoipClient* Create(
      JNIEnv* env,
      const webrtc::JavaParamRef<jobject>& application_context,
      const webrtc::JavaParamRef<jobject>& j_voip_client);

  ~AndroidVoipClient() override;

  // Provides client with a Java List of Strings containing names of
  // the built-in supported codecs through callback.
  void GetSupportedCodecs(JNIEnv* env);

  // Provides client with a Java String of the default local IPv4 address
  // through callback. If IPv4 address is not found, provide the default
  // local IPv6 address. If IPv6 address is not found, provide an empty
  // string.
  void GetLocalIPAddress(JNIEnv* env);

  // Sets the encoder used by the VoIP API.
  void SetEncoder(JNIEnv* env,
                  const webrtc::JavaParamRef<jstring>& j_encoder_string);

  // Sets the decoders used by the VoIP API.
  void SetDecoders(JNIEnv* env,
                   const webrtc::JavaParamRef<jobject>& j_decoder_strings);

  // Sets two local/remote addresses, one for RTP packets, and another for
  // RTCP packets. The RTP address will have IP address j_ip_address_string
  // and port number j_port_number_int, the RTCP address will have IP address
  // j_ip_address_string and port number j_port_number_int+1.
  void SetLocalAddress(JNIEnv* env,
                       const webrtc::JavaParamRef<jstring>& j_ip_address_string,
                       jint j_port_number_int);
  void SetRemoteAddress(
      JNIEnv* env,
      const webrtc::JavaParamRef<jstring>& j_ip_address_string,
      jint j_port_number_int);

  // Starts a VoIP session, then calls a callback method with a boolean
  // value indicating if the session has started successfully. The VoIP
  // operations below can only be used after a session has already started.
  void StartSession(JNIEnv* env);

  // Stops the current session, then calls a callback method with a
  // boolean value indicating if the session has stopped successfully.
  void StopSession(JNIEnv* env);

  // Starts sending RTP/RTCP packets to the remote endpoint, then calls
  // a callback method with a boolean value indicating if sending
  // has started successfully.
  void StartSend(JNIEnv* env);

  // Stops sending RTP/RTCP packets to the remote endpoint, then calls
  // a callback method with a boolean value indicating if sending
  // has stopped successfully.
  void StopSend(JNIEnv* env);

  // Starts playing out the voice data received from the remote endpoint,
  // then calls a callback method with a boolean value indicating if
  // playout has started successfully.
  void StartPlayout(JNIEnv* env);

  // Stops playing out the voice data received from the remote endpoint,
  // then calls a callback method with a boolean value indicating if
  // playout has stopped successfully.
  void StopPlayout(JNIEnv* env);

  // Deletes this object. Used by client when they are done.
  void Delete(JNIEnv* env);

  // Implementation for Transport.
  bool SendRtp(const uint8_t* packet,
               size_t length,
               const webrtc::PacketOptions& options) override;
  bool SendRtcp(const uint8_t* packet, size_t length) override;

  // Slots for sockets to connect to.
  void OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket,
                             const char* rtp_packet,
                             size_t size,
                             const rtc::SocketAddress& addr,
                             const int64_t& timestamp);
  void OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
                              const char* rtcp_packet,
                              size_t size,
                              const rtc::SocketAddress& addr,
                              const int64_t& timestamp);

 private:
  AndroidVoipClient(JNIEnv* env,
                    const webrtc::JavaParamRef<jobject>& j_voip_client)
      : voip_thread_(rtc::Thread::CreateWithSocketServer()),
        j_voip_client_(env, j_voip_client) {}

  void Init(JNIEnv* env,
            const webrtc::JavaParamRef<jobject>& application_context);

  // Overloaded methods having native C++ variables as arguments.
  void SetEncoder(const std::string& encoder);
  void SetDecoders(const std::vector<std::string>& decoders);
  void SetLocalAddress(const std::string& ip_address, int port_number);
  void SetRemoteAddress(const std::string& ip_address, int port_number);

  // Methods to send and receive RTP/RTCP packets. Takes in a
  // copy of a packet as a vector to prolong the lifetime of
  // the packet as these methods will be called asynchronously.
  void SendRtpPacket(const std::vector<uint8_t>& packet_copy);
  void SendRtcpPacket(const std::vector<uint8_t>& packet_copy);
  void ReadRTPPacket(const std::vector<uint8_t>& packet_copy);
  void ReadRTCPPacket(const std::vector<uint8_t>& packet_copy);

  // Used to invoke operations and send/receive RTP/RTCP packets.
  std::unique_ptr<rtc::Thread> voip_thread_;
  // Reference to the VoipClient java instance used to
  // invoke callbacks when operations are finished.
  webrtc::ScopedJavaGlobalRef<jobject> j_voip_client_
      RTC_GUARDED_BY(voip_thread_);
  // A list of AudioCodecSpec supported by the built-in
  // encoder/decoder factories.
  std::vector<webrtc::AudioCodecSpec> supported_codecs_
      RTC_GUARDED_BY(voip_thread_);
  // A JNI context used by the voip_thread_.
  JNIEnv* env_ RTC_GUARDED_BY(voip_thread_);
  // The entry point to all VoIP APIs.
  std::unique_ptr<webrtc::VoipEngine> voip_engine_ RTC_GUARDED_BY(voip_thread_);
  // Used by the VoIP API to facilitate a VoIP session.
  absl::optional<webrtc::ChannelId> channel_ RTC_GUARDED_BY(voip_thread_);
  // Members below are used for network related operations.
  std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_ RTC_GUARDED_BY(voip_thread_);
  std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_
      RTC_GUARDED_BY(voip_thread_);
  rtc::SocketAddress rtp_local_address_ RTC_GUARDED_BY(voip_thread_);
  rtc::SocketAddress rtcp_local_address_ RTC_GUARDED_BY(voip_thread_);
  rtc::SocketAddress rtp_remote_address_ RTC_GUARDED_BY(voip_thread_);
  rtc::SocketAddress rtcp_remote_address_ RTC_GUARDED_BY(voip_thread_);
};

}  // namespace webrtc_examples

#endif  // EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_
