/*
 * File:   glass_general.h
 * Author: martin
 *
 * Created on September 1, 2011, 3:20 PM
 */

#ifndef GLASS_GENERAL_H
#define	GLASS_GENERAL_H

#include <jni.h>

#ifdef	__cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <X11/Xlib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

#define JLONG_TO_PTR(value) ((void*)(intptr_t)(value))
#define PTR_TO_JLONG(value) ((jlong)(intptr_t)(value))

extern JNIEnv* mainEnv; // Use only with main loop thread!!!

#ifdef __cplusplus
}
#include <exception>

struct jni_exception: public std::exception {
    jni_exception(jthrowable _th): throwable(_th), message() {
            jmessage = (jstring)mainEnv->CallObjectMethod(throwable,
                    mainEnv->GetMethodID(mainEnv->FindClass("java/lang/Throwable"),
                    "getMessage", "()Ljava/lang/String;"));
            message = mainEnv->GetStringUTFChars(jmessage, NULL);
    }
    const char *what() const throw()
    {
        return message;
    }
    ~jni_exception() throw(){
        mainEnv->ReleaseStringUTFChars(jmessage, message);
    }
private:
    jthrowable throwable;
    const char *message;
    jstring jmessage;
};

#define CHECK_JNI_EXCEPTION(env) \
        if (env->ExceptionCheck()) {\
            return;\
        }

#define CHECK_JNI_EXCEPTION_RET(env, ret) \
        if (env->ExceptionCheck()) {\
                return ret;\
        }

#define JNI_EXCEPTION_TO_CPP(env) \
        if (env->ExceptionCheck()) {\
            throw jni_exception(env->ExceptionOccurred());\
        }

