/*
-------------------------------------------------------------------------------
This file is part of OgreKit.
http://gamekit.googlecode.com/

Copyright (c) 2006-2010 zcube(JiSeop Moon).

Contributor(s): harkon.kr.
-------------------------------------------------------------------------------
gamekit\Samples\AndroidDemo\Shared\Main.cpp


-------------------------------------------------------------------------------
*/

#include "OgreKit.h"
#include "Ogre.h"
#include "android/AndroidInputManager.h"
#include <stdlib.h>
#include <pthread.h>

//extern static void onJavaDetach(void* arg);
#include <jni.h>
#include <stdlib.h>
#include "AndroidLogListener.h"
#include <android/log.h>

#define LOG_TAG "OgreKit"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOG_FOOT LOGI("%s %s %d", __FILE__, __FUNCTION__, __LINE__)

const gkString gkDefaultBlend = "/sdcard/gk_android.blend";
const gkString gkDefaultConfig = "/sdcard/OgreKitStartup.cfg";


class OgreKit : public gkCoreApplication, public gkMessageManager::GenericMessageListener
{
public:
gkString m_blend;
gkScene* m_scene;
OIS::AndroidInputManager* m_input;

public:
OgreKit(): m_blend(gkDefaultBlend), m_scene(0), m_input(0) { };
virtual ~OgreKit() {}
bool init(const gkString& blend,JavaVM* vm);
void keyReleased(const gkKeyboard& key, const gkScanCode& sc);
void injectKey(int action, int uniChar, int keyCode) { if (m_input) m_input->injectKey(action, uniChar, keyCode); }
void injectTouch(int action, float x, float y) { if (m_input) m_input->injectTouch(action, x, y); }
void injectAcceleration(float x,float y, float z) { if (m_input) m_input->injectAcceleration(x,y,z);}
void setOffsets(int x, int y) { if (m_input) m_input->setOffsets(x,y); }
void setWindowSize(int w, int h) { if (m_input) m_input->setWindowSize(w,h); }
void handleMessage(gkMessageManager::Message* message);
JNIEnv* GetEnv();
// jstring JNU_NewStringNative(JNIEnv *env, char *str);
private:
JavaVM* mJVM;
bool setup(void);
jmethodID mFireString;
};

//
//jstring OgreKit::JNU_NewStringNative(JNIEnv *env, char *str)
//{
// jstring result;
// jbyteArray bytes = 0;
// int len;
// if ((env)->EnsureLocalCapacity(2) < 0) {
// return NULL; /* out of memory error */
// }
// len = strlen(str);
// bytes = (env)->NewByteArray(len);
// if (bytes != NULL) {
// (env)->SetByteArrayRegion( bytes, 0, len,
// (jbyte *)str);
// result = (env)->NewObject( Class_java_lang_String,
// MID_String_init, bytes);
// (env)->DeleteLocalRef(bytes);
// return result;
// } /* else fall through */
// return NULL;
//}

