#include "release.h"
#if (IS_DEBUG)
#define DEBUG_LOGD true
//#define DEBUG_RTMP_LOGLEVEL RTMP_LOGALL
//#define DEBUG_RTMP_LOGLEVEL RTMP_LOGWARNING
#define DEBUG_RTMP_LOGLEVEL RTMP_LOGDEBUG
#else
#define DEBUG_LOGD false
#define DEBUG_RTMP_LOGLEVEL RTMP_LOGERROR
#endif

#define  LOG_TAG    "NicoRoJNI_RTMP"
#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 <android/log.h>

#include <librtmp/rtmp.h>
#include <librtmp/log.h>

#include "JniWrapper.h"

#include "jp_sourceforge_nicoro_LiveVideoLoader.h"

struct RtmpData {
	RTMP* r;
	char* url;
};

static bool checkNotNullAndThrow(JNIEnv * env, RtmpData* p) {
	if (p == NULL || p->r == 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;
}

static void throwOutOfMemoryError(JNIEnv * env, const char* message) {
	jclass clsj = env->FindClass("java/lang/OutOfMemoryError");
	if (clsj != NULL) {
		env->ThrowNew(clsj, message);
		env->DeleteLocalRef(clsj);
	}
}

extern "C" {
	static void logCallback(int level, const char *fmt, va_list ap) {
		if (level > RTMP_debuglevel) {
			return;
		}

		int prio;
		switch (level) {
		case RTMP_LOGCRIT:
			prio = ANDROID_LOG_FATAL;
			break;
		case RTMP_LOGERROR:
			prio = ANDROID_LOG_ERROR;
			break;
		case RTMP_LOGWARNING:
			prio = ANDROID_LOG_WARN;
			break;
		case RTMP_LOGINFO:
			prio = ANDROID_LOG_INFO;
			break;
		case RTMP_LOGDEBUG:
		case RTMP_LOGDEBUG2:
			prio = ANDROID_LOG_DEBUG;
			break;
		case RTMP_LOGALL:
			prio = ANDROID_LOG_VERBOSE;
			break;
		default:
			prio = ANDROID_LOG_FATAL;
			break;
		}
		__android_log_vprint(prio, LOG_TAG, fmt, ap);
	}
}

JNIEXPORT jlong JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_init
  (JNIEnv * env, jclass) {
	RTMP_LogSetLevel(DEBUG_RTMP_LOGLEVEL);
	RTMP_LogSetCallback(&logCallback);

	RtmpData* p = new RtmpData();
	if (p == NULL) {
		throwOutOfMemoryError(env, "RtmpData Alloc failed");
		return 0L;
	}
	p->r = RTMP_Alloc();
	if (p->r == NULL) {
		throwOutOfMemoryError(env, "RTMP Alloc failed");
		delete p;
		return 0L;
	}
	RTMP_Init(p->r);
	return reinterpret_cast<jlong>(p);
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_close
  (JNIEnv * env, jclass, jlong nativeInstance) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return;
	}

//	RTMP_UserInterrupt();
	RTMP_Close(p->r);
}

JNIEXPORT void JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_free
  (JNIEnv * env, jclass, jlong nativeInstance) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (checkNotNullAndThrow(env, p)) {
		RTMP_Free(p->r);
	}
	if (p != NULL && p->url != NULL) {
		delete[] p->url;
	}
	delete p;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_setupURL
  (JNIEnv * env, jclass, jlong nativeInstance, jstring url) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	JAutoPtrUTFChars urlUtf8(env, url);
	if (urlUtf8.get() == NULL) {
		throwOutOfMemoryError(env, "convert String to UTF8 failed");
		return JNI_FALSE;
	}

//	return RTMP_SetupURL(p, const_cast<char*>(urlUtf8.get())) != FALSE;
	p->url = new char[strlen(urlUtf8.get() + 1)];
	strcpy(p->url, urlUtf8.get());
	return RTMP_SetupURL(p->r, p->url) != FALSE;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_connect
  (JNIEnv * env, jclass, jlong nativeInstance) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	return RTMP_Connect(p->r, NULL) != FALSE;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_connectStream
  (JNIEnv * env, jclass, jlong nativeInstance, jint seekTime) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	return RTMP_ConnectStream(p->r, seekTime) != FALSE;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_reconnectStream
  (JNIEnv * env, jclass, jlong nativeInstance, jint seekTime) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	return RTMP_ReconnectStream(p->r, seekTime) != FALSE;
}

JNIEXPORT jint JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_read
  (JNIEnv * env, jclass, jlong nativeInstance, jbyteArray buf, jint offset, jint length) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return -1;
	}

	JByteArray jaBufferForNative(env,
			buf,
			0);
	jbyte* pBufferForNative = jaBufferForNative.getElements();
	if (pBufferForNative == NULL) {
		throwOutOfMemoryError(env, "byte[] get elements failed");
		return -1;
	}

	return RTMP_Read(p->r, reinterpret_cast<char*>(pBufferForNative + offset), length);
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_pause
  (JNIEnv * env, jclass, jlong nativeInstance, jboolean doPause) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	return RTMP_Pause(p->r, static_cast<int>(doPause)) != FALSE;
}

JNIEXPORT jboolean JNICALL Java_jp_sourceforge_nicoro_LiveVideoLoader_sendSeek
  (JNIEnv * env, jclass, jlong nativeInstance, jint dTime) {
	RtmpData* p = reinterpret_cast<RtmpData*>(nativeInstance);
	if (!checkNotNullAndThrow(env, p)) {
		return JNI_FALSE;
	}

	return RTMP_SendSeek(p->r, dTime) != FALSE;
}
