/*
 *  Copyright (c) 2013 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.
 */

#include "webrtc/modules/audio_device/android/audio_manager_jni.h"

#include <assert.h>

#include "webrtc/modules/utility/interface/helpers_android.h"
#include "webrtc/system_wrappers/interface/trace.h"

namespace webrtc {

static JavaVM* g_jvm_ = NULL;
static JNIEnv* g_jni_env_ = NULL;
static jobject g_context_ = NULL;
static jclass g_audio_manager_class_ = NULL;
static jobject g_audio_manager_ = NULL;

AudioManagerJni::AudioManagerJni()
    : low_latency_supported_(false),
      native_output_sample_rate_(0),
      native_buffer_size_(0) {
  if (!HasDeviceObjects()) {
    assert(false);
  }
  AttachThreadScoped ats(g_jvm_);
  JNIEnv* env = ats.env();
  assert(env && "Unsupported JNI version!");
  CreateInstance(env);
  // Pre-store device specific values.
  SetLowLatencySupported(env);
  SetNativeOutputSampleRate(env);
  SetNativeFrameSize(env);
}

void AudioManagerJni::SetAndroidAudioDeviceObjects(void* jvm, void* env,
                                                   void* context) {
  assert(jvm);
  assert(env);
  assert(context);

  // Store global Java VM variables to be accessed by API calls.
  g_jvm_ = reinterpret_cast<JavaVM*>(jvm);
  g_jni_env_ = reinterpret_cast<JNIEnv*>(env);
  g_context_ = g_jni_env_->NewGlobalRef(reinterpret_cast<jobject>(context));

  // FindClass must be made in this function since this function's contract
  // requires it to be called by a Java thread.
  // See
  // http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
  // as to why this is necessary.
  // Get the AudioManagerAndroid class object.
  jclass javaAmClassLocal = g_jni_env_->FindClass(
      "org/webrtc/voiceengine/AudioManagerAndroid");
  assert(javaAmClassLocal);

  // Create a global reference such that the class object is not recycled by
  // the garbage collector.
  g_audio_manager_class_ = reinterpret_cast<jclass>(
      g_jni_env_->NewGlobalRef(javaAmClassLocal));
  assert(g_audio_manager_class_);
}

void AudioManagerJni::ClearAndroidAudioDeviceObjects() {
  g_jni_env_->DeleteGlobalRef(g_audio_manager_class_);
  g_audio_manager_class_ = NULL;
  g_jni_env_->DeleteGlobalRef(g_context_);
  g_context_ = NULL;
  g_jni_env_->DeleteGlobalRef(g_audio_manager_);
  g_audio_manager_ = NULL;
  g_jni_env_ = NULL;
  g_jvm_ = NULL;
}

void AudioManagerJni::SetLowLatencySupported(JNIEnv* env) {
  jmethodID id = LookUpMethodId(env, "isAudioLowLatencySupported", "()Z");
  low_latency_supported_ = env->CallBooleanMethod(g_audio_manager_, id);
}

void AudioManagerJni::SetNativeOutputSampleRate(JNIEnv* env) {
  jmethodID id = LookUpMethodId(env, "getNativeOutputSampleRate", "()I");
  native_output_sample_rate_ = env->CallIntMethod(g_audio_manager_, id);
}

void AudioManagerJni::SetNativeFrameSize(JNIEnv* env) {
  jmethodID id = LookUpMethodId(env,
                                "getAudioLowLatencyOutputFrameSize", "()I");
  native_buffer_size_ = env->CallIntMethod(g_audio_manager_, id);
}

bool AudioManagerJni::HasDeviceObjects() {
  return g_jvm_ && g_jni_env_ && g_context_ && g_audio_manager_class_;
}

jmethodID AudioManagerJni::LookUpMethodId(JNIEnv* env,
                                          const char* method_name,
                                          const char* method_signature) {
  jmethodID ret_val = env->GetMethodID(g_audio_manager_class_, method_name,
                                       method_signature);
  assert(ret_val);
  return ret_val;
}

void AudioManagerJni::CreateInstance(JNIEnv* env) {
  // Get the method ID for the constructor taking Context.
  jmethodID id = LookUpMethodId(env, "<init>", "(Landroid/content/Context;)V");
  g_audio_manager_ = env->NewObject(g_audio_manager_class_, id, g_context_);
  // Create a global reference so that the instance is accessible until no
  // longer needed.
  g_audio_manager_ = env->NewGlobalRef(g_audio_manager_);
  assert(g_audio_manager_);
}

}  // namespace webrtc