void OgreKit::handleMessage(gkMessageManager::Message* message){
LOGI("HANDLE MSG %s ",message->m_subject.c_str());

JNIEnv* env = this->GetEnv();

jclass ANDROID_MAIN = (env)->FindClass( "org/gamekit/jni/Main");
if (!ANDROID_MAIN)
{
LOGI("COULDNT FIND MAIN!!!");
return ;
}

//调用Java静态方法
mFireString = (env)->GetStaticMethodID(ANDROID_MAIN, "fireStringMessage", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");

// jstring from = this->JNU_NewStringNative(env,message->m_subject.c_str());
jstring from = env->NewStringUTF(message->m_from.c_str());
jstring to = env->NewStringUTF(message->m_to.c_str());
jstring subject = env->NewStringUTF(message->m_subject.c_str());
jstring body = env->NewStringUTF(message->m_body.c_str());
env->CallStaticVoidMethod(ANDROID_MAIN,mFireString, from,to,subject,body);

}

JNIEnv* OgreKit::GetEnv()
{
JNIEnv* env = NULL;
if (mJVM) (mJVM)->GetEnv((void**)&env, JNI_VERSION_1_2);
return env;
}

bool OgreKit::init(const gkString& blend,JavaVM* vm)
{
gkPrintf("----------- OgreKit Android Demo init -----------------");
LOG_FOOT;

gkString cfgfname;
mJVM = vm;
// Parse command line
m_blend = gkDefaultBlend;
if (!blend.empty()) m_blend = blend;

getPrefs().wintitle = gkString("OgreKit Demo (Press Escape to exit)[") + m_blend + gkString("]");
getPrefs().blendermat=true;
// getPrefs().viewportOrientation="portrait";
// m_prefs.disableSound=false;
gkPath path = cfgfname;

LOG_FOOT;

// overide settings if found
if (path.isFileInBundle())
getPrefs().load(path.getPath());

LOG_FOOT;

bool ok = initialize();
LOG_FOOT;
gkMessageManager::getSingleton().addListener(this);
return ok;
}


bool OgreKit::setup(void)
{
LOG_FOOT;

gkBlendFile* blend = gkBlendLoader::getSingleton().loadFile(gkUtils::getFile(m_blend), gkBlendLoader::LO_ALL_SCENES);
if (!blend)
{
LOGI("File loading failed.\n");
return false;
}

LOG_FOOT;

m_scene = blend->getMainScene();
if (!m_scene)
{
LOGI("No usable scenes found in blend.\n");
return false;
}

LOG_FOOT;

m_scene->createInstance();

LOG_FOOT;

// add input hooks
gkWindow* win = gkWindowSystem::getSingleton().getMainWindow();
m_input = static_cast<OIS::AndroidInputManager*>(win->getInputManager());

LOG_FOOT;


return true;
}




OgreKit okit;
//Ogre::LogManager gLogManager;
AndroidLogListener gLogListener;
gkString file;

jboolean init(JNIEnv* env, jobject thiz, jstring arg)
{
file = gkDefaultBlend;
const char* str = env->GetStringUTFChars(arg, 0);
if (str)
{
file = str;
env->ReleaseStringUTFChars(arg, str);
}

return JNI_TRUE;
}

JavaVM* vmPointer;

jboolean render(JNIEnv* env, jobject thiz, jint drawWidth, jint drawHeight, jboolean forceRedraw)
{
//LOG_FOOT;
static bool first = true;
if (first)
{
first = false;
LOG_FOOT;

okit.getPrefs().winsize.x = drawWidth;
okit.getPrefs().winsize.y = drawHeight;

gkLogger::enable("OgreKitDemo.log", true);
Ogre::LogManager::getSingleton().getDefaultLog()->addListener(&gLogListener);

LOG_FOOT;

// LOGI("****** %s ******", file.c_str());


LOG_FOOT;

okit.getPrefs().verbose = true;
if (!okit.init(file,vmPointer))
{
LOG_FOOT;
// error
return JNI_FALSE;
}
gkLogger::write("window");
gkWindow* win = gkWindowSystem::getSingleton().getMainWindow();
gkLogger::write("window");
okit.m_input = static_cast<OIS::AndroidInputManager*>(win->getInputManager());
gkLogger::write("window");

LOG_FOOT;
gkLogger::write("steploop");

gkEngine::getSingleton().initializeStepLoop();
gkLogger::write("window");

LOG_FOOT;
okit.setWindowSize(drawWidth, drawHeight);
gkLogger::write("window");

}

// gkLogger::write("steponeframe");

if (gkEngine::getSingleton().stepOneFrame())
return JNI_TRUE;
else
return JNI_FALSE;
}



void cleanup(JNIEnv* env)
{
LOG_FOOT;
gkEngine::getSingleton().finalizeStepLoop();
}

jboolean inputEvent(JNIEnv* env, jobject thiz, jint action, jfloat mx, jfloat my)
{
// LOG_FOOT;
okit.injectTouch(action, mx, my);
return JNI_TRUE;
}

jboolean keyEvent(JNIEnv* env, jobject thiz, jint action, jint unicodeChar, jint keyCode, jobject keyEvent)
{
// LOG_FOOT;
okit.injectKey(action, unicodeChar, keyCode);
return JNI_TRUE;
}

void setOffsets(JNIEnv* env, jobject thiz, jint x, jint y)
{
// LOGI("%s %d %d", __FUNCTION__, x, y);
okit.setOffsets(x,y);
}

void sendSensor(JNIEnv *env, jclass thiz, jint type, jfloat x, jfloat y, jfloat z) {
// LOGI("%d %f %f %f", __FUNCTION__, type,x, y,z);
okit.injectAcceleration(x,y,z);
}

/*
* Class: org_gamekit_jni_GameKitJNI
* Method: sendMessage
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*/
void sendMessage
(JNIEnv *env, jclass thiz, jstring jfrom, jstring jto, jstring jsubject, jstring jbody){

const char* str = env->GetStringUTFChars(jfrom, 0);
gkString from = str;
env->ReleaseStringUTFChars(jfrom,str);

const char* str2 = env->GetStringUTFChars(jto, 0);
gkString to = str2;
env->ReleaseStringUTFChars(jto,str2);

const char* str3 = env->GetStringUTFChars(jsubject, 0);
gkString subject = str3;
env->ReleaseStringUTFChars(jsubject,str3);

const char* str4 = env->GetStringUTFChars(jbody, 0);
gkString body = str4;
env->ReleaseStringUTFChars(jbody,str4);

if (gkMessageManager::getSingletonPtr()) {
gkMessageManager::getSingletonPtr()->sendMessage(from,to,subject,body);
}
// LOGI("%s %s %s %s", from.c_str(),to.c_str(),subject.c_str(),body.c_str());
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
// EXPORT_JNI_OnLoad(vm,reserved);

JNIEnv *env;

vmPointer = vm;

LOGI("JNI_OnLoad called");
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
{
LOGE("Failed to get the environment using GetEnv()");
return -1;
}
JNINativeMethod methods[] =
{
{
"init",
"(Ljava/lang/String;)Z",
(void *) init
},
{
"render",
"(IIZ)Z",
(void *) render
},
{
"inputEvent",
"(IFFLandroid/view/MotionEvent;)Z",
(void *) inputEvent
},
{
"keyEvent",
"(IIILandroid/view/KeyEvent;)Z",
(void *) keyEvent
},
{
"cleanup",
"()V",
(void *) cleanup
},
{
"setOffsets",
"(II)V",
(void *) setOffsets
},
{
"sendSensor",
"(IFFF)V",
(void *) sendSensor
},
{
"sendMessage",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
(void *) sendMessage
}

};
jclass k;
k = (env)->FindClass ("org/gamekit/jni/Main");
(env)->RegisterNatives(k, methods, 8);

return JNI_VERSION_1_4;
}