#define DEBUG_LOGD false

#define  LOG_TAG    "NicoRoJNI"
#if (DEBUG_LOGD)
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#else
#define  LOGD(...)
#endif /* LOG_TAG */


#include <string.h>
#include <jni.h>

#include <unistd.h>
#include <sys/endian.h>

#include <android/log.h>

#include "NicoroFFmpegPlayer.h"

#include "JniWrapper.h"

#include "jp_sourceforge_nicoro_NicoroFFmpegPlayer.h"
#include "jp_sourceforge_nicoro_NicoroSwfPlayer.h"

static JavaVM* gVM = NULL;

extern "C"
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
	gVM = vm;
	return JNI_VERSION_1_4;
}

extern "C"
void JNI_OnUnload(JavaVM *vm, void *reserved) {
	gVM = NULL;
}

static bool checkNotNullAndThrow(JNIEnv * env, NicoroFFmpegPlayer* pNicoroFFmpegPlayer) {
	if (pNicoroFFmpegPlayer == NULL) {
		jclass clsj = env->FindClass("java/lang/NullPointerException");
		if (clsj != NULL) {
			env->ThrowNew(clsj, "Native instance is null");
			env->DeleteLocalRef(clsj);
		}
		return false;
	}
	return true;
}

JNIEXPORT jlong JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_createNative
  (JNIEnv * env, jobject thiz) {
	LOGD("NicoroFFmpegPlayer#createNative start");
	return reinterpret_cast<jlong>(new NicoroFFmpegPlayer(gVM));
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_destroyNative
  (JNIEnv * env, jobject thiz, jlong nativeInstance) {
	LOGD("NicoroFFmpegPlayer#destroyNative start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (pNicoroFFmpegPlayer != NULL) {
		delete pNicoroFFmpegPlayer;
	}
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_loadFile
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jstring file) {
	LOGD("NicoroFFmpegPlayer#loadFile start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);

	JAutoPtrUTFChars fileUtf8(env, file);
	if (fileUtf8.get() != NULL) {
		pNicoroFFmpegPlayer->loadFileUseStream(fileUtf8.get());
	} else {
		LOGD("get filename NG");
	}

	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_loadStream
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray buffer, jobject ioCallback, jobject data) {
	LOGD("NicoroFFmpegPlayer#loadStream start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->loadStream(env, thiz, buffer, ioCallback, data);
	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_createAudioTrack
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jobject infoCallback) {
	LOGD("NicoroFFmpegPlayer#createAudioTrack start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->createAudioTrack(env, thiz, infoCallback);
	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_createDrawBuffer
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jobject infoCallback) {
	LOGD("NicoroFFmpegPlayer#createDrawBuffer start");
	NicoroFFmpegPlayer* pNicoro = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoro)) {
		return;
	}

	pNicoro->startJNI(env, thiz);
	pNicoro->createDrawBuffer(env, thiz, infoCallback);
	pNicoro->endJNI();
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_decodeFrame
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jintArray drawBuffer, jint skipVideoFrame, jbyteArray readBuffer, jobject ioCallback, jobject data) {
	LOGD("NicoroFFmpegPlayer#decodeFrame start");
	NicoroFFmpegPlayer* pNicoro = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoro)) {
		return NicoroFFmpegPlayer::CODE_DECODE_FRAME_ERROR_OR_END;
	}

	pNicoro->startJNI(env, thiz);
	jint ret = pNicoro->decodeFrame(env, thiz, drawBuffer, skipVideoFrame, readBuffer, ioCallback, data);
	pNicoro->endJNI();
	return ret;
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_readFrame
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray readBuffer, jobject ioCallback) {
	NicoroFFmpegPlayer* pNicoro = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoro)) {
		return NicoroFFmpegPlayer::CODE_FRAME_TYPE_ERROR_OR_END;
	}

	pNicoro->startJNI(env, thiz);
	jint ret = pNicoro->readFrame(env, thiz, readBuffer, ioCallback);
	pNicoro->endJNI();
	return ret;
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_decodeVideo
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jintArray drawBuffer, jobject data, jint skipVideoFrame) {
	NicoroFFmpegPlayer* pNicoro = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoro)) {
		return NicoroFFmpegPlayer::CODE_DECODE_FRAME_ERROR_OR_END;
	}

	pNicoro->startJNI(env, thiz);
	jint ret = pNicoro->decodeVideo(env, thiz, drawBuffer, data, skipVideoFrame);
	pNicoro->endJNI();
	return ret;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_decodeAudio
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jobject data) {
	NicoroFFmpegPlayer* pNicoro = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoro)) {
		return JNI_FALSE;
	}

	pNicoro->startJNI(env, thiz);
	bool ret = pNicoro->decodeAudio(env, thiz, data);
	pNicoro->endJNI();
	if (ret) {
		return JNI_TRUE;
	} else {
		return JNI_FALSE;
	}
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_seekFrameBySecond
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jint second) {
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return -1;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	int ret = pNicoroFFmpegPlayer->seekFrameBySecond(env, thiz, second);
	pNicoroFFmpegPlayer->endJNI();
	return ret;
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroFFmpegPlayer_reopenInputStream
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray buffer, jobject ioCallback) {
	LOGD("NicoroFFmpegPlayer#reopenInputStream start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->reopenInputStream(buffer, ioCallback);
	pNicoroFFmpegPlayer->endJNI();
}



JNIEXPORT jlong JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_createNative
  (JNIEnv * env, jobject thiz) {
	LOGD("NicoroSwfPlayer#createNative start");
	return reinterpret_cast<jlong>(new NicoroFFmpegPlayer(gVM));
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_destroyNative
  (JNIEnv * env, jobject thiz, jlong nativeInstance) {
	LOGD("NicoroSwfPlayer#destroyNative start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (pNicoroFFmpegPlayer != NULL) {
		delete pNicoroFFmpegPlayer;
	}
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_loadStreamADPCM
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray buffer, jobject ioCallback, jint timeBaseNumerator, jint timeBaseDenominator, jint sampleRate, jint channels) {
	LOGD("NicoroSwfPlayer#loadStreamADPCM start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->loadStreamADPCM(env, thiz, buffer, ioCallback, timeBaseNumerator, timeBaseDenominator, sampleRate, channels);
	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_loadStreamMP3
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray buffer, jobject ioCallback, jint timeBaseNumerator, jint timeBaseDenominator, jint sampleRate, jint channels) {
	LOGD("NicoroSwfPlayer#loadStreamMP3 start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->loadStreamMP3(env, thiz, buffer, ioCallback, timeBaseNumerator, timeBaseDenominator, sampleRate, channels);
	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_reopenInputStream
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray buffer, jobject ioCallback) {
	LOGD("NicoroSwfPlayer#reopenInputStream start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	pNicoroFFmpegPlayer->reopenInputStream(buffer, ioCallback);
	pNicoroFFmpegPlayer->endJNI();
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_readFrame
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jbyteArray readBuffer, jobject ioCallback) {
	LOGD("NicoroSwfPlayer#readFrame start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return NicoroFFmpegPlayer::CODE_FRAME_TYPE_ERROR_OR_END;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	jint ret = pNicoroFFmpegPlayer->readFrame(env, thiz, readBuffer, ioCallback);
	pNicoroFFmpegPlayer->endJNI();
	return ret;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_NicoroSwfPlayer_decodeAudio
  (JNIEnv * env, jobject thiz, jlong nativeInstance, jobject data) {
	LOGD("NicoroSwfPlayer#decodeAudio start");
	NicoroFFmpegPlayer* pNicoroFFmpegPlayer = reinterpret_cast<NicoroFFmpegPlayer *>(nativeInstance);
	if (!checkNotNullAndThrow(env, pNicoroFFmpegPlayer)) {
		return JNI_FALSE;
	}

	pNicoroFFmpegPlayer->startJNI(env, thiz);
	bool ret = pNicoroFFmpegPlayer->decodeAudio(env, thiz, data);
	pNicoroFFmpegPlayer->endJNI();
	if (ret) {
		return JNI_TRUE;
	} else {
		return JNI_FALSE;
	}
}
