// Copyright 2022 Alibaba Group Holding Limited.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _Included_pyutils
#define _Included_pyutils

#define CLASS_TABLE(F)                                            \
  F(JBOOLEAN_OBJ_TYPE, "java/lang/Boolean")                       \
  F(JNUMBER_TYPE, "java/lang/Number")                             \
  F(JCHARSEQUENCE_TYPE, "java/lang/CharSequence")                 \
  F(JBYTE_OBJ_TYPE, "java/lang/Byte")                             \
  F(JSHORT_OBJ_TYPE, "java/lang/Short")                           \
  F(JINT_OBJ_TYPE, "java/lang/Integer")                           \
  F(JLONG_OBJ_TYPE, "java/lang/Long")                             \
  F(JFLOAT_OBJ_TYPE, "java/lang/Float")                           \
  F(JDOUBLE_OBJ_TYPE, "java/lang/Double")                         \
  F(JCHAR_OBJ_TYPE, "java/lang/Character")                        \
  F(JSTRING_TYPE, "java/lang/String")                             \
  F(JBIGDECIMAL_TYPE, "java/math/BigDecimal")                     \
  F(JBIGINTEGER_TYPE, "java/math/BigInteger")                     \
  F(JBOOLEAN_ARRAY_TYPE, "[Z")                                    \
  F(JBYTE_ARRAY_TYPE, "[B")                                       \
  F(JCHAR_ARRAY_TYPE, "[C")                                       \
  F(JSHORT_ARRAY_TYPE, "[S")                                      \
  F(JINT_ARRAY_TYPE, "[I")                                        \
  F(JLONG_ARRAY_TYPE, "[J")                                       \
  F(JFLOAT_ARRAY_TYPE, "[F")                                      \
  F(JDOUBLE_ARRAY_TYPE, "[D")                                     \
  F(JSTRING_ARRAY_TYPE, "[Ljava/lang/String;")                    \
  F(JOBJECT_ARRAY_TYPE, "[Ljava/lang/Object;")                    \
  F(JUTILDATE_TYPE, "java/util/Date")                             \
  F(JSQLDATE_TYPE, "java/sql/Date")                               \
  F(JSQLTIME_TYPE, "java/sql/Time")                               \
  F(JSQLTIMESTAMP_TYPE, "java/sql/Timestamp")                     \
  F(JLOCALDATE_TYPE, "java/time/LocalDate")                       \
  F(JLOCALTIME_TYPE, "java/time/LocalTime")                       \
  F(JLOCALDATETIME_TYPE, "java/time/LocalDateTime")               \
  F(JITERABLE_TYPE, "java/lang/Iterable")                         \
  F(JITERATOR_TYPE, "java/util/Iterator")                         \
  F(JCOLLECTION_TYPE, "java/util/Collection")                     \
  F(JLIST_TYPE, "java/util/List")                                 \
  F(JARRAYLIST_TYPE, "java/util/ArrayList")                       \
  F(JMAP_TYPE, "java/util/Map")                                   \
  F(JHASHMAP_TYPE, "java/util/HashMap")                           \
  F(JMAP_ENTRY_TYPE, "java/util/Map$Entry")                       \
  F(JILLEGAL_STATE_EXEC_TYPE, "java/lang/IllegalStateException")  \
  F(JNOSUCHELEMENT_EXEC_TYPE, "java/util/NoSuchElementException") \
  F(JPYTHONEXCE_TYPE, "pemja/core/PythonException")               \
  F(JPYITERPRETER_TYPE, "pemja/core/object/PyIterator")           \
  F(JPYOBJECT_TYPE, "pemja/core/object/PyObject")                 \
  F(JTHROWABLE_TYPE, "java/lang/Throwable")                       \
  F(JSTACK_TRACE_ELEMENT_TYPE, "java/lang/StackTraceElement")     \
  F(JCONSTRUCTOR_TYPE, "java/lang/reflect/Constructor")           \
  F(JFIELD_TYPE, "java/lang/reflect/Field")                       \
  F(JMETHOD_TYPE, "java/lang/reflect/Method")                     \
  F(JMEMBER_TYPE, "java/lang/reflect/Member")                     \
  F(JMODIFIER_TYPE, "java/lang/reflect/Modifier")                 \
  F(JCLASS_TYPE, "java/lang/Class")                               \
  F(JOBJECT_TYPE, "java/lang/Object")

