/* -*- Mode: C; c-file-style: "gnu" -*-
   common.c -- common methods for the awt cruft.
   Created: Chris Toshok <toshok@hungry.com>, 9-Mar-1998.
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "config.h"

#include "arch.h"
#include "jniint.h"
#include "ClazzFile.h"
#include <Xm/Xm.h>
#include <assert.h>
#include <stdlib.h>

#include "common.h"

#ifdef FOR_MOZILLA
#include "prmem.h"
#endif

XtAppContext _awt_appContext;
Widget _awt_toplevelWidget;
Display *_awt_Display;
Screen *_awt_Screen;

jboolean awt_initialized = JNI_FALSE;

void
initialize_awt_with_stuff(Widget toplevel_widget,
                          XtAppContext app_context)
{
  if (!awt_initialized)
    {
      awt_initialized = JNI_TRUE;

      LOCK_AWT();

      _awt_appContext = app_context;
      _awt_toplevelWidget = toplevel_widget;

      _awt_Display = XtDisplay(_awt_toplevelWidget);
      _awt_Screen = XtScreen(_awt_toplevelWidget);

      UNLOCK_AWT();
    }
}

void
initialize_awt(void)
{
  int fake_argc = 1;
  char **fake_argv;

  fake_argv = malloc(sizeof(char*) * 1);
  fake_argv[0] = "japhar";

  PR_LOG(awtLm, PR_LOG_DEBUG, ("sun.awt.motif/initialize_awt() start\n"));

  if (!awt_initialized)
    {
      LOCK_AWT();
      awt_initialized = JNI_TRUE;

      _awt_toplevelWidget  = XtOpenApplication(&_awt_appContext,
                                               "AWTapp",
                                               NULL, 0, &fake_argc,
                                               fake_argv,
                                               NULL,
                                               applicationShellWidgetClass,
                                               NULL, 0);

      _awt_Display = XtDisplay(_awt_toplevelWidget);
      _awt_Screen = XtScreen(_awt_toplevelWidget);

      UNLOCK_AWT();
    }
}

Widget
get_component_widget(JNIEnv *env,
                     jobject component_peer)
{
  return (Widget)HVM_ObjectGetNativeState(component_peer);
}

void
set_component_widget(JNIEnv *env,
                     jobject component_peer,
                     Widget widget)
{
  HVM_ObjectSetNativeState(component_peer, widget);
}

GraphicsData*
get_graphics_data(JNIEnv *env,
                  jobject graphics)
{
  return (GraphicsData*)HVM_ObjectGetNativeState(graphics);
}

void
set_graphics_data(JNIEnv *env,
                  jobject graphics,
                  GraphicsData* data)
{
  HVM_ObjectSetNativeState(graphics, data);
}

Widget
get_menubar_widget(JNIEnv *env,
                   jobject menubar_peer)
{
  return (Widget)HVM_ObjectGetNativeState(menubar_peer);
}

void
set_menubar_widget(JNIEnv *env,
                   jobject menubar_peer,
                   Widget widget)
{
  HVM_ObjectSetNativeState(menubar_peer, widget);
}

Widget
get_menuitem_widget(JNIEnv *env,
                    jobject menuitem_peer)
{
  return (Widget)HVM_ObjectGetNativeState(menuitem_peer);
}

void
set_menuitem_widget(JNIEnv *env,
                    jobject menuitem_peer,
                    Widget widget)
{
  HVM_ObjectSetNativeState(menuitem_peer, widget);
}

char *
get_menuitem_label(JNIEnv *env,
                   jobject menuitem_peer)
{
  jclass peer_class, menuitem_class;
  jfieldID target_field;
  jobject target;
  jfieldID label_field;
  jstring label;
  const jbyte *label_bytes;
  char *ret_val;

  peer_class = (*env)->FindClass(env, "sun/awt/motif/MMenuItemPeer");
  menuitem_class = (*env)->FindClass(env, "java/awt/MenuItem");

  target_field = (*env)->GetFieldID(env, peer_class, "target", "Ljava/awt/MenuItem;");
  label_field = (*env)->GetFieldID(env, menuitem_class, "label", "Ljava/lang/String;");

  target = (*env)->GetObjectField(env, menuitem_peer, target_field);
  label = (*env)->GetObjectField(env, target, label_field);

  label_bytes = (*env)->GetStringUTFChars(env, label, NULL);

  ret_val = strdup((const char*)label_bytes);

  (*env)->ReleaseStringUTFChars(env, label, label_bytes);

  return ret_val;
}

char *
get_checkbox_label(JNIEnv *env,
                   jobject checkbox_peer)
{
  jclass peer_class, checkbox_class;
  jfieldID target_field;
  jobject target;
  jfieldID label_field;
  jstring label;
  const jbyte *label_bytes;
  char *ret_val;

  peer_class = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  checkbox_class = (*env)->FindClass(env, "java/awt/Checkbox");

  target_field = (*env)->GetFieldID(env, peer_class, "target", "Ljava/awt/Component;");
  label_field = (*env)->GetFieldID(env, checkbox_class, "label", "Ljava/lang/String;");

  target = (*env)->GetObjectField(env, checkbox_peer, target_field);
  label = (*env)->GetObjectField(env, target, label_field);

  label_bytes = (*env)->GetStringUTFChars(env, label, NULL);

  ret_val = strdup((const char*)label_bytes);

  (*env)->ReleaseStringUTFChars(env, label, label_bytes);

  return ret_val;
}


static void
send_mouse_event(JNIEnv *env,
                 Widget w,
                 XEvent *event,
                 jobject peer,
                 jint id)
{
  jclass mouseEventClass = (*env)->FindClass(env, "java/awt/event/MouseEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls,
                                             "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, mouseEventClass,
                             "<init>", "(Ljava/awt/Component;IJIIIIZ)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, mouseEventClass,
                                ctor,
                                /* 1 */ target,
                                /* 2 */ id, /* event_id */
                                /* 3 */ (jlong)event->xmotion.time, /* when */
                                /* 4 */ 0, /* XXX ??? */
                                /* 5 */ 0, /* XXX modifiers */
                                /* 6 */ event->xmotion.x,
                                /* 7 */ event->xmotion.y,
                                /* 8 */ JNI_FALSE /* popupTrigger */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
                                        "postEvent",
                                        "(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_button_press_event(JNIEnv *env,
                        Widget w,
                        XEvent *event,
                        jobject peer)
{
  send_mouse_event(env, w, event, peer, 501);
}

