/*---------------------------------------------------------------------------*
 *  android_speech_srec_MicrophoneInputStream.cpp                            *
 *                                                                           *
 *  Copyright 2007 Nuance Communciations, Inc.                               *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
 *  you may not use this file except in compliance with the License.         *
 *                                                                           *
 *  You may obtain a copy of the License at                                  *
 *      http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 *---------------------------------------------------------------------------*/


#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define LOG_TAG "srec_jni"
#include <utils/Log.h>

#include <media/AudioRecord.h>
#include <media/mediarecorder.h>

#include <jni.h>

using namespace android;



//
// helper function to throw an exception
//
static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
    if (jclass cls = env->FindClass(ex)) {
        char msg[1000];
        sprintf(msg, fmt, data);
        env->ThrowNew(cls, msg);
        env->DeleteLocalRef(cls);
    }
}


///////////////////////////////////////////////////////////////////////////////
// MicrophoneInputStream JNI implememtations
///////////////////////////////////////////////////////////////////////////////

static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordNew
        (JNIEnv *env, jclass clazz, jint sampleRate, jint fifoFrames) {

    android::AudioRecord* ar = new android::AudioRecord(
            android::AUDIO_SOURCE_VOICE_RECOGNITION, sampleRate,
            android::AudioSystem::PCM_16_BIT, android::AudioSystem::CHANNEL_IN_MONO,
            fifoFrames, 0);
    if (ar == NULL) {
        LOGE("Error creating AudioRecord");
    }
    else {
        status_t s = ar->initCheck();
        if (s != NO_ERROR) {
            delete ar;
            ar = NULL;
            LOGE("initCheck error %d ", s);
        }
    }
    return (int)ar;
}

static JNIEXPORT int JNICALL Java_android_speech_srec_Recognizer_AudioRecordStart
        (JNIEnv *env, jclass clazz, jint audioRecord) {
    return (int)(((AudioRecord*)audioRecord)->start());
}

static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordRead
        (JNIEnv *env, jclass clazz, jint audioRecord, jbyteArray array, jint offset, jint length) {
    jbyte buffer[4096];
    if (length > (int)sizeof(buffer)) length = sizeof(buffer);
    length = ((AudioRecord*)audioRecord)->read(buffer, length);
    if (length < 0) {
        throwException(env, "java/io/IOException", "AudioRecord::read failed %d", length);
        return -1;
    }
    env->SetByteArrayRegion(array, offset, length, buffer);
    return length;
}

static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordStop
        (JNIEnv *env, jclass clazz, jint audioRecord) {
    if (int rtn = ((AudioRecord*)audioRecord)->stop()) {
        throwException(env, "java/io/IOException", "AudioRecord::stop failed %d", rtn);
    }
}

static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordDelete
        (JNIEnv *env, jclass clazz, jint audioRecord) {
    delete (AudioRecord*)audioRecord;
}


/*
 * Table of methods associated with a single class.
 */
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    {"AudioRecordNew",    "(II)I",    (void*)Java_android_speech_srec_Recognizer_AudioRecordNew},
    {"AudioRecordStart",  "(I)I",     (void*)Java_android_speech_srec_Recognizer_AudioRecordStart},
    {"AudioRecordRead",   "(I[BII)I", (void*)Java_android_speech_srec_Recognizer_AudioRecordRead},
    {"AudioRecordStop",   "(I)V",     (void*)Java_android_speech_srec_Recognizer_AudioRecordStop},
    {"AudioRecordDelete", "(I)V",     (void*)Java_android_speech_srec_Recognizer_AudioRecordDelete},
};

/*
 * Set some test stuff up.
 *
 * Returns the JNI version on success, -1 on failure.
 */
jint register_android_speech_srec_MicrophoneInputStream(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jclass clazz = NULL;
    const char* className = "android/speech/srec/MicrophoneInputStream";

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        return -1;
    }
    assert(env != NULL);

    clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGE("Native registration unable to find class '%s'\n", className);
        return -1;
    }
    if (env->RegisterNatives(clazz, gMethods,
            sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
        LOGE("RegisterNatives failed for '%s'\n", className);
        return -1;
    }

    /* success -- return valid version number */
    return JNI_VERSION_1_4;;
}