// Define primitive class type.
extern jclass JBOOLEAN_TYPE;
extern jclass JBYTE_TYPE;
extern jclass JSHORT_TYPE;
extern jclass JINT_TYPE;
extern jclass JLONG_TYPE;
extern jclass JFLOAT_TYPE;
extern jclass JDOUBLE_TYPE;
extern jclass JCHAR_TYPE;

// define void class type
extern jclass JVOID_TYPE;

// Define an extern variable for everything in the class table.
#define DEFINE_CLASS_GLOBAL(var, name) extern jclass var;
CLASS_TABLE(DEFINE_CLASS_GLOBAL)

#define CACHE_CLASS(var, name)              \
  if (var == NULL) {                        \
    clazz = (*env)->FindClass(env, name);   \
    var = (*env)->NewGlobalRef(env, clazz); \
    (*env)->DeleteLocalRef(env, clazz);     \
  }

#define UNREF_CACHE_CLASS(var, name)   \
  if (var != NULL) {                   \
    (*env)->DeleteGlobalRef(env, var); \
    var = NULL;                        \
  }

#define CACHE_PRIMITIVE_CLASS(primitive, primitive_array)     \
  if (primitive == NULL) {                                    \
    clazz = JavaClass_getComponentType(env, primitive_array); \
    primitive = (*env)->NewGlobalRef(env, clazz);             \
    (*env)->DeleteLocalRef(env, clazz);                       \
  }

// ----------------- Upper and lower bounds of Number type.
// --------------------------
#define JBYTE_MIN -128
#define JBYTE_MAX 127

#define JSHORT_MIN -32768
#define JSHORT_MAX 32767

// Some compilers overflow parsing -2147483648 so derive it from JINT_MAX.
#define JINT_MIN (-1 * JINT_MAX - 1)
#define JINT_MAX 2147483647

#define JLONG_MAX 9223372036854775807
// Some compilers overflow parsing -9223372036854775808, so derive it from
// JLONG_MAX.
#define JLONG_MIN (-1 * JLONG_MAX - 1)

#define JCHAR_MAX 0xFFFF

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

//  ------------------------------- Java Type ID
//  ----------------------------------------
#define JBOOLEAN_ID 0
#define JBYTE_ID 1
#define JSHORT_ID 2
#define JINT_ID 3
#define JLONG_ID 4
#define JFLOAT_ID 5
#define JDOUBLE_ID 6
#define JCHAR_ID 7
#define JSTRING_ID 8
#define JVOID_ID 9
#define JOBJECT_ID 10
#define JBYTES_ID 11
#define JLIST_ID 12
#define JMAP_ID 13
#define JARRAY_ID 14

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

/* Function to cache java classes in CLASS_TABLE */
JcpAPI_FUNC(void) Jcp_CacheClasses(JNIEnv *env);

/* Function to unref java classes in CLASS_TABLE */
JcpAPI_FUNC(void) Jcp_UnRefCacheClasses(JNIEnv *env);

/* Function to return a const char* from a Java String Object */
JcpAPI_FUNC(const char *) JcpString_FromJString(JNIEnv *, jstring);

/* Function to release the memory of const char* converted by Java String Object
 */
JcpAPI_FUNC(void) JcpString_Clear(JNIEnv *, jstring, const char *);

/* Function to get the Object Id from the Java Class */
JcpAPI_FUNC(int) JcpJObject_GetObjectId(JNIEnv *, jclass);