static void
send_button_release_event(JNIEnv *env,
                          Widget w,
                          XEvent *event,
                          jobject peer)
{
  send_mouse_event(env, w, event, peer, 502);
}

static void
send_mouse_dragged_event(JNIEnv *env,
                         Widget w,
                         XEvent *event,
                         jobject peer)
{
  send_mouse_event(env, w, event, peer, 506);
}

static void
send_mouse_moved_event(JNIEnv *env,
                       Widget w,
                       XEvent *event,
                       jobject peer)
{
  send_mouse_event(env, w, event, peer, 503);
}

static void
send_key_event(JNIEnv *env,
               Widget w,
               XEvent *event,
               jobject peer,
               jint id)
{
  jclass keyEventClass = (*env)->FindClass(env, "java/awt/event/KeyEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls,
                                             "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jmethodID postEvent;
  KeySym keysym;

  keysym = XLookupKeysym((XKeyEvent*)event, 0);

  printf ("sending keysym %ld\n", (long)keysym);

  ctor = (*env)->GetMethodID(env, keyEventClass,
                             "<init>", "(Ljava/awt/Component;IJII)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, keyEventClass,
                                ctor,
                                /* 1 */ target,
                                /* 2 */ id, /* event_id */
                                /* 3 */ (jlong)event->xkey.time, /* when */
                                /* 5 */ 0, /* XXX modifiers */
                                /* 6 */ keysym);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
                                        "postEvent",
                                        "(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_keypress_event(JNIEnv *env,
                    Widget w,
                    XEvent *event,
                    jobject peer)
{
  send_key_event(env, w, event, peer, 401);
}

static void
send_keyrelease_event(JNIEnv *env,
                      Widget w,
                      XEvent *event,
                      jobject peer)
{
  send_key_event(env, w, event, peer, 402);
}

static void
send_component_event(JNIEnv *env,
                     jobject peer,
                     int id)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jclass componentEventClass = (*env)->FindClass(env, "java/awt/event/ComponentEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, componentEventClass,
                             "<init>", "(Ljava/awt/Component;I)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, componentEventClass,
                                ctor,
                                /* 1 */ target,
                                /* 2 */ id /* event_id */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
                                        "postEvent",
                                        "(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_component_resized_event(JNIEnv *env,
                             Widget w,
                             XEvent *event,
                             jobject peer)
{
  send_component_event(env, peer, 101);
}

static void
send_component_moved_event(JNIEnv *env,
                           Widget w,
                           XEvent *event,
                           jobject peer)
{
  send_component_event(env, peer, 100);
}

static void
send_focus_event(JNIEnv *env,
                 jobject peer,
                 int id)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jclass focusEventClass = (*env)->FindClass(env, "java/awt/event/FocusEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, focusEventClass,
                             "<init>", "(Ljava/awt/Component;I)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, focusEventClass,
                                ctor,
                                /* 1 */ target,
                                /* 2 */ id /* event_id */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
                                        "postEvent",
                                        "(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_focus_in_event(JNIEnv *env,
                    Widget w,
                    XEvent *event,
                    jobject peer)
{
  send_focus_event(env, peer, 1004);
}

static void
send_focus_out_event(JNIEnv *env,
                     Widget w,
                     XEvent *event,
                     jobject peer)
{
  send_focus_event(env, peer, 1005);
}

/* Not static, as it is used from component.c */
/* static */ void
send_paint_event(JNIEnv *env,
                 Widget w,
                 XEvent *event,
                 jobject peer)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;

  jclass rect_cls;
  jmethodID rect_ctor;
  jobject rect;

  jobject event_obj;
  jmethodID ctor;
  jint id = 801; /* XXX we should get it from the static final field. */
  jclass paintEventClass = (*env)->FindClass(env, "java/awt/event/PaintEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  rect_cls = (*env)->FindClass(env, "java/awt/Rectangle");
  rect_ctor = (*env)->GetMethodID(env, rect_cls, "<init>", "(IIII)V");
  rect = (*env)->NewObject(env, rect_cls, rect_ctor,
                           event->xexpose.x, event->xexpose.y,
                           event->xexpose.width, event->xexpose.height);

  ctor = (*env)->GetMethodID(env, paintEventClass, "<init>",
                             "(Ljava/awt/Component;ILjava/awt/Rectangle;)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, paintEventClass,
                                ctor,
                                /* 1 */ target,
                                /* 2 */ id, /* event_id */
                                /* 3 */ rect);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
                                        "postEvent",
                                        "(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

#ifdef FOR_MOZILLA
typedef struct JapharEventType {
  PLEvent e;
  /* japhar specific stuff */
  Widget w;
  XEvent *xevent;
  jobject peer;
} JapharEventType;

static void
japhar_event_handler(JapharEventType *event)
{
  JNIEnv *env = HVM_ThreadGetEnv();
  jobject peer = event->peer;
  XEvent *xevent = event->xevent;
  Widget w = event->w;

  printf ("in japhar_event_handler\n");

  switch (xevent->type)
    {
    case ButtonPress:
      send_button_press_event(env, w, xevent, peer);
      break;
    case ButtonRelease:
      send_button_release_event(env, w, xevent, peer);
      break;
    case MotionNotify:
      if (xevent->xmotion.state
          & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))
        send_mouse_dragged_event(env, w, xevent, peer);
      else
        send_mouse_moved_event(env, w, xevent, peer);
      break;
    case FocusIn:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("FocusIn\n"));
      break;
    case FocusOut:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("FocusOut\n"));
      break;
    case EnterNotify:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("EnterNotify\n"));
      break;
    case LeaveNotify:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("LeaveNotify\n"));
      break;
    case Expose:
      send_paint_event(env, w, xevent, peer);
      break;
    }
}
static void
japhar_event_destructor(JapharEventType *event)
{
  free(event->xevent);
  free(event);
}