extern "C" {
#else
#define CHECK_JNI_EXCEPTION(env) \
        if ((*env)->ExceptionCheck(env)) {\
                return;\
        }

#define CHECK_JNI_EXCEPTION_RET(env, ret) \
        if ((*env)->ExceptionCheck(env)) {\
                return ret;\
        }
#endif

#define HANDLE_MEM_ALLOC_ERROR(env, nativePtr, message) \
        ((nativePtr == NULL) && glass_throw_oom(env, message))

    typedef struct {
        jobject runnable;
        int flag;
    } RunnableContext;

    extern char const * const GDK_WINDOW_DATA_CONTEXT;

    GdkCursor* get_native_cursor(int type);

    // JNI global references
    extern jclass string; // java.lang.String

    extern jclass byteBuffer; //java.nio.ByteBuffer
    extern jmethodID byteBufferArray; //java.nio.ByteBuffer#array()[B
    extern jmethodID byteBufferWrap; //java.nio.ByteBuffer#wrap([B)Ljava/nio/ByteBuffer;

    extern jmethodID jRunnableRun; // java.lang.Runnable#run ()V

    extern jclass arrayList; // java.util.ArrayList
    extern jmethodID arrayListInit; // java.util.ArrayList#<init> ()V
    extern jmethodID arrayListAdd; // java.util.ArrayList#add (Ljava/lang/Object;)Z
    extern jmethodID arrayListGetIdx; //java.util.ArryList#get (I)Ljava/lang/Object;

    extern jmethodID jPixelsAttachData; // com.sun.class.ui.Pixels#attachData (J)V
    extern jclass gtk_pixels;
    extern jmethodID jGtkPixelsInit; // com.sun.class.ui.gtk.GtkPixels#<init> (IILjava/nio/ByteBuffer;)V

    extern jmethodID jViewNotifyResize; // com.sun.glass.ui.View#notifyResize (II)V
    extern jmethodID jViewNotifyMouse; // com.sun.glass.ui.View#notifyMouse (IIIIIIIZZ)V
    extern jmethodID jViewNotifyRepaint; // com.sun.glass.ui.View#notifyRepaint (IIII)V
    extern jmethodID jViewNotifyKey; // com.sun.glass.ui.View#notifyKey (II[CI)V
    extern jmethodID jViewHandleViewEvent; //com.sun.glass.ui.View#handleViewEvent (I)V
    extern jmethodID jViewNotifyDragEnter; //com.sun.glass.ui.View#notifyDragEnter (IIIII)I
    extern jmethodID jViewNotifyDragOver; //com.sun.glass.ui.View#notifyDragOver (IIIII)I
    extern jmethodID jViewNotifyDragDrop; //com.sun.glass.ui.View#notifyDragDrop (IIIII)I
    extern jmethodID jViewNotifyDragLeave; //com.sun.glass.ui.View#notifyDragLeave ()V
    extern jmethodID jViewNotifyScroll; //com.sun.glass.ui.View#notifyScroll (IIIIDDIIIIIDD)V
    extern jmethodID jViewNotifyInputMethod; //com.sun.glass.ui.View#notifyInputMethod (Ljava/lang/String;[I[I[BIII)V
    extern jmethodID jViewNotifyInputMethodDraw; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodDraw (Ljava/lang/String;III)V
    extern jmethodID jViewNotifyInputMethodCaret; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodCaret (III)V
    extern jmethodID jViewNotifyMenu; //com.sun.glass.ui.View#notifyMenu (IIIIZ)V

    extern jmethodID jWindowNotifyResize; // com.sun.glass.ui.Window#notifyResize (III)V
    extern jmethodID jWindowNotifyMove; // com.sun.glass.ui.Window#notifyMove (II)V
    extern jmethodID jWindowNotifyDestroy; // com.sun.glass.ui.Window#notifyDestroy ()V
    extern jmethodID jWindowNotifyClose; // com.sun.glass.ui.Window#notifyClose ()V
    extern jmethodID jWindowNotifyFocus; // com.sun.glass.ui.Window#notifyFocus (I)V
    extern jmethodID jWindowNotifyFocusUngrab; // com.sun.glass.ui.Window#notifyFocusUngrab ()V
    extern jmethodID jWindowNotifyMoveToAnotherScreen; // com.sun.glass.ui.Window#notifyMoveToAnotherScreen (JJ)V
    extern jmethodID jWindowNotifyDelegatePtr; //com.sun.glass.ui.Window#notifyDelegatePtr (J)V
    extern jmethodID jWindowIsEnabled; // com.sun.glass.ui.Window#isEnabled ()Z
    extern jfieldID jWindowPtr; // com.sun.glass.ui.Window#ptr
    extern jfieldID jCursorPtr; // com.sun.glass.ui.Cursor#ptr

    extern jmethodID jGtkWindowNotifyStateChanged; // com.sun.glass.ui.GtkWindow#notifyStateChanged (I)V

    extern jmethodID jSizeInit; // com.sun.class.ui.Size#<init> ()V

    extern jmethodID jMapGet; // java.util.Map#get(Ljava/lang/Object;)Ljava/lang/Object;
    extern jmethodID jMapKeySet; // java.util.Map#keySet()Ljava/util/Set;
    extern jmethodID jMapContainsKey; // java.util.Map#containsKey(Ljava/lang/Object;)Z

    extern jclass jHashSet; // java.util.HashSet
    extern jmethodID jHashSetInit; // java.util.HashSet#<init> ()V

    extern jmethodID jSetAdd; //java.util.Set#add (Ljava/lang/Object;)Z
    extern jmethodID jSetSize; //java.util.Set#size ()I
    extern jmethodID jSetToArray; //java.util.Set#toArray ([Ljava/lang/Object;)[Ljava/lang/Object;

    extern jmethodID jIterableIterator; // java.lang.Iterable#iterator()Ljava/util/Iterator;
    extern jmethodID jIteratorHasNext; // java.util.Iterator#hasNext()Z;
    extern jmethodID jIteratorNext; // java.util.Iterator#next()Ljava/lang/Object;

    extern jclass jScreenClass; //com.sun.glass.ui.Screen
    extern jmethodID jgetScreenForLocation; //com.sun.glass.ui.Screen#getScreenForLocation(JJ)Lcom.sun.glass.ui.Screen;
    extern jmethodID jgetNativeScreen; //com.sun.glass.ui.Screen#getNativeScreen()J

    extern jclass jApplication; //com.sun.glass.ui.gtk.Application
    extern jfieldID jDisplay; //com.sun.glass.ui.gtk.Application#display
    extern jfieldID jScreen; //com.sun.glass.ui.gtk.Application#screen
    extern jfieldID jVisualID; //com.sun.glass.ui.gtk.Application#visualID

#ifdef VERBOSE
#define LOG0(msg) {printf(msg);fflush(stdout);}
#define LOG1(msg, param) {printf(msg, param);fflush(stdout);}
#define LOG2(msg, param1, param2) {printf(msg, param1, param2);fflush(stdout);}
#define LOG3(msg, param1, param2, param3) {printf(msg, param1, param2, param3);fflush(stdout);}
#define LOG4(msg, param1, param2, param3, param4) {printf(msg, param1, param2, param3, param4);fflush(stdout);}
#define LOG5(msg, param1, param2, param3, param4, param5) {printf(msg, param1, param2, param3, param4, param5);fflush(stdout);}
#define LOG_STRING_ARRAY(env, array) dump_jstring_array(env, array);
#define LOG_EXCEPTION(env) log_exception(env);
#else
#define LOG0(msg)
#define LOG1(msg, param)
#define LOG2(msg, param1, param2)
#define LOG3(msg, param1, param2, param3)
#define LOG4(msg, param1, param2, param3, param4)
#define LOG5(msg, param1, param2, param3, param4, param5)
#define LOG_STRING_ARRAY(env, array)
#define LOG_EXCEPTION(env)
#endif

#define ERROR0(msg) {fprintf(stderr, msg);fflush(stderr);}
#define ERROR1(msg, param) {fprintf(stderr, msg, param);fflush(stderr);}
#define ERROR2(msg, param1, param2) {fprintf(stderr, msg, param1, param2);fflush(stderr);}
#define ERROR3(msg, param1, param2, param3) {fprintf(stderr, msg, param1, param2, param3);fflush(stderr);}
#define ERROR4(msg, param1, param2, param3, param4) {fprintf(stderr, msg, param1, param2, param3, param4);fflush(stderr);}

    void glass_throw_exception(JNIEnv * env,
            const char * exceptionClass,
            const char * exceptionMessage);
    int glass_throw_oom(JNIEnv * env, const char * exceptionMessage);
    void dump_jstring_array(JNIEnv*, jobjectArray);

    guint8* convert_BGRA_to_RGBA(const int* pixels, int stride, int height);

    void log_exception(JNIEnv *env);

#ifdef	__cplusplus
}
#endif

#endif	/* GLASS_GENERAL_H */

