/*
 * IntCall.cpp
 * 
 * Created on 11.09.2004.
 *
 * $Id: IntCall.cpp,v 1.3 2006/04/19 20:54:55 grnull Exp $
 *
 * eaio: NativeCall - calling operating system methods from Java
 * Copyright (c) 2004-2006 Johann Burkard (<mailto:jb@eaio.com>)
 * <http://eaio.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <jni.h>
#include "com_eaio_nativecall_IntCall.h"

#ifdef _WINDOWS
#include <windows.h>
#include <winbase.h>
#endif

#ifdef _WINDOWS
#ifdef _X86_

#define _push(x) __asm { push x };

#endif
#endif

// NativeCall fields

extern jfieldID fieldFunction, fieldModule, fieldFunctionHandle,
 fieldModuleHandle, fieldLastErrorCode;

// Holder fields

extern jfieldID fieldHolderO;

// Classes

extern jclass classBoolean,/* classByte, classCharacter, classShort,*/
 classInteger, /* classLong, classFloat, classDouble,*/ classString,
 classByteArray, classCharArray, /*classIntArray,*/ classHolder;

// Wrapper class methods

extern jmethodID methodBooleanValue,/* methodByteValue, methodCharValue,
methodShortValue,*/ methodIntValue/*, methodLongValue, methodFloatValue,
methodDoubleValue*/;

// Constructors

extern jmethodID newIntegerInt, newBooleanBoolean;

/*
 * Class:     com_eaio_nativecall_IntCall
 * Method:    executeCall
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_eaio_nativecall_IntCall_executeCall
(JNIEnv *env, jobject obj) {

 jint functionHandle = env->GetIntField(obj, fieldFunctionHandle);
 jint outVal;

#ifdef _WINDOWS
#ifdef _X86_

 __asm {

  call functionHandle
  mov outVal, eax

 }

#endif
#endif

 env->SetIntField(obj, fieldLastErrorCode, GetLastError());

 return outVal;

}

/*
 * Class:     com_eaio_nativecall_IntCall
 * Method:    executeCall0
 * Signature: ([Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL Java_com_eaio_nativecall_IntCall_executeCall0
(JNIEnv *env, jobject obj, jobjectArray params) {
  
 const int len = env->GetArrayLength(params);
 
 int* arrays = NULL;
 if (!(arrays = new int[len])) {
  env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
  return 0;
 }
 memset(arrays, 0, (sizeof(int) * len));

 jobject param;

 for (int i = len - 1; i >= 0; i--) {  
  
  param = env->GetObjectArrayElement(params, i);
  
  if (param == NULL) {
   _push(0);
  }
  else if (env->IsInstanceOf(param, classInteger)) {
   int intArg = env->CallIntMethod(param, methodIntValue);
   _push(intArg)
  }
  else if (env->IsInstanceOf(param, classByteArray)) {
   char* byteArrayArg = (char*) env->GetPrimitiveArrayCritical((jarray) param, 0);
   arrays[i] = (int) &byteArrayArg;
   _push(byteArrayArg)
  }
  else if (env->IsInstanceOf(param, classCharArray)) {
   unsigned short* charArrayArg = (unsigned short*) env->GetPrimitiveArrayCritical(
    (jarray) param, 0);
   arrays[i] = (int) &charArrayArg;
   _push(charArrayArg)
  }
  else if (env->IsInstanceOf(param, classBoolean)) {
   jboolean booleanArg = env->CallBooleanMethod(param, methodBooleanValue);
   int tempArg = (booleanArg == JNI_FALSE ? 0 : 1);
   _push(tempArg)
  }
  else if (env->IsInstanceOf(param, classHolder)) {
   
   /* Holder */
   
   jobject o = env->GetObjectField(param, fieldHolderO);
   
   if (env->IsInstanceOf(o, classInteger)) {
    int *intPtr = new int;
    *intPtr = env->CallIntMethod(o, methodIntValue);
    arrays[i] = (int) intPtr;
    _push(intPtr);
   }
   else if (env->IsInstanceOf(o, classByteArray)) {
    char* byteArrayArg = (char*) env->GetPrimitiveArrayCritical((jarray) o, 0);
    arrays[i] = (int) &byteArrayArg;
    _push(byteArrayArg)
   }
   else if (env->IsInstanceOf(o, classCharArray)) {
    unsigned short* charArrayArg = (unsigned short*) env->GetPrimitiveArrayCritical(
     (jarray) o, 0);
    arrays[i] = (int) &charArrayArg;
    _push(charArrayArg)
   }
   else if (env->IsInstanceOf(o, classBoolean)) {
    jboolean booleanArg = env->CallBooleanMethod(o, methodBooleanValue);
    int *tempPtr = new int;
    *tempPtr = (booleanArg == JNI_FALSE ? 0 : 1);
    arrays[i] = (int) tempPtr;
    _push(tempPtr);
   }
   
   /* end Holder */
   
  }
  
 }

 jint functionHandle = env->GetIntField(obj, fieldFunctionHandle);
 jint outVal;
 
#ifdef _WINDOWS
#ifdef _X86_
 
 __asm {
  
  call functionHandle
  mov outVal, eax
   
 }
 
#endif
#endif
 
 for (int j = 0; j < len; ++j) {
  param = env->GetObjectArrayElement(params, j);
  if (param == NULL) {}
  else if (env->IsInstanceOf(param, classByteArray) || env->IsInstanceOf(param, classCharArray)) {
   env->ReleasePrimitiveArrayCritical((jarray) param, (void*) arrays[j], 0);
  }
  else if (env->IsInstanceOf(param, classHolder)) {
   
   jobject o = env->GetObjectField(param, fieldHolderO);

   if (env->IsInstanceOf(o, classInteger)) {
    int* out = (int*) arrays[j];
    env->SetObjectField(param, fieldHolderO, env->NewObject(classInteger, newIntegerInt, *out));
    delete out;
   }
   else if (env->IsInstanceOf(o, classByteArray) || env->IsInstanceOf(o, classCharArray)) {
    env->ReleasePrimitiveArrayCritical((jarray) o, (void*) arrays[j], 0);
   }
   else if (env->IsInstanceOf(o, classBoolean)) {
    int* out = (int*) arrays[j];
    env->SetObjectField(param, fieldHolderO, env->NewObject(classBoolean, newBooleanBoolean, (*out == 0 ? JNI_FALSE : JNI_TRUE)));
    delete out;
   }
   
  }

 }
 
 delete [] arrays;

 env->SetIntField(obj, fieldLastErrorCode, GetLastError());
 
 return outVal;
 
}