static void
component_event_handler(Widget w,
                        XtPointer closure,
                        XEvent *xevent,
                        Boolean *cont)
{
  XEvent *event_copy = (XEvent*)malloc(sizeof(XEvent));
  JapharEventType *event;
  jobject peer = (jobject)closure;

  printf ("in component_event_handler\n");

  memcpy(event_copy, xevent, sizeof(XEvent));

  PL_ENTER_EVENT_QUEUE_MONITOR(_awt_eventQueue);

  event = PR_NEW(JapharEventType);
  if (event == NULL) goto done;

  PL_InitEvent((PLEvent*)event, NULL,
               (PLHandleEventProc)japhar_event_handler,
               (PLDestroyEventProc)japhar_event_destructor);

  event->xevent = event_copy;
  event->peer = peer;
  event->w = w;

  PL_PostEvent(_awt_eventQueue, &event->e);

 done:
  PL_EXIT_EVENT_QUEUE_MONITOR(_awt_eventQueue);

  *cont = False;
}
#else
static void
component_event_handler(Widget w,
                        XtPointer closure,
                        XEvent *event,
                        Boolean *cont)
{
  JNIEnv *env = HVM_ThreadGetEnv()->thread_env;
  jobject peer = (jobject)closure;

  switch (event->type)
    {
    case ButtonPress:
      send_button_press_event(env, w, event, peer);
#if 0
      *cont = False;
#endif
      break;
    case ButtonRelease:
      send_button_release_event(env, w, event, peer);
#if 0
      *cont = False;
#endif
      break;
    case MotionNotify:
      if (event->xmotion.state
          & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))
        send_mouse_dragged_event(env, w, event, peer);
      else
        send_mouse_moved_event(env, w, event, peer);
#if 0
      *cont = False;
#endif
      break;
    case ConfigureNotify:
      {
        printf ("sending component moved event\n");
        send_component_moved_event(env, w, event, peer);
        printf ("sending component resized event\n");
        send_component_resized_event(env, w, event, peer);
        break;
      }
    case FocusIn:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("FocusIn\n"));
      send_focus_in_event(env, w, event, peer);
      break;
    case FocusOut:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("FocusOut\n"));
      send_focus_out_event(env, w, event, peer);
      break;
    case EnterNotify:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("EnterNotify\n"));
      break;
    case LeaveNotify:
      PR_LOG (awtLm, PR_LOG_DEBUG, ("LeaveNotify\n"));
      break;
    case Expose:
    case GraphicsExpose:
      printf ("expose event\n");
      send_paint_event(env, w, event, peer);