// ----------------------- Java object to Python object
// ---------------------------------

/* Function to returns the match degree of the PyObject and the jclass */
JcpAPI_FUNC(int) JcpPyObject_match(JNIEnv *, PyObject *, jclass);

/* Function to return a Python Object from a Java Object */
JcpAPI_FUNC(PyObject *) JcpPyObject_FromJObject(JNIEnv *, jobject);

/* Functions to return a Python primitive object from a C primitive value */
JcpAPI_FUNC(PyObject *) JcpPyBool_FromLong(long);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromInt(int);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromLong(jlong);
JcpAPI_FUNC(PyObject *) JcpPyFloat_FromDouble(double);
JcpAPI_FUNC(PyObject *) JcpPyString_FromChar(jchar);

/* Function to return a Python String object from a Java String object */
JcpAPI_FUNC(PyObject *) JcpPyString_FromJString(JNIEnv *, jstring);

/* Functions to return a Python primitive object from a Java object */
JcpAPI_FUNC(PyObject *) JcpPyBool_FromJBoolean(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromJByte(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromJShort(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromJInteger(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyInt_FromJLong(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyFloat_FromJFloat(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyFloat_FromJDouble(JNIEnv *, jobject);
JcpAPI_FUNC(PyObject *) JcpPyString_FromJChar(JNIEnv *, jobject);

/* Function to return a Python bytes from a Java byte array */
JcpAPI_FUNC(PyObject *) JcpPyBytes_FromJByteArray(JNIEnv *, jbyteArray);

/* Function to return a Python Tuple from a Java boolean array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJBooleanArray(JNIEnv *, jbooleanArray);

/* Function to return a Python Tuple from a Java short array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJShortArray(JNIEnv *, jshortArray);

/* Function to return a Python Tuple from a Java int array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJIntArray(JNIEnv *, jintArray);

/* Function to return a Python Tuple from a Java long array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJLongArray(JNIEnv *, jlongArray);

/* Function to return a Python Tuple from a Java float array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJFloatArray(JNIEnv *, jfloatArray);

/* Function to return a Python Tuple from a Java double array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJDoubleArray(JNIEnv *, jdoubleArray);

/* Function to return a Python Tuple from a Java object array */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJObjectArray(JNIEnv *, jobjectArray);

/* Function to return a Python Tuple from a Java Map entry */
JcpAPI_FUNC(PyObject *) JcpPyTuple_FromJMapEntry(JNIEnv *, jobject);

/* Function to return a Python List from a Java List object */
JcpAPI_FUNC(PyObject *) JcpPyList_FromJListObject(JNIEnv *, jobject);

/* Function to return a Python Dict from a Java Map object */
JcpAPI_FUNC(PyObject *) JcpPyDict_FromJMap(JNIEnv *, jobject);

/* Function to return a Python Date from a Java Sql Date object */
JcpAPI_FUNC(PyObject *) JcpPyDate_FromJSqlDate(JNIEnv *, jobject);

/* Function to return a Python Time from a Java Sql Time object */
JcpAPI_FUNC(PyObject *) JcpPyTime_FromJSqlTime(JNIEnv *, jobject);

/* Function to return a Python DateTime from a Java Sql Timestamp object */
JcpAPI_FUNC(PyObject *) JcpPyDateTime_FromJSqlTimestamp(JNIEnv *, jobject);

/* Function to return a Python Decimal from a Java BigDecimal object */
JcpAPI_FUNC(PyObject *) JcpPyDecimal_FromJBigDecimal(JNIEnv *, jobject);

/* Function to return a Python Decimal from a Java BigInteger object */
JcpAPI_FUNC(PyObject *)
    JcpPyDecimal_FromJBigInteger(JNIEnv *env, jobject value);

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

// -----------------------   Python object to Java object
// -----------------------------

/* Function to return a Java Object from a Python Object */
JcpAPI_FUNC(jobject) JcpPyObject_AsJObject(JNIEnv *, PyObject *, jclass);

/* Function to return a jvalue from a Python Object */
JcpAPI_FUNC(jvalue) JcpPyObject_AsJValue(JNIEnv *, PyObject *, jclass);

/* Functions to return a Java primitive value from a Python primitive object */
JcpAPI_FUNC(jboolean) JcpPyBool_AsJBoolean(PyObject *);
JcpAPI_FUNC(jbyte) JcpPyInt_AsJByte(PyObject *);
JcpAPI_FUNC(jshort) JcpPyInt_AsJShort(PyObject *);
JcpAPI_FUNC(jint) JcpPyInt_AsJInt(PyObject *);
JcpAPI_FUNC(jlong) JcpPyInt_AsJLong(PyObject *);
JcpAPI_FUNC(jfloat) JcpPyFloat_AsJFloat(PyObject *);
JcpAPI_FUNC(jdouble) JcpPyFloat_AsJDouble(PyObject *);

/* Functions to return a Java primitive wrapper object from a Python primitive
 * object */
JcpAPI_FUNC(jobject) JcpPyBool_AsJObject(JNIEnv *, PyObject *, jclass);
JcpAPI_FUNC(jobject) JcpPyInt_AsJObject(JNIEnv *, PyObject *, jclass);
JcpAPI_FUNC(jobject) JcpPyFloat_AsJObject(JNIEnv *, PyObject *, jclass);

/* Function to return a long from a Python Int Object */
JcpAPI_FUNC(long) JcpPyInt_AsLong(PyObject *);

/* Function to return a long from a Python Int Object */
JcpAPI_FUNC(long long) JcpPyInt_AsLongLong(PyObject *);

/* Function to return a Java Object from a Python bytes value */
JcpAPI_FUNC(jobject) JcpPyBytes_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java Object from a Python String value */
JcpAPI_FUNC(jobject) JcpPyString_AsJObject(JNIEnv *, PyObject *, jclass);

/* Function to return a Java char value from a Python String Object */
JcpAPI_FUNC(jchar) JcpPyString_AsJChar(PyObject *);

/* Function to return a Java String object from a Python String Object */
JcpAPI_FUNC(jstring) JcpPyString_AsJString(JNIEnv *, PyObject *);

/* Function to return a Java ArrayList from a Python List object */
JcpAPI_FUNC(jobject) JcpPyList_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java Array from a Python Tuple object */
JcpAPI_FUNC(jobject) JcpPyTuple_AsJObject(JNIEnv *, PyObject *, jclass);

/* Function to return a Java Map from a Python Dict object */
JcpAPI_FUNC(jobject) JcpPyDict_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java Sql Date object from a Python Date object */
JcpAPI_FUNC(jobject) JcpPyDate_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java Sql Time object from a Python Time object */
JcpAPI_FUNC(jobject) JcpPyTime_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java Sql Timestamp object from a Python DateTime object
 */
JcpAPI_FUNC(jobject) JcpPyDateTime_AsJObject(JNIEnv *, PyObject *);

/* Function to check whether the Python object is Python Decimal object */
JcpAPI_FUNC(int) JcpPyDecimal_Check(PyObject *);

/* Function to return a Java BigDecimal/BigInteger object from a Python Decimal
 * object */
JcpAPI_FUNC(jobject) JcpPyDecimal_AsJObject(JNIEnv *, PyObject *, jclass);

/* Function to return a Java Generator Object from a Python Generator object */
JcpAPI_FUNC(jobject) JcpPyGenerator_AsJObject(JNIEnv *, PyObject *);

/* Function to return a Java PyObject from a Python object */
JcpAPI_FUNC(jobject) JcpPyObject_AsJPyObject(JNIEnv *, PyObject *);

#endif  // ifndef _Included_pyutils