#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <math.h>

#include "HA_interface.h"
#include "HA_clientinterface.h"
#include "HA_java.h"
#include "HA_log.h"

#define CLASS_NAME "jp/hark/embeddedhark/EmbeddedHARKMainActivity"
//#define CLASS_NAME "com/psol/invehiclelauncher/engine/VoiceControlEngine"
//#define CLASS_NAME "visualiser/audiovisualiser/MainActivity"

#define PI (3.14159265)

extern "C" {

JavaVM *g_vm;
jobject g_ref;
jclass g_clazz;

jboolean native_init(JNIEnv *env, jobject thiz)
{

	::g_ref = env->NewGlobalRef(thiz);
	::g_clazz = env->GetObjectClass(::g_ref);

	LOGD("native init");

	return(true);

}

static int jniRegisterNativeMethods(JNIEnv *env, const char *className, const JNINativeMethod *gMEthods, int numMethods)
{

	jclass clazz;

	LOGD("Registering %s natives\n", className);
	clazz = env->FindClass(className);
	if (clazz == NULL) {
		LOGD("Native registration unable to find class '%s'\n", className);
		return(JNI_ERR);
	}
	if (env->RegisterNatives(clazz, gMEthods, numMethods) < 0) {
		LOGD("RegisterNatives failed for '%s'\n", className);
		return(JNI_ERR);
	}
	return(JNI_OK);

}

jint jniCallbackJavaMethod(const char *className, const char *methodName, const char *signature)
{
	JNIEnv *env;
	jint ret;
	jclass clazz;
	jmethodID mid;
	
	ret = g_vm->GetEnv((void**) &env, JNI_VERSION_1_6);
	
	if(ret != JNI_OK){
		return ret;
	}
	
	if(className){
		clazz = env->FindClass(className);
		if(clazz == NULL){
			LOGD("Native callback unable to find class '%s'\n", className);
			return(JNI_ERR);
		}
	}
	else{ // If className is NULL string, use main Activity.
		clazz = env->FindClass(CLASS_NAME);
		if(clazz == NULL){
			LOGD("Native callback unable to find class '%s'\n", CLASS_NAME);
			return(JNI_ERR);
		}
	}
	
	mid = env->GetMethodID(clazz, methodName, signature);
	if(mid == NULL){
		LOGD("Native callback unable to get method '%s' signature '%s'\n", methodName, signature);
		return(JNI_ERR);
	}
	
	env->CallVoidMethod(clazz, mid);
	
	return(JNI_OK);
}

static JNINativeMethod sMethods[] = {
		{"native_init", "()Z", (void *)native_init},
		//
		//
		{"HACreate", "()I", (void *)HACreate},
		{"HAStart", "()I", (void *)HAStart},
		{"HAStop", "()I", (void *)HAStop},
		{"HASetParam", "()I", (void *)HASetParam},
		{"HAGetParam", "()I", (void *)HAGetParam},
		//
		{"HASetJuliusMFTServer", "(Ljava/lang/String;)I", (void *)HASetJuliusMFTServer},

		// K.Takagi
		//{"HASetRaspZXServer", "(ZLjava/lang/String;)I", (void *)HASetRaspZXServer},
		{"HASetSocketMicServer", "(ZLjava/lang/String;)I", (void *)HASetSocketMicServer},

		//{"HASetRaspLCDevice",    "(II)I",  (void *)HASetRaspLCDevice},
		//{"HASetRaspLCInterface", "(III)I", (void *)HASetRaspLCInterface},
		//{"HASetRaspLCFd",        "(I)I",   (void *)HASetRaspLCFd},
		{"HASetUSBMicDeviceType",    "(I)I",  (void *)HASetUSBMicDeviceType},
		{"HASetUSBMicDevice",    "(II)I",  (void *)HASetUSBMicDevice},
		{"HASetUSBMicInterface", "(III)I", (void *)HASetUSBMicInterface},
		{"HASetUSBMicFd",        "(I)I",   (void *)HASetUSBMicFd},

		//
		// K.Takagi
		{"HASetParamSendPort", "(I)I", (void *)HASetParamSendPort},

		{"HASetAudioParam",    "(II)I", (void *)HASetAudioParam},

		{"HASetLocalizeMusicParamNumSource", "(I)I",  (void *)HASetLocalizeMusicParamNumSource},
		{"HASetLocalizeMusicParamPeriod",    "(I)I",  (void *)HASetLocalizeMusicParamPeriod},
		{"HASetLocalizeMusicParamFrequency", "(II)I", (void *)HASetLocalizeMusicParamFrequency},
		{"HASetLocalizeMusicParamDeg",       "(II)I", (void *)HASetLocalizeMusicParamDeg},
		{"HASetLocalizeMusicParamFilePath",  "(Ljava/lang/String;)I", (void *)HASetLocalizeMusicParamFilePath},

		{"HASetSourceTrackerParamThresh",         "(F)I",  (void *)HASetSourceTrackerParamThresh},
		{"HASetSourceTrackerParamPauseLength",    "(F)I",  (void *)HASetSourceTrackerParamPauseLength},
		{"HASetSourceTrackerParamMinSrcInterval", "(F)I",  (void *)HASetSourceTrackerParamMinSrcInterval},

		{"HASetSourceIntervalExtenderParam", "(I)I",  (void *)HASetSourceIntervalExtenderParam},

		{"HASetGhdssParamFilePath",  "(Ljava/lang/String;)I", (void *)HASetGhdssParamFilePath},

		{"HASetEstimateLeakParamFactor",     "(F)I",  (void *)HASetEstimateLeakParamFactor},

		{"HASetHrleParamLx",         "(F)I",  (void *)HASetHrleParamLx},
		{"HASetHrleParamTr",         "(F)I",  (void *)HASetHrleParamTr},
		{"HASetHrleParamMinLv",      "(F)I",  (void *)HASetHrleParamMinLv},
		{"HASetHrleParamStepLv",     "(F)I",  (void *)HASetHrleParamStepLv},
		{"HASetHrleParamNumBin",     "(I)I",  (void *)HASetHrleParamNumBin},

		{"HASetWhiteNoiseAdderParamLevel",     "(F)I",  (void *)HASetWhiteNoiseAdderParamLevel},

		{"HASetPreEmphasisParamCutoff",   "(I)I",  (void *)HASetPreEmphasisParamCutoff},
		{"HASetPreEmphasisParamCoef",     "(F)I",  (void *)HASetPreEmphasisParamCoef},

		{"HASetMelFilterParamCutoff",   "(I)I",   (void *)HASetMelFilterParamCutoff},
		{"HASetMelFilterParamFreq",     "(II)I",  (void *)HASetMelFilterParamFreq},

		{"HASetHarkPath", "(Ljava/lang/String;)I", (void *)HASetHarkPath},
	
	//takigahira
		{"HASetParamOutputTypeMicRaw",          "(I)I",  (void *)HASetParamOutputTypeMicRaw},
		{"HASetParamOutputTypeMicSpec",         "(I)I",  (void *)HASetParamOutputTypeMicSpec},
		{"HASetParamOutputTypeLocalizePowSpec", "(I)I",  (void *)HASetParamOutputTypeLocalizePowSpec},
		{"HASetParamOutputTypeSepRaw",          "(I)I",  (void *)HASetParamOutputTypeSepRaw},
		{"HASetParamOutputTypeSepSpec",         "(I)I",  (void *)HASetParamOutputTypeSepSpec},
		{"HASetParamOutputTypeSepFeature",      "(II)I", (void *)HASetParamOutputTypeSepFeature},
		{"HASetParamOutputTypeSource",          "(I)I",  (void *)HASetParamOutputTypeSource},
	
		{"HASetParamOutputFilePathMicRaw",          "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathMicRaw},
		{"HASetParamOutputFilePathMicSpec",         "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathMicSpec},
		{"HASetParamOutputFilePathLocalizePowSpec", "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathLocalizePowSpec},
		{"HASetParamOutputFilePathSepRaw",          "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathSepRaw},
		{"HASetParamOutputFilePathSepSpec",         "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathSepSpec},
		{"HASetParamOutputFilePathSepFeature",      "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathSepFeature},
		{"HASetParamOutputFilePathSource",          "(Ljava/lang/String;)I", (void *)HASetParamOutputFilePathSource},
	
		{"HASetParamLocalizeAlgorithm",   "(I)I", (void *)HASetParamLocalizeAlgorithm},
		{"HASetParamSeparationAlgorithm", "(I)I", (void *)HASetParamSeparationAlgorithm},
		{"HASetParamFilterAlgorithm",     "(I)I", (void *)HASetParamFilterAlgorithm},
		{"HASetParamSIDAlgorithm",        "(I)I", (void *)HASetParamSIDAlgorithm},
	
		{"HASetExtendedSourceTrackerParamThresh",                 "(F)I", (void *)HASetExtendedSourceTrackerParamThresh},
		{"HASetExtendedSourceTrackerParamPauseLength",            "(I)I", (void *)HASetExtendedSourceTrackerParamPauseLength},
		{"HASetExtendedSourceTrackerParamRemoveLength",           "(I)I", (void *)HASetExtendedSourceTrackerParamRemoveLength},
		{"HASetExtendedSourceTrackerParamRemoveCheckLength",      "(I)I", (void *)HASetExtendedSourceTrackerParamRemoveCheckLength},
		{"HASetExtendedSourceTrackerParamCompareMode",            "(I)I", (void *)HASetExtendedSourceTrackerParamCompareMode},
		{"HASetExtendedSourceTrackerParamMinSrcInterval",         "(F)I", (void *)HASetExtendedSourceTrackerParamMinSrcInterval},
		{"HASetExtendedSourceTrackerParamMinTFIndexInterval",     "(I)I", (void *)HASetExtendedSourceTrackerParamMinTFIndexInterval},
		{"HASetExtendedSourceTrackerParamMaxNumSource",           "(I)I", (void *)HASetExtendedSourceTrackerParamMaxNumSource},
		{"HASetExtendedSourceTrackerParamPrerollLength",          "(I)I", (void *)HASetExtendedSourceTrackerParamPrerollLength},
		{"HASetExtendedSourceTrackerParamRemovedSourceAlgorithm", "(I)I", (void *)HASetExtendedSourceTrackerParamRemovedSourceAlgorithm},
		{"HASetExtendedSourceTrackerParamMinID",                  "(I)I", (void *)HASetExtendedSourceTrackerParamMinID},
	
		{"HASetNormalizeMusicParamInitialThreshold", "(F)I", (void *)HASetNormalizeMusicParamInitialThreshold},
		{"HASetNormalizeMusicParamActiveProp",       "(F)I", (void *)HASetNormalizeMusicParamActiveProp},
		{"HASetNormalizeMusicParamUpdateInterval",   "(I)I", (void *)HASetNormalizeMusicParamUpdateInterval},
		{"HASetNormalizeMusicParamPriorWeight",      "(F)I", (void *)HASetNormalizeMusicParamPriorWeight},
		{"HASetNormalizeMusicParamMaxSourceNum",     "(I)I", (void *)HASetNormalizeMusicParamMaxSourceNum},
		{"HASetNormalizeMusicParamParticleNum",      "(I)I", (void *)HASetNormalizeMusicParamParticleNum},
		{"HASetNormalizeMusicParamLocalPeakMargin",  "(F)I", (void *)HASetNormalizeMusicParamLocalPeakMargin},

		{"HASetNoneSeparationParamTargetChannel",  "(I)I", (void *)HASetNoneSeparationParamTargetChannel},
	
		{"HASetBeamformingParamBFMethod",               "(I)I", (void *)HASetBeamformingParamBFMethod},
		{"HASetBeamformingParamTFConjFilePath",         "(Ljava/lang/String;)I", (void *)HASetBeamformingParamTFConjFilePath},
		{"HASetBeamformingParamLCMethod",               "(I)I", (void *)HASetBeamformingParamLCMethod},
		{"HASetBeamformingParamLCMyu",                  "(F)I", (void *)HASetBeamformingParamLCMyu},
		{"HASetBeamformingParamSSMethod",               "(I)I", (void *)HASetBeamformingParamSSMethod},
		{"HASetBeamformingParamSSMyu",                  "(F)I", (void *)HASetBeamformingParamSSMyu},
		{"HASetBeamformingParamSSScale",                "(F)I", (void *)HASetBeamformingParamSSScale},
		{"HASetBeamformingParamNlFunc",                 "(I)I", (void *)HASetBeamformingParamNlFunc},
		{"HASetBeamformingParamDecompositionAlgorithm", "(I)I", (void *)HASetBeamformingParamDecompositionAlgorithm},
		{"HASetBeamformingParamAlpha",                  "(F)I", (void *)HASetBeamformingParamAlpha},
		{"HASetBeamformingParamRegFactor",              "(F)I", (void *)HASetBeamformingParamRegFactor},
	
		//{"HASetLogisticRegressionParamCepstrumNormalize", "(I)I", (void *)HASetLogisticRegressionParamCepstrumNormalize},
		//{"HASetLogisticRegressionParam", "(I)I", (void *)HASetLogisticRegressionParam},
	
		//
		//
		{"HACCreate", "()I", (void *)HACCreate},
		{"HACStart", "()I", (void *)HACStart},
		{"HACStop", "()I", (void *)HACStop},
		{"HACisStop", "()Z", (void *)HACisStop},
		//
		{"HACConnectPortA", "()I", (void *)HACConnectPortA},
		//{"HACRecvFromPortA", "(I[D)I", (void *)HACRecvFromPortA},
		{"HACRecvFromPortA", "(I[I[I[I[D)I", (void *)HACRecvFromPortA},
		{"HACClosePortA", "(I)I", (void *)HACClosePortA},
		//
		{"HACConnectPortB", "()I", (void *)HACConnectPortB},
		//{"HACRecvFromPortB", "(I[D)I", (void *)HACRecvFromPortB},
		{"HACRecvFromPortB", "(I[I[I[I[I[D)I", (void *)HACRecvFromPortB},
		{"HACClosePortB", "(I)I", (void *)HACClosePortB},
		//
		{"HACConnectPortC", "()I", (void *)HACConnectPortC},
		{"HACRecvFromPortC", "(I[I[I[D)I", (void *)HACRecvFromPortC},
		{"HACClosePortC", "(I)I", (void *)HACClosePortC},
		//
		{"HACConnectPortD", "()I", (void *)HACConnectPortD},
		//{"HACRecvFromPortD", "(I[D)I", (void *)HACRecvFromPortD},
		{"HACRecvFromPortD", "(I[I[I[I[D[D)I", (void *)HACRecvFromPortD},
		{"HACClosePortD", "(I)I", (void *)HACClosePortD},
		//
		{"HACConnectPortE", "()I", (void *)HACConnectPortE},
		//{"HACRecvFromPortE", "(I[D)I", (void *)HACRecvFromPortE},
		{"HACRecvFromPortE", "(I[I[I[I[D[D)I", (void *)HACRecvFromPortE},
		{"HACClosePortE", "(I)I", (void *)HACClosePortE},
		// TAKIGAHIRA of portF
		{"HACConnectPortF", "()I", (void *)HACConnectPortF},
		//{"HACRecvFromPortF", "(I[D)I", (void *)HACRecvFromPortF},
		{"HACRecvFromPortF", "(I[I[I[I[D[D)I", (void *)HACRecvFromPortF},
		{"HACClosePortF", "(I)I", (void *)HACClosePortF},
		// TAKIGAHIRA of portF
		{"HACConnectPortFwithSrc", "()I", (void *)HACConnectPortFwithSrc},
		//{"HACRecvFromPortFwithSrc", "(I[I[D)I", (void *)HACRecvFromPortFwithSrc},
		{"HACRecvFromPortFwithSrc", "(I[I[I[I[D[D)I", (void *)HACRecvFromPortFwithSrc},
		{"HACClosePortFwithSrc", "(I)I", (void *)HACClosePortFwithSrc}

};

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{

	::g_vm = vm;

	JNIEnv *env = NULL;
	jint result = -1;

	if ((result = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6)) != JNI_OK) {
		LOGE("GetEnv Error %d", result);
		return(result);
	}

	if ((result = ::jniRegisterNativeMethods(env, CLASS_NAME, sMethods, NELEM(sMethods))) != JNI_OK) {
		return(result);
	}

	return(JNI_VERSION_1_6);

}

}