#if 0
      *cont = False;
#endif
      break;
    case KeyPress:
      send_keypress_event(env, w, event, peer);
      break;
    case KeyRelease:
      send_keyrelease_event(env, w, event, peer);
      break;
    }
}
#endif

void
add_component_event_handler(Widget w, jobject peer)
{
  XtInsertEventHandler(w,
                       StructureNotifyMask                |
                       ButtonPressMask                    |
                       ButtonReleaseMask                  |
                       ButtonMotionMask                   |
                       PointerMotionMask                  |
                       KeyPressMask                       |
                       KeyReleaseMask                     |
                       FocusChangeMask                    |
                       EnterWindowMask                    |
                       LeaveWindowMask,
                       False, component_event_handler, peer, 0);
}

void
add_canvas_exposure_handler(Widget w, jobject peer)
{
  XtInsertEventHandler(w,
                       ExposureMask,
                       False, component_event_handler, peer, 0);
}

void
inherit_background_from(Widget parent,
                        Widget child)
{
  Pixel bg_color;

  XtVaGetValues(parent,
                XmNbackground, &bg_color,
                NULL);

  XmChangeColor(child,
                bg_color);
}

Pixel
jcolor_to_pixel(JNIEnv *env, jobject color)
{
  jclass color_cls = (*env)->GetObjectClass(env, color);
  jmethodID getRed = (*env)->GetMethodID(env, color_cls, "getRed", "()I");
  jmethodID getGreen = (*env)->GetMethodID(env, color_cls, "getGreen", "()I");
  jmethodID getBlue = (*env)->GetMethodID(env, color_cls, "getBlue", "()I");
  jint red, green, blue;
  XColor col;

  red = (*env)->CallIntMethod(env, color, getRed);
  green = (*env)->CallIntMethod(env, color, getGreen);
  blue = (*env)->CallIntMethod(env, color, getBlue);

  col.red = (red << 8) | red;
  col.green = (green << 8) | green;
  col.blue = (blue << 8) | blue;
  col.flags = DoRed | DoGreen | DoBlue;

  LOCK_AWT();
  XAllocColor(_awt_Display, DefaultColormapOfScreen(_awt_Screen),
              &col);
  UNLOCK_AWT();

  return col.pixel;
}

XFontStruct *
jfont_to_xfontstruct(JNIEnv *env, jobject font)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jclass font_cls = (*env)->GetObjectClass(env, font);
  jfieldID peer_field;
  jobject peer;
  jfieldID size_field;
  jint size;
  jclass mfontpeer_cls = (*env)->FindClass(env, "sun/awt/motif/MFontPeer");
  jfieldID xfsname_field = (*env)->GetFieldID(env, mfontpeer_cls, "xfsname",
                                              "Ljava/lang/String;");
  jstring xfsname;
  char *xfsname_str;
  char *buf;
  XFontStruct *fontstruct;

  peer_field = (*env)->GetFieldID(env, font_cls,
                                  "peer", "Ljava/awt/peer/FontPeer;");
  peer = (*env)->GetObjectField(env, font, peer_field);
  size_field = (*env)->GetFieldID(env, font_cls,
                                  "size", "I");
  size = (*env)->GetIntField(env, font, size_field);

  xfsname = (*env)->GetObjectField(env, peer, xfsname_field);

  if (NULL != xfsname)
    xfsname_str = HVM_StringToCString(henv, xfsname);
  else
    { /* XXX What should we really throw? */
      HVM_ExceptionThrow(henv, "java/lang/NullPointerException",
              "MFontPeer.xfsname is null!  Is font.properties installed?");
      return NULL;
    }

  buf = (char*)malloc(strlen(xfsname_str) + 5);
  sprintf(buf, xfsname_str, size * 10);
  free(xfsname_str);

  LOCK_AWT();
  printf ("loading font %s\n", buf);
  fontstruct = XLoadQueryFont(_awt_Display, buf);
  UNLOCK_AWT();

  return fontstruct;
}
#if UNUSED
static XIM
get_xim(JNIEnv *env, jobject im)
{
}

static void
set_xim(JNIEnv *env,
        jobject im,
        XIM xim)
{
}
#endif
