/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */

#include "stdlib.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#ifdef USE_XSHM
# include <sys/shm.h> 
# include <X11/extensions/XShm.h>

extern int xshm_pending;
extern int xshm_completion;
#endif

#include <jni.h>

#define jlong_to_ptr(value) ((void*)((long)value))
#define ptr_to_jlong(value) (long)(value)

#define USE_GLASS_CHECK
#ifndef USE_GLASS_CHECK
        #define GLASS_CHECK_EXCEPTION(ENV)
        #define GLASS_CLEAR_EXCEPTION(ENV)
#else
        #define GLASS_CHECK_EXCEPTION(ENV) \
            if ((*ENV)->ExceptionCheck(ENV) == JNI_TRUE) {                                 \
                fprintf(stderr, "Glass detected outstanding Java exception at %s:%s:%d\n", \
                        __FUNCTION__, __FILE__, __LINE__);                                 \
                (*ENV)->ExceptionDescribe(ENV);                                            \
                (*ENV)->ExceptionClear(ENV);                                               \
            };
        #define GLASS_CLEAR_EXCEPTION(ENV) (*ENV)->ExceptionClear(ENV); 
#endif

#define MAT_X11_DEBUG 0
#define DEBUG_PRINTF(...) if (MAT_X11_DEBUG) { printf(__VA_ARGS__); } 

typedef struct _GlassView {
    jobject jview; //todo - not really used ?
} GlassView;

typedef struct _GlassWindow {
    JNIEnv *env;        
    Window window;          // X11 window
    jobject jwindow;        // Window obj
    jobject jview;          // View obj associated with this window
    GlassView *view;
    unsigned int button;    // the "current" button pressed if any
    int destroyed;          // close/destroy has be called.
    int x,y,width,height;
    int configureCount;
    jboolean visible;       // have we called set visible on this ?
    XImage *image;        // Pointer to XImage structure
#ifdef USE_XSHM
    int shWidth, shHeight;
    XShmSegmentInfo shminfo;
#endif
} GlassWindow;

extern JavaVM *jVM;

extern Display *mat_x11Display;     // default connection to the display

void matOpenDisplay();              // open the display connection
Window matCreateWindow();           // create a non-visible 1x1 window
void matSetBounds(Window window,    // setBounds ie move/resize a window
       int x, int y,
       jboolean xset, jboolean yset,
       int width, int height,
       int cw, int ch);
void matTranslateScreenCoords(Window window, 
        int relX, int relY, 
        int *screenX, int *screenY);
void matSetVisible(Window window, int visible); // map/unmap a window
void matSetTitle(Window window, const char * title); // window title
void matCloseDisplay(jboolean isEventThread); // close the display connection
void matCloseWindow(Window);    // close the display connection
void matLock();                 // thread lock the display
void matUnlock();               // thread lock the display
void mat_runOnEventThread(JNIEnv *env, int waitType, jobject jRunnable);
#define PostEventInvokeAndWait 1
#define PostEventInvokeLater 2
#define PostEventCloseRunLoop 9
Screen* matScreenDefault();     // default Screen of display

extern Atom mat_wmDeleteMessage;

// get the GlassWindow that matches a given X Window for event handling
GlassWindow* mat_matchXWindow(Window xwindow);

// Window 
void mat_FocusWindow(XEvent *evt,jboolean activate);
void mat_CloseWindow(XEvent* evt);
void mat_DestroyWindow(XEvent *evt);
void mat_ResizeWindow(GlassWindow *gw, int width, int height);
void mat_MoveWindow(GlassWindow *gw,
    int relX, int relY,
    int screenX, int screenY);

// View 
void mat_KeyEvent(XEvent *evt, jboolean pressed);
void mat_MouseEnterEvent(XEvent *evt, jboolean enter);
void mat_MouseButtonEvent(XEvent *evt, jboolean pressed);
void mat_MouseMotionEvent(XEvent *evt);
void mat_ViewEvent(XEvent*evt, jint type);
void mat_ViewRepaint(XEvent *event);

// Screen
void mat_SetJavaScreen(Screen *screen, JNIEnv *env, jobject jscreen);

// Key
jint mat_GetJavaKeyCode(KeySym keysym);

// Misc utils - hidden in GlassApplication
extern char * mat_RuntimeException;
void mat_JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg);

// JNI handles
extern jmethodID mat_jRunnableRun;

extern jclass mat_jIntegerClass;
extern jmethodID mat_jVectorAddElement;

extern jclass mat_jScreenClass;

extern jmethodID mat_jHandleViewEvent;
extern jmethodID mat_jViewNotifyMove;
extern jmethodID mat_jViewNotifyResize;
extern jmethodID mat_jViewNotifyRepaint;
extern jmethodID mat_jViewNotifyKey;
extern jmethodID mat_jViewNotifyMouse;

extern jmethodID mat_jMapGetMethod;
extern jmethodID mat_jBooleanValueMethod;
extern jmethodID mat_jListAddElement;

extern jfieldID mat_jViewPtr;

extern jfieldID mat_jWindowX;
extern jfieldID mat_jWindowY;
extern jfieldID mat_jWindowWidth;
extern jfieldID mat_jWindowHeight;
extern jfieldID mat_jWindowView;

extern jmethodID mat_jWindowNotifyClose;
extern jmethodID mat_jWindowNotifyDestroy;
extern jmethodID mat_jWindowNotifyFocus;
extern jmethodID mat_jWindowNotifyFocusDisabled;
extern jmethodID mat_jWindowNotifyMove;
extern jmethodID mat_jWindowNotifyMoveToAnotherScreen;
extern jmethodID mat_jWindowNotifyResize;

extern jclass mat_jPixelsClass;

extern jfieldID mat_jPixelsWidth;
extern jfieldID mat_jPixelsHeight;
extern jfieldID mat_jPixelsBytes;
extern jfieldID mat_jPixelsInts;

extern jmethodID mat_jPixelsAttachData;

extern jclass mat_jCursorClass;

