#include "JPeerSDK.h"

using namespace PeerSDK;

#define TAG "__remote.demo.Native.PeerSDK.Peer.Peer__"

struct fields_t {
    jfieldID  ptr;
	jfieldID  ptrAddress;
	jfieldID  jptr;
};
static fields_t fields;

static jmethodID  NativeObjectGetNativeAddressMethodID = 0;

static jmethodID methods_PeerStream_getNativeAddress;
static jclass clazz_PeerStream = 0;

//----------------------------------

class JPeer : public JObject
{
public:
	JPeer(JNIEnv *env, jobject thiz)
	: JObject(env, thiz)
	{
		mPeer = new Peer();
		mPeerAddress = (jlong) &mPeer;

		env->SetLongField(thiz, fields.jptr, (jlong) this);
		env->SetLongField(thiz, fields.ptr, (jlong) mPeer);
		env->SetLongField(thiz, fields.ptrAddress, (jlong) &mPeer);
	}

	virtual ~JPeer()
    {
		delete mPeer;
    }
	
public:
	void InitObjects(JNIEnv* env, jobject thiz)
	{
		mSelf = env->NewGlobalRef(thiz);
	}
	
	void DisposeObjects(JNIEnv* env)
	{
		if (mSelf != NULL)
			env->DeleteGlobalRef(mSelf);
	}

private:
	jobject   mSelf;


private:	
	Peer*  mPeer;
	jlong  mJPeer;
	
public:	
	Peer*  getPeer() const        { return mPeer; }
	jlong  getJPeer() const       { return (jlong) this; }	
	void   Dispose()              { delete mPeer; }
};

JNIEXPORT void JNICALL remote_demo_Native_PeerSDK_Peer_Peer_nativeInit
  (JNIEnv *env, jobject thiz)
{
	JPeer* jpeer = new JPeer(env, thiz);
	jpeer->InitObjects(env, thiz);
}

JNIEXPORT void JNICALL remote_demo_Native_PeerSDK_Peer_Peer_nativeRelease
  (JNIEnv *env, jobject thiz)
{
	JPeer* jpeer = (JPeer*) env->GetLongField(thiz, fields.jptr);
	if (jpeer == NULL)
		return;
	jpeer->DisposeObjects(env);
	delete jpeer;
}

JNIEXPORT jboolean JNICALL remote_demo_Native_PeerSDK_Peer_Peer_connect
  (JNIEnv *env, jobject thiz, jstring host, jint port, jstring user, jstring password, jstring type, jboolean tryOthers)
{
	JPeer* jpeer = (JPeer*) env->GetLongField(thiz, fields.jptr);
	Peer* peer = jpeer->getPeer();

	const char* _host = env->GetStringUTFChars(host, 0); // Obtain a C-copy of the Java string
	const char* _user = env->GetStringUTFChars(user, 0);
	const char* _password = env->GetStringUTFChars(password, 0);
	const char* _type = env->GetStringUTFChars(type, 0);

	PeerResult result = peer->Connect(_host, (int32) port, _user, _password, _type, (bool) tryOthers);

	////////////////////////////
	
	if (_host != NULL)
		env->ReleaseStringUTFChars(host, _host);
	if (_user != NULL)
		env->ReleaseStringUTFChars(user, _user);	
	if (_password != NULL)
		env->ReleaseStringUTFChars(password, _password);
	if (_type != NULL)
		env->ReleaseStringUTFChars(type, _type);

	return (jboolean)(bool) result;
}

JNIEXPORT void JNICALL remote_demo_Native_PeerSDK_Peer_Peer_createLiveStream
  (JNIEnv *env, jobject thiz, jobject stream)
{
	JPeer* jpeer = (JPeer*) env->GetLongField(thiz, fields.jptr);
	Peer*  peer  = *(Peer**) jpeer->getPeerAddress();
	
	jlong value = 0;
	GET_NATIVE_ADDRESS_OF_NATIVEOBJECT(stream, NativeObjectGetNativeAddressMethodID, value);
	peer->CreateLiveStream((PeerStream**) value);
}

 
JNIEXPORT void JNICALL remote_demo_Native_PeerSDK_Peer_Peer_ack
  (JNIEnv *env, jobject thiz)
{
	JPeer* jpeer = (JPeer*) env->GetIntField(thiz, fields.jptr);
	Peer* peer = jpeer->getPeer();
	peer->Ack();
}

/*
 * Table of methods associated with a single class.
 */
static JNINativeMethod gMethods[] = 
{
	// Method, Signature, FuncPtr
	{"nativeInit",           "()V",                      (void*)remote_demo_Native_PeerSDK_Peer_Peer_nativeInit },
	{"nativeRelease",        "()V",                      (void*)remote_demo_Native_PeerSDK_Peer_Peer_nativeRelease },
	{"connect",              "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Z",
			(void*)remote_demo_Native_PeerSDK_Peer_Peer_connect },
	{"createLiveStream",     "(Lremote/demo/Native/PeerSDK/Peer/Stream/PeerStream;)V",
			(void*)remote_demo_Native_PeerSDK_Peer_Peer_createLiveStream },
	{"ack",                  "()V",                      (void*)remote_demo_Native_PeerSDK_Peer_Peer_ack },
	
};

/*
 * Returns the JNI version on success, -1 on failure.
 */
void InitJNIGlobalRef(JNIEnv* env)
{
	// get feildID of "Peer" Java object
	jclass thizClassRef = env->FindClass("remote/demo/Native/PeerSDK/Peer/Peer");
	fields.ptr        = env->GetFieldID(thizClassRef, "mPtr", "J");
	fields.ptrAddress = env->GetFieldID(thizClassRef, "mPtrAddress", "J");
	fields.jptr       = env->GetFieldID(thizClassRef, "mJPtr", "J");
	env->DeleteLocalRef(thizClassRef);
	
	//////////////////////////////////////////////////////////////////////
	// get class reference
	jniNewClassGlobalReference(env, clazz_PeerStream,      "remote/demo/Native/PeerSDK/Peer/Stream/PeerStream");

	//////////////////////////////////////////////////////////////////////
	// get methodID
	methods_PeerStream_getNativeAddress = env->GetMethodID(clazz_PeerStream, "getNativeAddress","()J");

}
DEFINE_REGISTER_CLASS(remote_demo_Native_PeerSDK_Peer_Peer, remote/demo/Native/PeerSDK/Peer/Peer);

extern jint register_remote_demo_Native_PeerSDK_Peer_Stream_PeerStream(JavaVM* vm, void* reserved);



#define INVOKE_REGISTER_CLASS_METHOD(Clazz) register_##Clazz(vm, reserved)
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	jint result = -1;

	LOGI("JNI_OnLoad");
	if (register_remote_demo_Native_PeerSDK_Peer_Peer(vm, reserved) < 0)
	{
		LOGE("ERROR: remote_demo_Native_PeerSDK_Peer_Peer native registration failed\n");
        goto bail;
	}

	if (register_remote_demo_Native_PeerSDK_Peer_Stream_PeerStream(vm, reserved) < 0)
	{
		LOGE("ERROR: remote_demo_Native_PeerSDK_Peer_Stream native registration failed\n");
        goto bail;
	}
	
	/* success -- return valid version number */
    result = JNI_VERSION_1_4;

bail:
    return result;
}
